API integration
This article covers the endpoints, authentication, the response shapes, common integration patterns, and the guarantees around compatibility over time.
Every post authored in the Posts module is exposed to external
integrators through the public REST API under /api/v1/post/*.
The API is stable, versioned, documented via Swagger, and
authenticated with a per-organisation API key. Integrators can
list, search, and read individual articles; retrieve per-locale
translations; consume feed and RSS endpoints; and read reader
comments — everything the public site does, available for
building custom front-ends, mobile apps, or content-aggregation
tools.
This article covers the endpoints, authentication, the response shapes, common integration patterns, and the guarantees around compatibility over time.
Authentication
Every request needs a valid API key. See the API key article for how keys are issued, formatted, rotated, and passed on the request. The short version:
- Include the key as
apiKeyin the query string or JSON body. - Every response respects the organisation scope of the key — you never see posts from another organisation, even if you know their identifier.
- Rate limits apply per key tier.
The rest of this article assumes a valid apiKey is included on
every call shown.
Endpoints
GET /api/v1/post/list
List posts belonging to the calling organisation.
Query parameters:
locale— the locale to return the post's title, body, and perex in. Defaults to the organisation's primary locale.mainCategoryCode— filter to posts whose main category has this code. Optional.status— filter by computed status (draft,scheduled,published,private,archived). Optional.visibility— filter by raw visibility (public,private,unlisted,subscribe). Optional.hasMainImage—"true"or"false"to filter by whether a main image is set. Optional.missingTranslation—"true"to filter to posts missing at least one enabled locale. Optional.tagCode— filter to posts carrying the tag with this code.kind—post,branch, orall. Defaults topost.orderBy—<field>:<asc|desc>, e.g.publishedDate:desc. Fields:publishedDate,updatedDate,insertedDate,title,wordCount,commentsCount,viewsCount,ratingAverage.- Standard pagination —
page,limit(or the datagrid-envelope equivalents). filterFulltextQuery— free-text search across title, body, and perex.
Response: an object with items (an array of post summaries)
and itemCount (the total matching count before pagination).
Each item includes:
id— the article's external identifier (stable, unique per organisation).title— up to 140 characters, truncated with an ellipsis.mainCategoryId,mainCategoryName.mainAuthorId,mainAuthorName.publishedDate,insertedDate,updatedDate.visibility,status.mainImageUrl.isStar,isDeleted.wordCount,readingTimeMinutes.hasPerex.tags— array of{ id, name, code, color }.translatedLocales— array of locale codes for which a translation exists.staleLocales— subset oftranslatedLocaleswhose reference is out of date.viewsCount.viewsRecentDelta,viewsPriorDelta— for the trend arrow.ratingAverage,ratingCount.commentsCount.
GET /api/v1/post/detail
Full detail for a single post.
Query parameters:
id— the post's external identifier. Required.locale— the locale to return content in. Defaults to primary.
Response: the article summary above plus:
content— the full HTML body in the requested locale.perex— the article's short summary.metaTitle,metaDescription— SEO fields.authorIds— the ordered list of every author's external identifier.attachments— array of{ id, filename, downloadUrl, size, contentType, insertedDate }.translatedLocales,staleLocales— same semantics as on list.
GET /api/v1/post/{externalId}/comments
Every comment on the given post, in chronological order. Author names and comment text are included; commenter emails are NOT (email is admin-only).
GET /api/v1/post/feed
Convenience endpoint returning the most recent published posts
in a shape optimised for feed consumers — the same fields as
list but sorted by publication date descending and always
excluding private, unlisted, subscribe-only, and archived
posts.
GET /api/v1/post/rss
RSS 2.0 XML feed of the most recent published posts. Suitable
for direct consumption by feed readers. Locale is picked via
locale query parameter (or defaults to primary).
Per-category variants are available at
/api/v1/post/category/{categoryCode}/rss.
Visibility filtering
The API respects post visibility strictly:
public— always returned.unlisted— returned on detail requests when the caller supplies the specificid, but NOT included inlistorfeedresponses. This matches the behaviour of the public site.subscribe— only returned to authenticated subscribers. Anonymous integrator requests receive a gated response.private— never returned on any endpoint. Editable only through the admin.archived(deleted) — never returned. To surface deleted posts for a specific integration, use the admin surface, not this API.
Locale handling
For every content-returning endpoint, the locale query
parameter picks which language's title, body, perex, and meta
fields are returned. Behaviour:
- Requested locale exists — return it.
- Requested locale is missing — fall back to the organisation's primary locale.
- Neither — an empty response for the content fields (rare; only when the post has no translations at all).
The response always tells you which locale was actually served
via the translatedLocales field, so a caller can detect
fallback and react.
Rate limits
Every API key has a tier (Production, Development, System — see
API key for the tiers). Requests
above the tier's per-minute quota receive an HTTP 429 with a
Retry-After header. Well-behaved integrations should
implement exponential backoff on 429.
Public read endpoints are cached edge-side for a short interval (seconds); repeated identical requests within that window return cached responses that do not count against the quota as aggressively.
Errors
Standard shape for every error response:
{
"error": {
"code": "post_not_found",
"message": "Post 'aBcDeFgH12345678' was not found in this organisation.",
"details": {}
}
}
Common error codes on the post endpoints:
post_not_found— the requestediddoes not exist or the key does not have access.invalid_locale— the locale parameter is not a supported locale code.rate_limit_exceeded— too many requests; wait forRetry-Afterseconds before retrying.unauthorised— API key missing, malformed, or invalid.forbidden— the endpoint is not enabled for the caller's tier.
View counting
Every detail request (and, depending on the integration
theme, every list request) increments the article's view
counter. See Views and analytics
for the buffering behaviour.
Integrations that want to fetch article data without
inflating the view counter (e.g. for internal migration
tooling, editorial dashboards) should include the
countAsView=false query parameter. This is respected on
detail and suppresses the counter increment.
Common integration patterns
Embedding an article in another product
- Call
/detailwith the targetidand desiredlocale. - Render
title,perex,mainImageUrl, andcontent(the HTML body) inside your own theme. - Optionally show
mainAuthorName,publishedDate,readingTimeMinutesfor byline context.
A syndicated site aggregating multiple organisations
- For each source organisation, use its API key.
- Call
/feedon each to get recent posts. - Merge, dedupe (using the
idper source), sort, render.
A mobile app browsing the article catalogue
- Call
/listwith pagination and the current visitor's locale. - Render the list; on tap, call
/detail. - Cache aggressively on the client — the article
iduniquely identifies a version, and any change bumpsupdatedDatewhich the client can use as a cache-buster.
Building a custom search interface
- Call
/listwithfilterFulltextQueryand locale. - Render results, highlighting the query terms in the
returned
titleandperex.
Compatibility guarantees
The /api/v1/post/* endpoints are covered by the platform's
public API stability policy:
- Additive changes (new fields, new optional parameters, new endpoints) can appear at any time and are not breaking.
- Behavioural changes (removing a field, renaming a field,
changing the shape of an existing response) require a new
major version —
/api/v2/*— and thev1endpoints remain available for the announced deprecation window (typically at least a year). - Deprecation announcements appear in the platform's release notes; integrations should subscribe to those.
Where the OpenAPI spec lives
The full machine-readable OpenAPI spec is available at
https://docs.bizkithub.com/api — every endpoint is
documented there with its request parameters, response shape,
error codes, and inline examples. The Swagger explorer on the
docs site lets you try a call live from your browser using your
own API key.
Related
- API key — how authentication works.
- Post management overview — the admin surface the API mirrors.
- Translations — for how locale parameter interacts with translation freshness.
- Views and analytics
- Comments and moderation — for how comment data is exposed.
- Reader feedback — for the rating fields on the API.