The passages readers marked in this article, in the order they appear in
the body. Use start_offset and end_offset to place them, and quote
with prefix and suffix to re anchor when the body has since changed.
Highlights are not attributed to a named person. Each carries an
owner_key, an opaque value that is stable per highlighter within one
article and meaningless across articles, so a client can count distinct
highlighters and group one person's marks without learning who they are.
Headers
-
The contract version this request is written against, as a release date (
YYYY-MM-DD). Omitting the header pins the request to the version that was current when the API key was issued, so an integration keeps working when a newer version ships. An unknown value answers400withinvalid_version.
Path parameters
-
The article's
id(a UUID) or itsshort_id, an eight character base62 string that is unique across Commune. Theslugis not accepted here because it is unique only within a newsletter.
Query parameters
-
The
pagination.next_cursorvalue from the previous page. Omit it to read the first page. A cursor is opaque, is only valid for the same operation with the same filters, and is not a durable identifier.Maximum length is
512. -
How many items to return in this page. This is a page size, not an offset. Fewer items than requested may come back and that does not mean the collection is exhausted, only an absent
next_cursordoes.Minimum value is
1, maximum value is100. Default value is20. -
Comma separated list of relationship paths to inline in the response. Unexpanded relationships are returned as a reference object carrying only
idandobject. Each operation documents the paths it accepts, and an unknown path answers400. Nested paths use a dot, for examplearticle.newsletter.One accepted path is not a relationship.
?expand=contentonGET /articles/{article}adds the Markdown rendition of the body beside the HTML one. It is the same trade the parameter always offers, a fuller response for a larger one, over a property that has more than one representation rather than over a reference. -
Comma separated allow list of top level properties to return on each object, so a client can trim a response it does not need in full.
idandobjectare always returned. An unknown property name answers400. Properties omitted by an operation, such ascontenton any article list, cannot be brought back withfields.A trimmed body is a subset of the schema this operation declares, and a property that schema marks required is absent when it was not asked for. That is the point of the parameter, so a client that validates responses against the schema either sends no
fieldsor relaxesrequired.
Responses
-
A page of highlights, in body order.
-
The request was malformed: an unknown query parameter, an unparseable cursor, an unknown
expandpath, or an unrecognisedCommune-Version. -
No API key was presented, or the key is unknown, revoked or expired.
All four answer identically, down to the wording. Saying that a key was revoked rather than never issued confirms to whoever is holding the string that it was once real, which a legitimate caller does not need and a thief should not get.
-
No such resource, or the key is not allowed to know that it exists. Commune answers
404rather than403where distinguishing the two would leak the existence of private content. -
Too many requests. Back off and retry after the interval named by the
Retry-Afterresponse header. -
Something failed inside Commune. The request may be retried.
curl https://api.usecommune.com/articles/k7Rm2xQp/highlights \
-H "Authorization: Bearer $COMMUNE_API_KEY" \
-H "Commune-Version: 2026-08-26"
curl \
--request GET 'https://api.usecommune.com/articles/k7Rm2xQp/highlights' \
--header "Authorization: Bearer $ACCESS_TOKEN" \
--header "Commune-Version: 2026-08-26"
{
"object": "list",
"data": [],
"pagination": {
"has_more": true,
"next_cursor": "Y3Vyc29yOjE3NTY0MjM2MDAwMDA6MDE5MmM4"
}
}
{
"error": {
"code": "bad_request",
"message": "Newsletter not found.",
"param": "cursor",
"allowed_values": [
"subscribed",
"unsubscribed",
"bounced",
"complained",
"pending"
],
"request_id": "req_01j9c8h1q7m3n4p5r6s7t8u9v0",
"docs_url": "https://usecommune.com/docs/api/errors#not_found"
}
}
# Headers
WWW-Authenticate: string
# Payload
{
"error": {
"code": "bad_request",
"message": "Newsletter not found.",
"param": "cursor",
"allowed_values": [
"subscribed",
"unsubscribed",
"bounced",
"complained",
"pending"
],
"request_id": "req_01j9c8h1q7m3n4p5r6s7t8u9v0",
"docs_url": "https://usecommune.com/docs/api/errors#not_found"
}
}
{
"error": {
"code": "bad_request",
"message": "Newsletter not found.",
"param": "cursor",
"allowed_values": [
"subscribed",
"unsubscribed",
"bounced",
"complained",
"pending"
],
"request_id": "req_01j9c8h1q7m3n4p5r6s7t8u9v0",
"docs_url": "https://usecommune.com/docs/api/errors#not_found"
}
}
# Headers
Retry-After: 42
# Payload
{
"error": {
"code": "bad_request",
"message": "Newsletter not found.",
"param": "cursor",
"allowed_values": [
"subscribed",
"unsubscribed",
"bounced",
"complained",
"pending"
],
"request_id": "req_01j9c8h1q7m3n4p5r6s7t8u9v0",
"docs_url": "https://usecommune.com/docs/api/errors#not_found"
}
}
{
"error": {
"code": "bad_request",
"message": "Newsletter not found.",
"param": "cursor",
"allowed_values": [
"subscribed",
"unsubscribed",
"bounced",
"complained",
"pending"
],
"request_id": "req_01j9c8h1q7m3n4p5r6s7t8u9v0",
"docs_url": "https://usecommune.com/docs/api/errors#not_found"
}
}