> ## Documentation Index
> Fetch the complete documentation index at: https://docs.kolaria.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Pagination

> Page through posts, feedback and GEO scan history with page and limit.

Kolaria uses page-based pagination on the list endpoints that can grow without bound. Each of them takes `page` and `limit` query parameters and returns a `pagination` object you can build paging controls from.

## Paginated endpoints

| Endpoint | Default `limit` | Max `limit` | Items key |
| - | - | - | - |
| `GET /v1/posts` | 10 | 100 | `posts` |
| `GET /v1/feedback` | 25 | 100 | `feedback` |
| `GET /v1/projects/{projectId}/geo/scans` | 20 | 100 | `scans` |

Other list endpoints are not paginated. Brand identities, integrations, schedules, event triggers, chats, skills, GEO projects, prompts, sequences, competitors and briefs return the full list. GEO traffic endpoints (`traffic/log`, `traffic/journeys`, `traffic/pages`) and `GET /v2/agent-chats` accept a `limit` only and return the most recent items. Visibility and traffic reads are windowed by `days` or `from`/`to` instead.

## Query Parameters

<ParamField query="limit" type="integer" default="10">
  Items per page. **Range:** 1 to 100. The default depends on the endpoint (see the table above).
</ParamField>

<ParamField query="page" type="integer" default="1">
  The page number to retrieve. Pages start at 1.
</ParamField>

Out-of-range values are rejected rather than clamped: `limit=500` or `page=0` returns `400` with an `error` such as `limit: Too big: expected number to be <=100`.

`GET /v1/posts` also accepts `sort` (`asc` or `desc` by creation date, default `desc`) and the comma-separated filters `status`, `contentType` and `brandIdentityId`. `GET /v1/feedback` accepts `status`, `kind` and `projectId`.

## Pagination Response

Every paginated response includes a `pagination` object:

<ResponseField name="pagination" type="object">
  Metadata about the current page and navigation options.

  <Expandable title="Pagination properties">
    <ResponseField name="limit" type="integer">
      The number of items per page (matches your query parameter or the endpoint default).
    </ResponseField>

    <ResponseField name="currentPage" type="integer">
      The page number being returned.
    </ResponseField>

    <ResponseField name="nextPage" type="integer | null">
      The next page number, or `null` when there is no next page.
    </ResponseField>

    <ResponseField name="previousPage" type="integer | null">
      The previous page number, or `null` on page 1.
    </ResponseField>

    <ResponseField name="totalPages" type="integer">
      The total number of pages. Always at least `1`, even when there are no items.
    </ResponseField>

    <ResponseField name="totalItems" type="integer">
      The total number of items across all pages after filters are applied.
    </ResponseField>
  </Expandable>
</ResponseField>

## Request Examples

<Tabs>
  <Tab title="Default Pagination">
    Fetch the first page with the default limit (`10`):

    <CodeGroup>
      ```bash cURL theme={null}
      curl -H "Authorization: Bearer YOUR_API_KEY" \
        "https://api.kolaria.com/v1/posts"
      ```

      ```typescript SDK theme={null}
      const result = await notra.content.listPosts();
      ```
    </CodeGroup>
  </Tab>

  <Tab title="Custom Limit">
    Fetch 5 items per page:

    <CodeGroup>
      ```bash cURL theme={null}
      curl -H "Authorization: Bearer YOUR_API_KEY" \
        "https://api.kolaria.com/v1/posts?limit=5"
      ```

      ```typescript SDK theme={null}
      const result = await notra.content.listPosts({
        limit: 5,
      });
      ```
    </CodeGroup>
  </Tab>

  <Tab title="Specific Page">
    Fetch page 2 with 5 items per page:

    <CodeGroup>
      ```bash cURL theme={null}
      curl -H "Authorization: Bearer YOUR_API_KEY" \
        "https://api.kolaria.com/v1/posts?page=2&limit=5"
      ```

      ```typescript SDK theme={null}
      const result = await notra.content.listPosts({
        page: 2,
        limit: 5,
      });
      ```
    </CodeGroup>
  </Tab>

  <Tab title="Feedback">
    Page through new feedback, 50 per page:

    ```bash cURL theme={null}
    curl -H "Authorization: Bearer YOUR_API_KEY" \
      "https://api.kolaria.com/v1/feedback?status=new&limit=50&page=1"
    ```
  </Tab>
