API
Content API
One authenticated GET endpoint that answers in four modes: a single article, a paginated category feed, the full sitemap, or nothing. Everything a blog front end needs to render published content.
This is the endpoint your website calls. It is read-only, it returns published content only, and it does exactly four things depending on which query parameters you send.
/api/external/articlesRead published articles from one project. The project is the one the API key belongs to -- there is no project parameter.
Auth Project API key, read (level 2) or read + write (level 3)
Parameters
| handle | string | URL handle of one article, for example guides/getting-started. Returns the full document. |
| article_id | string | MongoDB ObjectId of one article. Alternative to handle. |
| category | string | Category slug for a paginated feed, or all for every category. Requires lang, page and limit. |
| lang | string | Language code, matched exactly as the project stores it -- usually uppercase, for example EN. Required with category. Max 3 characters. |
| page | integer | Zero-indexed page number. Required with category. |
| limit | integer | Items per page, 1 to 30. Required with category. |
| sitemap | boolean | true returns every published handle with its dates, and ignores every other parameter. |
| private_key | string | Deprecated way to pass the API key. Use the Authorization header. |
Responses
200The article, the feed, or the sitemap. Shape depends on the mode.400A category feed was requested withoutpage,limitorlang.401Missing or unknown API key.403The key exists but its permission level does not allow reading.404No published article matches thehandleorarticle_id.
One mode per request
The parameters are evaluated in order: sitemap, then category, then article_id, then handle. Mixing them wastes a query and returns only the last one that matched. Sending none of them returns an empty array.
Mode 1: one article#
The workhorse. Give it the handle from your route and it returns the whole document, markdown body included, plus any structured metadata attached to the article and currently in effect.
curl -H "Authorization: Bearer $REBLOG_API_KEY" \
"https://reblog.so/api/external/articles?handle=guides/getting-started"The response is the article document. Every field is described on the article object page.
Mode 2: a category feed#
Paginated, newest first, one language at a time. Pass category=all to list everything in that language instead of one section.
curl -H "Authorization: Bearer $REBLOG_API_KEY" \
"https://reblog.so/api/external/articles?category=guides&lang=EN&page=0&limit=10"{
"articles": [
{
"_id": "66f3c1a2b4d5e6f708192a3b",
"title": "Getting started with Reblog",
"handle": "guides/getting-started",
"lang": "EN",
"status": "published",
"date": "2026-02-11T09:00:00.000Z",
"cover": "https://cdn.example.com/covers/getting-started.png",
"meta_description": "How to publish your first article.",
"categories": ["guides"]
}
],
"has_more": true,
"page": 0,
"limit": 10
}`has_more`, not a total
The feed fetches one extra row to decide whether another page exists, rather than counting the whole collection on every request. There is no total field: paginate until has_more is false.
`lang` is matched exactly
Project languages are stored with a fixed casing, almost always uppercase. lang=EN works, lang=en returns an empty list rather than an error. Use the casing shown in the project's language settings.
Mode 3: the sitemap#
Every published article across every language, projected down to the three fields a sitemap needs. Cheap enough to call on a schedule.
curl -H "Authorization: Bearer $REBLOG_API_KEY" \
"https://reblog.so/api/external/articles?sitemap=true"{
"articles": [
{
"handle": "guides/getting-started",
"date": "2026-02-11T09:00:00.000Z",
"edit_date": "2026-03-02T14:22:10.000Z"
}
]
}Use edit_date for <lastmod> when it is present, and fall back to date.
Drafts and scheduled posts never appear#
Every mode applies the same two conditions: the article's status must be published, and its publication date must already have passed. An article scheduled for next Tuesday is invisible to this endpoint until next Tuesday, so you can safely serve the response from a cache without leaking an embargo.
Caching#
The endpoint sends no cache headers -- caching is yours to decide, because only you know how fast your blog moves. Two patterns work well:
- Incremental static regeneration -- fetch with
next: { revalidate: 3600 }in a Next.js server component, and let the framework rebuild pages in the background. - Build-time fetch -- pull the sitemap, then each article, and ship static HTML. Re-run the build when you publish.
Never call this from the browser
The request carries a key with project-wide read access. Fetch it on the server, in a route handler, or at build time, and send your visitors the rendered result.