List articles
The endpoint you will actually run on a schedule. Articles come back oldest first with the full body HTML, so a stable incremental pull is just a stored timestamp and a since parameter.
GET
/v1/editions/{slug}/articlesParameters
| Param | In | Notes |
|---|---|---|
| slug | path | The edition UUID from GET /v1/editions. A segment that is not a UUID is a 404. |
| since | query | ISO-8601 datetime or date, EXCLUSIVE. A bare date means midnight UTC. Filters on created_at. |
| status | query | draft or published. Omit for both. Anything else is a 422. |
| limit | query | Page size. Default 20, clamped to 1-100 rather than rejected. |
| offset | query | Skip count. Negative values are clamped to 0. Paging past the end is an empty list, not an error. |
Request
curl "https://api.mastheads.app/v1/editions/3f9c1e84-2b7a-4d6e-9c15-8a0d7e6b4f21/articles?since=2026-07-05T00:00:00Z&limit=50" \
-H "Authorization: Bearer mh_live_your_key_here"Response
200 OK
{
"total": 142,
"limit": 50,
"offset": 0,
"articles": [
{
"id": "a1d4c7b2-5e83-4f19-b06c-2d9f8e3a7c54",
"slug": "harbour-works-overrun-by-two-years",
"url_path": "/harbour-works-overrun-by-two-years",
"status": "published",
"title": "Harbour works overrun by two years",
"intro": "",
"body": "<p>The harbour redevelopment ...</p>",
"conclusion": "",
"meta_title": "Harbour works overrun by two years",
"meta_description": "The council's own timetable ...",
"tags": [],
"section": null,
"language_code": "en",
"article_type": "standard",
"word_count": 842,
"source_url": "",
"byline": "Jordan Avery",
"image": {
"featured_url": "/v1/editions/3f9c1e84-2b7a-4d6e-9c15-8a0d7e6b4f21/articles/a1d4c7b2-5e83-4f19-b06c-2d9f8e3a7c54/image",
"source": "ai:generated"
},
"created_at": "2026-07-05T08:12:00",
"published_at": null
}
]
}Seven of these fields are constant in v5 and are shown as they really come back. They are listed rather than dropped because /v1 is a preserved v4 contract: the keys are still present, so a v4 client keeps parsing.
| Field | Type | Notes |
|---|---|---|
| id | string | A UUID. It is a string, not a number. |
| body | string | The whole article as HTML. This is the field you publish. |
| intro / conclusion | string | Always "". v5 stores one HTML column, and the split is not faked. |
| tags | array | Always []. v5 stores none. |
| section | null | Always null. v5 has no sections. |
| source_url | string | Always "". An article keeps a source LIST with no ordering, so there is no single source URL to name. |
| published_at | null | Always null. v5 has no published_at column, and deriving one from created_at would be a fabricated date. |
| article_type | string | Always "standard". |
| status | string | draft or published only. An article that is finished but not yet published reports as draft. |
| url_path | string | "/{slug}", or "" when the article has no slug. No section prefix. |
| byline | string | The article's named author, else the domain's editor of record, else the edition name. The same name the CMS publishes under. |
| image.featured_url | string | A RELATIVE path to the image endpoint, or null when the article has no image. Join it onto the base URL. |
| image.source | string | A vendor-free category: ai:generated, web:<domain>, news:<domain>, source:<domain>, or manual:. |
| created_at | string | Naive ISO-8601, UTC, no offset. This is what ?since= filters on. |
Errors
| Status | Code | When |
|---|---|---|
| 401 | invalid_key | Missing, invalid, or revoked API key. |
| 429 | rate_limited | Over 120 requests/minute on this key. Retry-After: 60. |
| 503 | unavailable | The data store could not be reached. |
| 404 | not_found | No such edition on your account, or the slug is not a UUID. Another tenant's edition is a 404, never a 403. |
| 422 | invalid_params | status was not draft or published, or since was not ISO-8601. |
Every request needs a key - Authentication. The error shape and the full status table are on Rate limits & errors.