Atlas CMS

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

ParameterTypeDefaultDescription
typestringRequired for entries. Content type slug (e.g. article, product).
localestringworkspace defaultReturn field values for this locale. Falls back to the workspace default locale when no translation exists.
fieldsstringall fieldsComma-separated field names to include in data. Supports dot notation for relations.
pageinteger1Page number (offset pagination). Ignored when cursor is provided. Maximum: 1000.
limitinteger10Results per page. Maximum: 100.
sortstringcreated_at:descSort field and direction separated by a colon (e.g. published_at:desc).
cursorstringOpaque cursor from meta.next_cursor. When provided, page is ignored.

The Response Envelope

Every list response wraps results in a consistent envelope:

GET /public/entries?type=article
{
  "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 fieldTypeDescription
totalintegerTotal entries matching the query. Returns -1 in cursor mode (count is skipped for performance).
pageintegerCurrent page (offset mode only).
limitintegerNumber of entries returned in this response.
next_cursorstring | nullPass 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
curl "https://api.atlas.latellu.com/api/v1/public/entries?type=article&page=2&limit=20" \
  -H "X-API-Key: <your_api_key>"
Response
{
  "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
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>"
Response — page 1
{
  "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
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

load-more.js
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

OffsetCursor
Use caseNumbered pages, admin listsFeeds, infinite scroll
meta.total✓ Available— Not available
Performance on large datasetsDegrades at high page numbersConsistent
Can jump to arbitrary page✓ Yes (?page=5)✗ Must walk forward
Maximum depthPage 1,000Unlimited

Sorting

Use the sort parameter with field:direction format. Direction is either asc (ascending) or desc (descending).

cURL — newest first (default)
curl "https://api.atlas.latellu.com/api/v1/public/entries?type=article&sort=created_at:desc" \
  -H "X-API-Key: <your_api_key>"
cURL — sort by custom field
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:

FieldDescription
created_atEntry creation date (default)
published_atDate the entry was published
positionManual order set in the dashboard (only on orderable content types)
Any field in dataCustom 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
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:

Response — data (all fields)
{
  "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:

Response — data (?fields=title,slug,cover_image)
{
  "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
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>"
Response — data (author fields only)
{
  "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:

SuffixFormatUse case
bodyTiptap JSON (default)Custom renderer, Tiptap editor
body_htmlSanitized HTMLDirect render in React / Vue
body_textPlain textSearch snippets, meta descriptions
cURL
curl "https://api.atlas.latellu.com/api/v1/public/entries?type=article&fields=title,slug,body_html" \
  -H "X-API-Key: <your_api_key>"
Response — data (?fields=title,slug,body_html)
{
  "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
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.

Response
{
  "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 locale parameter in depth: fallback behaviour and the data/translations response shape.
  • Entries — full entry structure and reading the data object.
  • API Reference — the complete parameter list for GET /public/entries.

On this page