API Endpoints
Reference documentation for the Distill REST API.
Coming Soon — API Access Not Yet Public
The Distill API is currently for internal use during early access and is not yet publicly available for third-party integrations. The endpoints documented here reflect the current internal implementation. Public API access with stable keys and versioning will be announced at a later date.
The Distill API lets you integrate newsletter processing and sending into your own workflows. All API requests are made over HTTPS to your app's base URL. The API follows REST conventions and returns JSON responses.
The Distill API is subject to change during early access. Endpoints and response formats may be updated without notice. Pin your integration to a specific behavior and test after updates.
Authentication
All API requests require a Bearer token obtained from Supabase authentication. To get a token:
- Call the Supabase auth endpoint with your credentials to receive a session.
- Extract the
access_tokenfrom the session response. - Include it in the
Authorizationheader of every API request.
Include the token in subsequent requests:
Tokens expire after 1 hour by default. Refresh them using the Supabase auth refresh endpoint.
Endpoints
Process Link
Scrapes a URL using Firecrawl, generates an AI summary using Gemini, and adds the processed link to a newsletter issue.
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
url | string | Yes | The URL to scrape and summarize |
issue_id | string (UUID) | Yes | The issue to add the processed link to |
Example request:
Example response:
Processing is synchronous — the response is returned after scraping and summarization complete. For pages that load slowly, allow up to 30 seconds for a response.
Process Links (Bulk)
Processes multiple URLs in a single request. Each URL is scraped and summarized independently. Partial success is supported — if some URLs fail, the rest are still processed and returned.
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
urls | string[] | Yes | Array of URLs to process (maximum 20 per request) |
issue_id | string (UUID) | Yes | The issue to add all processed links to |
Example request:
Example response:
If any URLs fail to process, they appear in the errors array with the URL and a reason. Successfully processed links appear in links regardless of any failures.
Categories
Returns all categories defined for a workspace, ordered by position.
Query parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
workspace_id | string (UUID) | Yes | The workspace to retrieve categories for |
Example request:
Example response:
Send Newsletter
Queues a newsletter issue for delivery to all subscribers in the specified list. Returns 202 Accepted immediately — sending happens asynchronously in the background.
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
issue_id | string (UUID) | Yes | The issue to send |
list_id | string (UUID) | Yes | The subscriber list to send to |
Example request:
Example response (202 Accepted):
enqueued_count is the number of subscribers who will receive the email. suppressed_count is the number of subscribers skipped because they have previously unsubscribed, hard-bounced, or filed a complaint.
An issue can only be sent once. Attempting to send an already-sent issue returns a 409 Conflict error.
Generate Preview
Renders the newsletter issue as HTML and returns the rendered preview. Use this to verify your content renders correctly before sending.
Request body:
Example request:
Example response:
The returned HTML includes all inline styles for email client compatibility.
Rate Limits
All API endpoints are rate-limited per authenticated user. Rate limits vary by plan tier — refer to your plan's limits at distill.ink/pricing. If you exceed the rate limit, the API returns 429 Too Many Requests with a Retry-After header indicating when you can retry.
Error Handling
All errors follow a consistent response format:
| Status Code | Meaning |
|---|---|
400 | Bad request — malformed JSON, missing required fields, or invalid field values |
401 | Unauthorized — missing or invalid Bearer token |
403 | Forbidden — authenticated but not authorized to access the requested resource |
409 | Conflict — resource state prevents the operation (e.g., issue already sent) |
429 | Too Many Requests — rate limit exceeded; check the Retry-After header |
500 | Internal Server Error — unexpected server-side error; contact support if persistent |
For 500 errors that persist, contact support at support@distill.ink with the request ID from the response headers (if present).