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

# Pagination

> Learn how to use pagination in the Notra API.

Notra uses offset-based pagination for list endpoints. Each response includes a
`pagination` object so you can build paging controls in your UI.

## How Pagination Works

When you request a list of posts, the response includes pagination metadata so
you can:

* Control how many items each request returns
* Move between pages
* Know how many total items exist
* Build clear pagination controls

## Query Parameters

<ParamField query="limit" type="integer" default="10">
  The maximum number of items to return per page. **Range:** 1-100 items per
  page
</ParamField>

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

## 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 requested per page (matches your query parameter).
    </ResponseField>

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

    <ResponseField name="nextPage" type="integer | null">
      The next page number, or `null` if you're on the last page.
    </ResponseField>

    <ResponseField name="previousPage" type="integer | null">
      The previous page number, or `null` if you're on the first page.
    </ResponseField>

    <ResponseField name="totalPages" type="integer">
      The total number of pages available.
    </ResponseField>

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

## Request Examples

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

    <CodeGroup>
      ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}}
      curl -H "Authorization: Bearer YOUR_API_KEY" \
        "https://api.usenotra.com/v1/posts"
      ```

      ```typescript SDK theme={"theme":{"light":"github-light","dark":"github-dark"}}
      const result = await notra.content.listPosts();
      ```
    </CodeGroup>
  </Tab>

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

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

      ```typescript SDK theme={"theme":{"light":"github-light","dark":"github-dark"}}
      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={"theme":{"light":"github-light","dark":"github-dark"}}
      curl -H "Authorization: Bearer YOUR_API_KEY" \
        "https://api.usenotra.com/v1/posts?page=2&limit=5"
      ```

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

## Building Pagination Controls

Use the pagination object to build navigation controls:

<CodeGroup>
  ```typescript fetch theme={"theme":{"light":"github-light","dark":"github-dark"}}
  const response = await fetch(
    "https://api.usenotra.com/v1/posts?page=1&limit=10",
    {
      headers: {
        Authorization: `Bearer ${process.env.NOTRA_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={"theme":{"light":"github-light","dark":"github-dark"}}
  import type { Pagination } from "@usenotra/sdk/models/operations";

  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={"theme":{"light":"github-light","dark":"github-dark"}}
  {
    "posts": [
      {
        "id": "YOUR_POST_ID",
        "title": "February 12-19, 2026 Release",
        "content": "<p>This week brought meaningful improvements to organization management, scheduling, and mobile responsiveness.</p><h2>Highlights</h2><h3>Organization membership unification with server validation</h3><p>Leave and delete operations now use consistent, validated server logic with atomic operations to prevent race conditions on concurrent requests.</p>",
        "markdown": "This week brought meaningful improvements to organization management, scheduling, and mobile responsiveness.\n\n## Highlights\n\n### Organization membership unification with server validation\nLeave and delete operations now use consistent, validated server logic with atomic operations to prevent race conditions on concurrent requests.",
        "contentType": "changelog",
        "sourceMetadata": null,
        "status": "published",
        "createdAt": "2026-02-19T11:32:54.082Z",
        "updatedAt": "2026-02-19T11:32:54.082Z"
      }
    ],
    "pagination": {
      "limit": 10,
      "currentPage": 1,
      "nextPage": null,
      "previousPage": null,
      "totalPages": 1,
      "totalItems": 1
    }
  }
  ```
</ResponseExample>

<ResponseExample>
  ```json Empty Result Set theme={"theme":{"light":"github-light","dark":"github-dark"}}
  {
    "posts": [],
    "pagination": {
      "limit": 10,
      "currentPage": 1,
      "nextPage": null,
      "previousPage": null,
      "totalPages": 1,
      "totalItems": 0
    }
  }
  ```
</ResponseExample>

<ResponseExample>
  ```json Error - Invalid Page theme={"theme":{"light":"github-light","dark":"github-dark"}}
  {
    "error": "Invalid page number",
    "details": {
      "message": "Page 2 does not exist.",
      "totalPages": 1,
      "requestedPage": 2
    }
  }
  ```
</ResponseExample>

## Best Practices

* Use a distinct cache key for each combination of `page`, `limit`, `sort`, `status`, and `contentType`.
* 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-20 items), especially for mobile
  clients.
</Tip>

<Tip>
  **Empty states:** Always handle `posts: []` 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>

<Tip>
  **UI state:** `pagination` already contains everything you need for page state:
  current page, total pages, next, and previous.
</Tip>

## Error Handling

<AccordionGroup>
  <Accordion title="Invalid page number">
    If you request a page that does not exist, the API returns an error response:

    ```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
    {
      "error": "Invalid page number",
      "details": {
        "message": "Page 2 does not exist.",
        "totalPages": 1,
        "requestedPage": 2
      }
    }
    ```

    Check for an `error` field before reading pagination values.
  </Accordion>

  <Accordion title="Invalid limit value">
    * Values below `1` default to `1`
    * Values above `100` are capped at `100`
    * Non-numeric values default to `10`
  </Accordion>

  <Accordion title="Empty datasets">
    When there are no items, `totalPages` is `0`, `totalItems` is `0`, and
    `posts` is an empty array.
  </Accordion>
</AccordionGroup>
