Pagination & Filtering
Control what the Public API returns — paginate large lists, sort results, pick specific fields, and request a locale.
Pagination & Filtering
All Public API list endpoints (GET /public/entries and GET /public/pages) share
a common set of query parameters for pagination, sorting, and field selection.
Every request requires an X-API-Key header.
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
type | string | — | Required for entries. Content type slug (e.g. article, product). |
locale | string | workspace default | Return field values for this locale. Falls back to the workspace default locale when no translation exists. |
fields | string | all fields | Comma-separated field names to include in data. Supports dot notation for relations. |
page | integer | 1 | Page number (offset pagination). Ignored when cursor is provided. Maximum: 1000. |
limit | integer | 10 | Results per page. Maximum: 100. |
sort | string | created_at:desc | Sort field and direction separated by a colon (e.g. published_at:desc). |
cursor | string | — | Opaque cursor from meta.next_cursor. When provided, page is ignored. |
The Response Envelope
Every list response wraps results in a consistent envelope:
{
"success": true,
"message": "Success",
"data": [
{
"slug": "getting-started-with-headless-cms",
"status": "published",
"data": {
"title": "Getting Started with Headless CMS",
"summary": "A beginner-friendly introduction to decoupled content management.",
"published_at": "2026-05-10T08:00:00Z"
}
},
{
"slug": "api-driven-content-strategy",
"status": "published",
"data": {
"title": "API-Driven Content Strategy for Modern Teams",
"summary": "How leading companies structure their content for multi-channel delivery.",
"published_at": "2026-05-03T08:00:00Z"
}
}
],
"meta": {
"total": 38,
"page": 1,
"limit": 10,
"next_cursor": "eyJpZCI6IDEwfQ"
}
}meta field | Type | Description |
|---|---|---|
total | integer | Total entries matching the query. Returns -1 in cursor mode (count is skipped for performance). |
page | integer | Current page (offset mode only). |
limit | integer | Number of entries returned in this response. |
next_cursor | string | null | Pass this as ?cursor= in the next request to fetch the following page. null when there are no more results. |
Offset Pagination
Use page and limit when you need numbered pages or the dataset is small to
medium (up to a few thousand entries).
curl "https://api.atlas.latellu.com/api/v1/public/entries?type=article&page=2&limit=20" \
-H "X-API-Key: <your_api_key>"{
"success": true,
"data": [
{
"slug": "css-grid-deep-dive",
"status": "published",
"data": {
"title": "CSS Grid Deep Dive",
"category": "Frontend",
"published_at": "2026-03-18T09:00:00Z"
}
},
{
"slug": "typescript-generics-explained",
"status": "published",
"data": {
"title": "TypeScript Generics Explained",
"category": "Backend",
"published_at": "2026-03-14T09:00:00Z"
}
}
],
"meta": {
"total": 38,
"page": 2,
"limit": 20,
"next_cursor": "eyJpZCI6IDQwfQ"
}
}Deep page limit
Offset pagination is capped at page 1,000. Requests beyond that return a
400 PAGE_TOO_DEEP error. Switch to cursor pagination for very large datasets.
Cursor Pagination
Cursor pagination is more efficient for large datasets and infinite-scroll feeds.
The server jumps directly to the next page from a known position — it does not
count all matching rows, so meta.total is -1.
Step 1 — first request (no cursor):
curl "https://api.atlas.latellu.com/api/v1/public/entries?type=article&limit=20&sort=created_at:desc" \
-H "X-API-Key: <your_api_key>"{
"success": true,
"data": [
{
"slug": "getting-started-with-headless-cms",
"status": "published",
"data": {
"title": "Getting Started with Headless CMS",
"published_at": "2026-05-10T08:00:00Z"
}
},
{
"slug": "api-driven-content-strategy",
"status": "published",
"data": {
"title": "API-Driven Content Strategy for Modern Teams",
"published_at": "2026-05-03T08:00:00Z"
}
}
],
"meta": {
"total": -1,
"page": 1,
"limit": 20,
"next_cursor": "eyJpZCI6IDIwfQ"
}
}Step 2 — next page (pass the cursor from the previous response):
curl "https://api.atlas.latellu.com/api/v1/public/entries?type=article&limit=20&sort=created_at:desc&cursor=eyJpZCI6IDIwfQ" \
-H "X-API-Key: <your_api_key>"Keep passing the new next_cursor from each response until next_cursor is null
— that means you have reached the last page.
Cursors are opaque
Never parse or construct cursor values manually. The format may change between API
versions. Always use the next_cursor value exactly as returned.
"Load more" pattern
async function fetchPage(cursor = null) {
const url = new URL('https://api.atlas.latellu.com/api/v1/public/entries');
url.searchParams.set('type', 'article');
url.searchParams.set('limit', '20');
url.searchParams.set('sort', 'created_at:desc');
if (cursor) url.searchParams.set('cursor', cursor);
const res = await fetch(url, {
headers: { 'X-API-Key': '<your_api_key>' },
});
const { data, meta } = await res.json();
return { entries: data, nextCursor: meta.next_cursor };
}
// Walk through all pages
let cursor = null;
do {
const { entries, nextCursor } = await fetchPage(cursor);
renderEntries(entries);
cursor = nextCursor;
} while (cursor !== null);Choosing between offset and cursor
| Offset | Cursor | |
|---|---|---|
| Use case | Numbered pages, admin lists | Feeds, infinite scroll |
meta.total | ✓ Available | — Not available |
| Performance on large datasets | Degrades at high page numbers | Consistent |
| Can jump to arbitrary page | ✓ Yes (?page=5) | ✗ Must walk forward |
| Maximum depth | Page 1,000 | Unlimited |
Sorting
Use the sort parameter with field:direction format. Direction is either asc
(ascending) or desc (descending).
curl "https://api.atlas.latellu.com/api/v1/public/entries?type=article&sort=created_at:desc" \
-H "X-API-Key: <your_api_key>"curl "https://api.atlas.latellu.com/api/v1/public/entries?type=product&sort=price:asc" \
-H "X-API-Key: <your_api_key>"Commonly available sort fields:
| Field | Description |
|---|---|
created_at | Entry creation date (default) |
published_at | Date the entry was published |
position | Manual order set in the dashboard (only on orderable content types) |
Any field in data | Custom fields defined on the content type (e.g. price, title) |
Manual ordering
If the content type has ordering enabled (configured by your workspace admin),
entries are returned by position:asc by default — reflecting the drag-and-drop
order set in the dashboard. You can override this with an explicit sort parameter.
Field Selection
By default all fields defined on the content type are returned inside data. Use
fields to request only what you need — smaller payloads, faster frontend renders.
curl "https://api.atlas.latellu.com/api/v1/public/entries?type=article&fields=title,slug,cover_image" \
-H "X-API-Key: <your_api_key>"Without fields — all fields returned:
{
"title": "Getting Started with Headless CMS",
"summary": "A beginner-friendly introduction to decoupled content management.",
"cover_image": {
"id": "0190d1a1-0000-7000-8000-000000000001",
"url": "https://cdn.atlas.latellu.com/blog/covers/headless-cms-intro.webp",
"alt": "Illustration of decoupled CMS architecture",
"width": 1200,
"height": 630,
"mime_type": "image/webp"
},
"body": { "type": "doc", "content": [ { "type": "paragraph", "content": [ "..." ] } ] },
"author": {
"id": "0190d1a1-0000-7000-8000-000000000042",
"slug": "arya-santoso",
"data": { "name": "Arya Santoso", "avatar": { "url": "https://cdn.atlas.latellu.com/avatars/arya.jpg" } }
},
"tags": ["headless", "cms", "tutorial"],
"published_at": "2026-05-10T08:00:00Z"
}With ?fields=title,slug,cover_image — only requested fields:
{
"title": "Getting Started with Headless CMS",
"slug": "getting-started-with-headless-cms",
"cover_image": {
"url": "https://cdn.atlas.latellu.com/blog/covers/headless-cms-intro.webp",
"alt": "Illustration of decoupled CMS architecture",
"width": 1200,
"height": 630
}
}Dot notation for relations
When a field is a relation (referencing another content type), the full resolved object is returned by default. Use dot notation to pick specific sub-fields:
curl "https://api.atlas.latellu.com/api/v1/public/entries?type=article&fields=title,slug,author.name,author.avatar" \
-H "X-API-Key: <your_api_key>"{
"title": "Getting Started with Headless CMS",
"slug": "getting-started-with-headless-cms",
"author": {
"name": "Arya Santoso",
"avatar": { "url": "https://cdn.atlas.latellu.com/avatars/arya.jpg" }
}
}Relations are resolved one level deep. author.profile.bio would not work — only
author.bio (one hop).
Rich text variants
Richtext fields are returned as Tiptap JSON by default. Append a suffix to the field name to request a different format:
| Suffix | Format | Use case |
|---|---|---|
body | Tiptap JSON (default) | Custom renderer, Tiptap editor |
body_html | Sanitized HTML | Direct render in React / Vue |
body_text | Plain text | Search snippets, meta descriptions |
curl "https://api.atlas.latellu.com/api/v1/public/entries?type=article&fields=title,slug,body_html" \
-H "X-API-Key: <your_api_key>"{
"title": "Getting Started with Headless CMS",
"slug": "getting-started-with-headless-cms",
"body_html": "<h2>What is a Headless CMS?</h2><p>A headless CMS separates content management from content presentation. Your team writes in a familiar editor, and your frontend fetches the content via API — whether that's a Next.js site, a mobile app, or a digital signage screen.</p>"
}Combining Parameters
All query parameters can be combined freely in a single request:
curl "https://api.atlas.latellu.com/api/v1/public/entries?type=article&locale=id&fields=title,slug,author.name,body_html&sort=published_at:desc&limit=12" \
-H "X-API-Key: <your_api_key>"This returns the 12 most recently published article entries in Indonesian, with
only title, slug, a resolved author.name, and sanitized HTML body.
{
"success": true,
"data": [
{
"slug": "mulai-dengan-headless-cms",
"status": "published",
"data": {
"title": "Memulai dengan Headless CMS",
"author": { "name": "Arya Santoso" },
"body_html": "<h2>Apa itu Headless CMS?</h2><p>Headless CMS memisahkan manajemen konten dari presentasi konten...</p>"
},
"translations": {
"id": {
"data": {
"title": "Memulai dengan Headless CMS",
"body_html": "<h2>Apa itu Headless CMS?</h2><p>Headless CMS memisahkan manajemen konten dari presentasi konten...</p>"
}
}
}
}
],
"meta": {
"total": 38,
"page": 1,
"limit": 12,
"next_cursor": "eyJpZCI6IDEyfQ"
}
}Next Steps
- Localization — the
localeparameter in depth: fallback behaviour and thedata/translationsresponse shape. - Entries — full entry structure and reading the
dataobject. - API Reference — the complete parameter list for
GET /public/entries.