All pages

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}/articles

Parameters

ParamInNotes
slugpathThe edition UUID from GET /v1/editions. A segment that is not a UUID is a 404.
sincequeryISO-8601 datetime or date, EXCLUSIVE. A bare date means midnight UTC. Filters on created_at.
statusquerydraft or published. Omit for both. Anything else is a 422.
limitqueryPage size. Default 20, clamped to 1-100 rather than rejected.
offsetquerySkip 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.

FieldTypeNotes
idstringA UUID. It is a string, not a number.
bodystringThe whole article as HTML. This is the field you publish.
intro / conclusionstringAlways "". v5 stores one HTML column, and the split is not faked.
tagsarrayAlways []. v5 stores none.
sectionnullAlways null. v5 has no sections.
source_urlstringAlways "". An article keeps a source LIST with no ordering, so there is no single source URL to name.
published_atnullAlways null. v5 has no published_at column, and deriving one from created_at would be a fabricated date.
article_typestringAlways "standard".
statusstringdraft or published only. An article that is finished but not yet published reports as draft.
url_pathstring"/{slug}", or "" when the article has no slug. No section prefix.
bylinestringThe article's named author, else the domain's editor of record, else the edition name. The same name the CMS publishes under.
image.featured_urlstringA RELATIVE path to the image endpoint, or null when the article has no image. Join it onto the base URL.
image.sourcestringA vendor-free category: ai:generated, web:<domain>, news:<domain>, source:<domain>, or manual:.
created_atstringNaive ISO-8601, UTC, no offset. This is what ?since= filters on.

Errors

StatusCodeWhen
401invalid_keyMissing, invalid, or revoked API key.
429rate_limitedOver 120 requests/minute on this key. Retry-After: 60.
503unavailableThe data store could not be reached.
404not_foundNo such edition on your account, or the slug is not a UUID. Another tenant's edition is a 404, never a 403.
422invalid_paramsstatus 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.