API Documentation
REST API for managing subscriptions programmatically. You can also download the OpenAPI spec.
Overview
Authentication
The API uses token authentication. Manage your tokens from the API tokens page.
Pass the token in the Authorization header on every request:
Authorization: Bearer {your-token}
Base URL
https://blogtrottr.com/api
All endpoints are relative to this base. Requests must include Accept: application/json.
Pagination
List responses are paginated at 100 items per page:
{
"data": [ ... ],
"links": {
"first": "https://...",
"last": "https://...",
"prev": null,
"next": "https://...?page=2"
},
"meta": {
"current_page": 1,
"last_page": 3,
"per_page": 100,
"total": 250
}
}
Use the page query parameter to navigate pages.
Field Reference
schedule
| Value | Description |
|---|---|
asap | Real-time — one email per new item |
2h_digest | Digest every 2 hours |
4h_digest | Digest every 4 hours |
6h_digest | Digest every 6 hours |
8h_digest | Digest every 8 hours |
12h_digest | Digest every 12 hours |
daily_digest | Daily digest |
output
| Value | Description |
|---|---|
email | Standard HTML email |
attach_pdf | Email with PDF attachment |
attach_html | Email with HTML attachment |
attach_text | Email with plain text attachment |
webhook | HTTP webhook delivery |
digest_sort
Only applicable when schedule is a digest type. Ignored for asap.
| Value | Description |
|---|---|
newest_first | Most recent items at the top (default) |
oldest_first | Oldest items at the top |
title_az | Sort by title A–Z |
title_za | Sort by title Z–A |
Filter object
The filter field on subscriptions and categories is a structured content filter. When a filter is enabled, each incoming feed item is tested against it; items that do not pass are skipped.
Top-level structure
| Field | Type | Description |
|---|---|---|
version | integer | Must be 2 |
enabled | boolean | Whether the filter is active. Set to false to disable without removing the filter. |
action | string | permit — only deliver items that match. deny — skip items that match. |
group_operator | string | How to combine multiple groups: and or or |
groups | array | Up to 5 rule groups (see below) |
Group
| Field | Type | Description |
|---|---|---|
operator | string | How to combine rules within the group: and or or |
rules | array | 1–250 rules. Total rules across all groups may not exceed 500. |
Rule
| Field | Type | Description |
|---|---|---|
field | string | item_title, item_body, item_url, author, category |
operator | string | contains, not_contains, regex, not_regex |
value | string | Match value, max 500 chars. For regex / not_regex: a PCRE pattern (no delimiters), max 200 chars, matched case-insensitively. |
Example
Permit only items whose title contains "AI" or "machine learning":
{
"version": 2,
"enabled": true,
"action": "permit",
"group_operator": "or",
"groups": [
{
"operator": "or",
"rules": [
{ "field": "item_title", "operator": "contains", "value": "AI" },
{ "field": "item_title", "operator": "contains", "value": "machine learning" }
]
}
]
}
JSON Schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": ["version", "enabled", "action", "group_operator", "groups"],
"additionalProperties": false,
"properties": {
"version": { "type": "integer", "const": 2 },
"enabled": { "type": "boolean" },
"action": { "type": "string", "enum": ["permit", "deny"] },
"group_operator": { "type": "string", "enum": ["and", "or"] },
"groups": {
"type": "array",
"maxItems": 5,
"items": {
"type": "object",
"required": ["operator", "rules"],
"additionalProperties": false,
"properties": {
"operator": { "type": "string", "enum": ["and", "or"] },
"rules": {
"type": "array",
"minItems": 1,
"maxItems": 250,
"items": {
"type": "object",
"required": ["field", "operator", "value"],
"additionalProperties": false,
"properties": {
"field": { "type": "string", "enum": ["item_title", "item_body", "item_url", "author", "category"] },
"operator": { "type": "string", "enum": ["contains", "not_contains", "regex", "not_regex"] },
"value": { "type": "string", "minLength": 1, "maxLength": 500 }
}
}
}
}
}
}
}
}
Note: the 500-rule total limit across all groups and the 200-character cap on regex patterns cannot be expressed in JSON Schema and are enforced server-side.
Errors
All errors return JSON:
{
"message": "The url field is required.",
"errors": {
"url": ["The url field is required."]
}
}
Feed discovery failures return:
{
"message": "Feed not found.",
"details": { ... }
}
HTTP status codes
| Code | Meaning |
|---|---|
200 | OK |
201 | Created |
204 | No Content |
401 | Unauthenticated — missing or invalid token |
403 | Forbidden — token lacks required permission |
404 | Not found |
422 | Validation error |
Subscriptions
List subscriptions
/api/subscriptions
permission: read
Returns a paginated list of subscriptions.
Query parameters
| Parameter | Type | Description |
|---|---|---|
url | string | Filter by exact feed URL |
enabled | boolean | Filter by enabled state (true / false) |
schedule | string | Filter by schedule value (see Field Reference) |
output | string | Filter by output type (see Field Reference) |
category_id | string|null | Filter by category ID. Pass null to list uncategorised subscriptions. |
Example response
{
"data": [
{
"id": "aabbccddeeff001122334455",
"enabled": true,
"url": "https://example.com/feed.rss",
"schedule": "asap",
"output": "email",
"digest_sort": null,
"from": null,
"subject_format": null,
"title_only": false,
"user_title": null,
"filter": { "version": 2, "enabled": false, ... },
"email_address_id": null,
"created_at": "2024-01-15T10:30:00.000000Z",
"updated_at": "2024-01-15T10:30:00.000000Z"
}
],
"links": { ... },
"meta": { ... }
}
Get subscription
/api/subscriptions/{id}
permission: read
Returns a single subscription.
| Status | Meaning |
|---|---|
200 | Success |
404 | Subscription not found |
Create subscription
/api/subscriptions
permission: create
Creates a new subscription. The feed URL will be discovered and normalised — if it resolves or redirects to a different URL than submitted, the final URL will be used.
Request body (JSON)
| Field | Required | Type | Notes |
|---|---|---|---|
url | Yes | string (URL) | Feed URL, max 1000 chars. Must be unique per account. |
schedule | Yes | string | See Field Reference |
output | Yes | string | See Field Reference |
digest_sort | No | string | Default: newest_first. See Field Reference. |
from | No | string | Custom sender name, max 150 chars |
subject_format | No | string | Custom email subject template, max 150 chars |
utitle | No | string | Custom subscription title, max 150 chars |
titleonly | No | boolean | Send item title only (no body) |
email_address_id | No | string | ID of an additional email address to deliver to |
category_id | No | string|null | ID of a category to assign this subscription to |
filter | No | object | Content filter rules object |
webhook_url | If output=webhook | string (URL) | Webhook endpoint, max 2048 chars. Must be a public URL. |
webhook_headers | No | array | Up to 10 custom headers. Each: {"key": "...", "value": "..."} |
webhook_content_type | No | string | application/json (default), application/x-www-form-urlencoded, text/plain |
webhook_body_template | If non-JSON webhook | string | Body template for non-JSON webhooks, max 10000 chars |
Example request
POST /api/subscriptions
Content-Type: application/json
Authorization: Bearer {token}
{
"url": "https://example.com/feed.rss",
"schedule": "asap",
"output": "email"
}
Responses
| Status | Meaning |
|---|---|
201 | Subscription created. Returns the subscription object. |
422 | Validation error (invalid fields, duplicate URL, plan limit reached, or feed not found) |
Update subscription
/api/subscriptions/{id}
permission: update
Partially updates a subscription. Only include the fields you want to change — all fields are optional.
Request body (JSON)
| Field | Type | Notes |
|---|---|---|
enabled | boolean | Enable or pause the subscription |
schedule | string | See Field Reference |
output | string | See Field Reference |
digest_sort | string | See Field Reference |
from | string|null | Custom sender name |
subject_format | string|null | Custom email subject template |
utitle | string|null | Custom subscription title |
titleonly | boolean|null | Send item title only |
email_address_id | string|null | Additional delivery email address ID |
category_id | string|null | Category ID. Pass null to remove from a category. |
filter | object | Content filter rules object |
webhook_url | string|null | Webhook endpoint |
webhook_headers | array|null | Custom headers array |
webhook_content_type | string|null | Webhook content type |
webhook_body_template | string|null | Webhook body template |
Responses
| Status | Meaning |
|---|---|
200 | Updated. Returns the updated subscription object. |
404 | Subscription not found |
422 | Validation error |
Delete subscription
/api/subscriptions/{id}
permission: delete
Permanently deletes a subscription.
| Status | Meaning |
|---|---|
204 | Deleted successfully. No response body. |
404 | Subscription not found |
Email addresses
List email addresses
/api/email-addresses
permission: read
Returns a paginated list of additional email addresses on the account. Use the id field as the email_address_id when creating or updating subscriptions.
Query parameters
| Parameter | Type | Description |
|---|---|---|
email | string | Filter by exact email address (case-insensitive) |
Example response
{
"data": [
{
"id": "aabbccddeeff001122334455",
"email": "[email protected]",
"enabled": true,
"created_at": "2024-01-15T10:30:00.000000Z"
}
],
"links": { ... },
"meta": { ... }
}
Get email address
/api/email-addresses/{id}
permission: read
Returns a single email address.
| Status | Meaning |
|---|---|
200 | Success |
404 | Email address not found |
Example response
{
"data": {
"id": "aabbccddeeff001122334455",
"email": "[email protected]",
"enabled": true,
"created_at": "2024-01-15T10:30:00.000000Z"
}
}
Categories
List categories
/api/categories
permission: read
Returns a paginated list of the account's subscription categories, ordered by name.
Example response
{
"data": [
{
"id": "aabbccddeeff001122334455",
"name": "Tech",
"filter": null,
"created_at": "2024-01-15T10:30:00.000000Z",
"updated_at": "2024-01-15T10:30:00.000000Z"
}
],
"links": { ... },
"meta": { ... }
}
Get category
/api/categories/{id}
permission: read
Returns a single category.
| Status | Meaning |
|---|---|
200 | Success |
404 | Category not found |
Create category
/api/categories
permission: create
Request body (JSON)
| Field | Required | Type | Notes |
|---|---|---|---|
name | Yes | string | Category name, max 80 chars. Must be unique per account. |
filter | No | object|null | Content filter rules object applied to all subscriptions in this category |
Responses
| Status | Meaning |
|---|---|
201 | Category created. Returns the category object. |
422 | Validation error (missing name, duplicate name) |
Update category
/api/categories/{id}
permission: update
Partially updates a category. Only include the fields you want to change.
Request body (JSON)
| Field | Type | Notes |
|---|---|---|
name | string | Category name, max 80 chars. Must be unique per account. |
filter | object|null | Content filter rules. Pass null to remove. |
Responses
| Status | Meaning |
|---|---|
200 | Updated. Returns the updated category object. |
404 | Category not found |
422 | Validation error |
Delete category
/api/categories/{id}
permission: delete
Permanently deletes a category. Any subscriptions assigned to it will have their category_id set to null.
| Status | Meaning |
|---|---|
204 | Deleted successfully. No response body. |
404 | Category not found |