</Tabs>

## Building Pagination Controls

Use the pagination object to build navigation controls:

<CodeGroup>
  ```typescript fetch theme={null}
  const response = await fetch(
    "https://api.kolaria.com/v1/posts?page=1&limit=10",
    {
      headers: {
        Authorization: `Bearer ${process.env.KOLARIA_API_KEY}`,
      },
    }
  );

  const data = await response.json();
  const { pagination } = data;

  const canGoBack = pagination.previousPage !== null;
  const canGoForward = pagination.nextPage !== null;
  const pageInfo = `Page ${pagination.currentPage} of ${pagination.totalPages}`;
  const itemCount = `${pagination.totalItems} total items`;
  ```

  ```typescript SDK theme={null}
  const result = await notra.content.listPosts({
    page: 1,
    limit: 10,
  });

  const { pagination } = result;

  const canGoBack = pagination.previousPage !== null;
  const canGoForward = pagination.nextPage !== null;
  const pageInfo = `Page ${pagination.currentPage} of ${pagination.totalPages}`;
  const itemCount = `${pagination.totalItems} total items`;
  ```
</CodeGroup>

<ResponseExample>
  ```json Success Response theme={null}
  {
    "posts": [
      {
        "id": "post_abc",
        "title": "August 24-31, 2026 Release",
        "slug": "august-24-31-2026-release",
        "content": "<p>This week brought meaningful improvements to scheduling and mobile responsiveness.</p>",
        "htmlUrl": null,
        "markdown": "This week brought meaningful improvements to scheduling and mobile responsiveness.",
        "rawHtml": null,
        "recommendations": null,
        "contentType": "changelog",
        "sourceMetadata": null,
        "status": "published",
        "createdAt": "2026-08-31T11:32:54.082Z",
        "updatedAt": "2026-08-31T11:32:54.082Z"
      }
    ],
    "pagination": {
      "limit": 10,
      "currentPage": 1,
      "nextPage": null,
      "previousPage": null,
      "totalPages": 1,
      "totalItems": 1
    },
    "organization": {
      "id": "org_123",
      "slug": "acme",
      "name": "Acme",
      "logo": null
    }
  }
  ```
</ResponseExample>

<ResponseExample>
  ```json Empty Result Set theme={null}
  {
    "posts": [],
    "pagination": {
      "limit": 10,
      "currentPage": 1,
      "nextPage": null,
      "previousPage": null,
      "totalPages": 1,
      "totalItems": 0
    },
    "organization": {
      "id": "org_123",
      "slug": "acme",
      "name": "Acme",
      "logo": null
    }
  }
  ```
</ResponseExample>

## Best Practices

* Use a distinct cache key for each combination of `page`, `limit`, `sort` and filters.
* Invalidate paginated list caches after post updates, deletes, and completed generation jobs.
* For a fuller strategy, see [Caching](/api/caching).

<Tip>
  **Performance:** Use smaller page sizes (5 to 20 items), especially for mobile
  clients.
</Tip>

<Tip>
  **Empty states:** Always handle an empty items array in your UI.
</Tip>

<Note>
  **Navigation:** Use `nextPage` and `previousPage` to enable or disable buttons.
  These values are `null` when movement is not possible.
</Note>

## Edge Cases

<AccordionGroup>
  <Accordion title="Page past the end">
    Requesting a page beyond `totalPages` is not an error. The API returns `200` with an empty items array, `nextPage: null` and `previousPage` set to the page before the one you asked for. Stop paging when `nextPage` is `null` rather than guessing page numbers.
  </Accordion>

  <Accordion title="Invalid limit or page">
    Values outside the allowed range are rejected with `400` and an `error` message naming the parameter. Non-numeric values fail the same way. Nothing is silently clamped.
  </Accordion>

  <Accordion title="Empty datasets">
    When there are no items, `totalItems` is `0`, `totalPages` is `1`, the items array is empty, and both `nextPage` and `previousPage` are `null`.
  </Accordion>

  <Accordion title="Filters and totals">
    `totalItems` and `totalPages` reflect the filtered result set. On `GET /v1/posts`, omitting `status` returns only `published` posts; pass `status=draft,published` to count drafts too.
  </Accordion>
</AccordionGroup>
