List articles
A page of the organization's published, public help-center articles, newest first.
GET /api/v1/public/articlesReturns a page of your organization's help-center articles, ordered by published_at,
newest first.
Authentication
Publishable key: Authorization: Bearer pk_….
Query parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
page | integer | 1 | 1-based page number. Any value < 1 is coerced to 1. |
per_page | integer | 20 | Page size. Any value < 1 falls back to 20; values above 100 are capped at 100. |
category_id | uuid | — | Restrict results to one category. A value that isn't a parseable UUID is ignored, not rejected. |
search | string | — | Case-insensitive substring match against the article title or body. |
Only published, public articles are ever returned
Drafts and internal articles are invisible to this API — there is no parameter that
exposes them. An article your team can see in the dashboard will not appear here
unless it is both published and visibility = public.
Response
200 OK. data is an array of articles; meta carries the pagination totals.
| Field | Type | Description |
|---|---|---|
id | uuid | The article ID. |
category_id | uuid or null | The category it belongs to, if any. |
title | string | The article title. |
slug | string | URL-safe name. Usable directly as the ref in Get an article. |
body | string | The full article body. |
published_at | string or null | ISO 8601 timestamp. |
updated_at | string | ISO 8601 timestamp. |
{
"success": true,
"request_id": "6d7979ce52075b24979cd5b415319339",
"data": [
{
"id": "019f21a3-b425-7ce6-a708-192a3b4c5d6e",
"category_id": "019f2192-a314-7bd5-96f7-08192a3b4c5d",
"title": "How to request a refund",
"slug": "how-to-request-a-refund",
"body": "You have 30 days from delivery to request a refund.",
"published_at": "2026-07-02T10:00:00.000000Z",
"updated_at": "2026-07-02T10:00:00.000000Z"
}
],
"meta": { "page": 1, "per_page": 20, "total": 42, "total_pages": 3 }
}The list response carries every article's full body
There is no summary or excerpt field: body comes back in full for every article
on the page. That makes this heavier than a typical index endpoint. If you're
rendering a directory of titles, ask for a modest per_page rather than pulling 100
full articles you won't show.
Paginating
meta.total_pages tells you when to stop.
async function allArticles(key: string) {
const articles = [];
let page = 1;
let totalPages = 1;
do {
const res = await fetch(
`${BASE_URL}/api/v1/public/articles?page=${page}&per_page=20`,
{ headers: { Authorization: `Bearer ${key}` } },
);
const { data, meta } = await res.json();
articles.push(...data);
totalPages = meta.total_pages;
page += 1;
} while (page <= totalPages);
return articles;
}Errors
| Status | error.code | When |
|---|---|---|
401 | UNAUTHORIZED | The key is missing, unknown, revoked, or is an sk_ key. |
429 | RATE_LIMITED | Your organization's publishable-key budget is exhausted. |
Note that a bad category_id is not an error — it's ignored, and you get the
unfiltered page back. If a category filter seems to do nothing, check that you're
sending a well-formed UUID.
Examples
# Second page of the Billing category, 10 at a time
curl -G "{BASE_URL}/api/v1/public/articles" \
-H "Authorization: Bearer pk_example_k3Yb9wQ2rTfA8sLdN1vH" \
--data-urlencode "category_id=019f2192-a314-7bd5-96f7-08192a3b4c5d" \
--data-urlencode "page=2" \
--data-urlencode "per_page=10"
# Search titles and bodies
curl -G "{BASE_URL}/api/v1/public/articles" \
-H "Authorization: Bearer pk_example_k3Yb9wQ2rTfA8sLdN1vH" \
--data-urlencode "search=refund"