API Reference
The Purl REST API lets you save, read, update, and delete links programmatically. All endpoints are authenticated with an API key and return JSON.
Base URL: https://purl.nublson.com/api/v1
Using an AI client? Connect over MCP instead.
Authentication
All API requests require a Bearer token in the Authorization header. Generate an API key in your Purl settings under Settings → API Keys.
Authorization: Bearer purl_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxAPI keys are scoped to your account. Keep them secret — do not expose them in client-side code or public repositories.
All /api/v1 endpoints include Access-Control-Allow-Origin: * headers, so they can be called directly from browser environments.
Rate Limiting
The API is rate-limited per account. When exceeded, requests return 429 Too Many Requests. Respect the Retry-After response header.
Endpoints
/linksReturns a paginated list of your saved links, ordered by most recently saved first.
Query parameters
| Parameter | Type | Description |
|---|---|---|
| limit | number | Number of results to return. Default 50, max 100. |
| cursor | string | Pagination cursor from the previous response's nextCursor field. |
| contentType | WEB | YOUTUBE | PDF | AUDIO | Filter results by content type. |
Example request
curl https://purl.nublson.com/api/v1/links?limit=10 \
-H "Authorization: Bearer purl_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"Response
{
"data": [
{
"id": "clxyz123",
"url": "https://example.com/article",
"title": "An interesting article",
"description": "A short description of the article.",
"favicon": "https://example.com/favicon.ico",
"thumbnail": null,
"domain": "example.com",
"contentType": "WEB",
"ingestStatus": "COMPLETED",
"createdAt": "2025-01-15T10:30:00.000Z"
}
],
"nextCursor": "clxyz122"
}Pass nextCursor as the cursor query parameter to fetch the next page. When nextCursor is null you have reached the last page.
/linksSaves a URL to your library. Purl automatically detects the content type and queues the link for AI processing if your plan includes it.
Body
| Parameter | Type | Description |
|---|---|---|
| url* | string | The URL to save. Must be a valid http or https URL. |
Example request
curl -X POST https://purl.nublson.com/api/v1/links \
-H "Authorization: Bearer purl_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com/article"}'Response — 201 Created
{
"id": "clxyz123",
"url": "https://example.com/article",
"title": "An interesting article",
"description": null,
"favicon": "https://example.com/favicon.ico",
"thumbnail": null,
"domain": "example.com",
"contentType": "WEB",
"ingestStatus": "PENDING",
"createdAt": "2025-06-05T12:00:00.000Z"
}/links/:idReturns a single link by its ID.
curl https://purl.nublson.com/api/v1/links/clxyz123 \
-H "Authorization: Bearer purl_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"/links/:idUpdates a link's URL, title, or description. At least one field is required.
Body (at least one required)
| Parameter | Type | Description |
|---|---|---|
| url | string | New URL for the link. |
| title | string | New display title. |
| description | string | null | New description. Pass null to clear it. |
curl -X PATCH https://purl.nublson.com/api/v1/links/clxyz123 \
-H "Authorization: Bearer purl_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"title": "Updated title"}'/links/:idPermanently deletes a link and its associated content. Returns 204 No Content on success.
curl -X DELETE https://purl.nublson.com/api/v1/links/clxyz123 \
-H "Authorization: Bearer purl_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"API Keys
/keysReturns all API keys for your account. Key values are never returned after creation — only the prefix and metadata.
curl https://purl.nublson.com/api/v1/keys \
-H "Authorization: Bearer purl_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"[
{
"id": "key_abc123",
"name": "My App",
"start": "purl_xxxx",
"createdAt": "2025-06-01T09:00:00.000Z"
}
]/keysCreates a new API key. The full key value is returned only once in the response — store it immediately.
| Parameter | Type | Description |
|---|---|---|
| name | string | A label for this key (e.g. "My App"). Defaults to "API Key". |
curl -X POST https://purl.nublson.com/api/v1/keys \
-H "Authorization: Bearer purl_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"name": "My App"}'Response — 201 Created
{
"id": "key_abc123",
"name": "My App",
"key": "purl_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"start": "purl_xxxx",
"createdAt": "2025-06-05T12:00:00.000Z"
}The full key value is only returned once — store it immediately. It cannot be retrieved again.
/keys/:idRevokes an API key. Returns 204 No Content on success. This action is irreversible.
curl -X DELETE https://purl.nublson.com/api/v1/keys/key_abc123 \
-H "Authorization: Bearer purl_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"Error Codes
All errors return JSON with an error field. Some errors include an additional code field.
| Parameter | Type | Description |
|---|---|---|
| 400 | Bad Request | Missing or invalid request body / parameters. |
| 401 | Unauthorized | Missing, invalid, or expired API key. |
| 402 | Payment Required | Plan limit reached. Check code: LIMIT_REACHED and feature fields. |
| 404 | Not Found | The requested resource does not exist or belongs to another account. |
| 429 | Too Many Requests | Rate limit exceeded. Retry after the Retry-After header value. |
| 500 | Internal Server Error | Unexpected server error. Contact support if it persists. |
{
"error": "Plan limit reached",
"code": "LIMIT_REACHED",
"feature": "ai_extractions"
}