Public read API

These unauthenticated routes expose active public publications and their published content. They return JSON except the RSS feed and XML sitemap.

Publication selector

Every route requires exactly one active publication selector:

{ "publication_slug": "engineering-notes" }

publication_id is also accepted. The public read API does not resolve browser hosts or paths; use /domain/resolve and /redirect/resolve where those lookups are needed.

Routes

Route Methods Required fields Optional fields Success result
/read/publication GET, POST Publication selector public_publication.
/read/home GET, POST Publication selector category_slug, pagination Paginated public posts.
/read/archive GET, POST Publication selector year, month, pagination Archive facets and public posts.
/read/search GET, POST Publication selector, q Pagination Query and public posts.
/read/categories GET, POST Publication selector Pagination Paginated categories.
/read/category GET, POST Publication selector, category_slug Pagination public_category and public posts.
/read/tags GET, POST Publication selector Pagination Paginated tags.
/read/tag GET, POST Publication selector, tag_slug Pagination public_tag and public posts.
/read/authors GET, POST Publication selector Pagination Paginated authors.
/read/author GET, POST Publication selector, author_slug Pagination public_author and public posts.
/read/post GET, POST Publication selector, post_slug public_post.
/read/page GET, POST Publication selector, page_slug public_page.
/read/feed GET, POST Publication selector site_url, post_path_prefix, limit RSS 2.0 XML.
/read/sitemap GET, POST Publication selector site_url, post_path_prefix, page_path_prefix XML sitemap.

Public list routes use the pagination contract in the service guide. Lists include only published, public, unarchived posts; posts under inactive categories are also excluded. limit defaults to 50 and is capped at 200 for these JSON lists.

Publication and content reads

curl -sS https://api.blog.service.m7.org/v1/read/post \
  -H 'Content-Type: application/json' \
  -d '{"publication_slug":"engineering-notes","post_slug":"release-notes"}'
{
  "status": 1,
  "comment": "post loaded",
  "data": {
    "public_post": {
      "slug": "release-notes",
      "title": "Release notes",
      "excerpt": "A summary.",
      "body": "<h1>Release notes</h1>",
      "body_format": "html",
      "source_body_format": "markdown",
      "visibility": "public",
      "published_at": "2026-08-19 00:00:00.000000",
      "category": { "slug": "updates", "name": "Updates" },
      "tags": [{ "slug": "release-notes", "name": "Release Notes" }],
      "authors": [{ "slug": "ada-lovelace", "name": "Ada Lovelace" }]
    }
  }
}

/read/post and /read/page return rendered HTML in body; source_body_format tells whether the stored body was Markdown or HTML. They permit published public and unlisted content, but not private content. Draft, review, scheduled, archived, disabled-publication, and inactive-category content returns 404.

Lists, archives, and search

/read/home accepts category_slug to select one active category. /read/archive accepts year from 1000 through 9998 and, only with year, month from 1 through 12. It returns archive and archive_periods alongside the normal paginated posts.

/read/search requires string q: one to 120 characters and at most eight distinct terms. All terms must match a post title, excerpt, or body. The result's query is the normalized query; results are ordered by relevance and then published date.

Each public post summary includes slug, title, nullable excerpt, nullable featured_media_id, nullable featured_media, nullable category, and ordered tags and authors. Public categories, tags, and authors include a post_count. Category summaries also include description, nullable summary, and optional featured media. Public author summaries include bio, external_url, and socials.

Feed and sitemap

/read/feed streams application/rss+xml; charset=UTF-8; /read/sitemap streams application/xml; charset=UTF-8. Both need a canonical site URL. Send site_url as an absolute http or https URL without a query string or fragment, or omit it to use a verified publication domain.

{
  "publication_slug": "engineering-notes",
  "site_url": "https://notes.example.com",
  "post_path_prefix": "/posts",
  "page_path_prefix": ""
}

If no site_url is supplied and the publication has no verified domain, these routes return 422. Path prefixes are optional, must be path-only values, and default to /posts for feed post links, /posts for sitemap post links, and an empty path for sitemap page links. Feed limit defaults to 50 and is capped at 100.