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

# Pagination

> Cursor-based pagination for Just Flow It API list endpoints.

All list endpoints in the Just Flow It API (such as `GET /v1/diagrams` and `GET /v1/folders`) use **cursor-based pagination**. Instead of page numbers, you fetch results in batches and follow an opaque `next_cursor` to walk forward through the collection.

<Note>
  Results are always ordered **newest first** (`created_at` descending).
</Note>

## Query parameters

<ParamField query="limit" type="integer" default="20">
  How many items to return per page. Must be between **1 and 100**. Defaults to `20`.
</ParamField>

<ParamField query="cursor" type="string">
  An opaque pagination token. Omit it on the first request. To fetch the next page, pass the `next_cursor` value returned by the previous response.
</ParamField>

## The list envelope

Every paginated response wraps the results in a consistent envelope:

<ResponseField name="data" type="array">
  The array of resource objects for this page. Each item carries its own `object` discriminator (for example `"diagram"` or `"folder"`).
</ResponseField>

<ResponseField name="has_more" type="boolean">
  `true` if there are more items beyond this page, `false` if this is the last page.
</ResponseField>

<ResponseField name="next_cursor" type="string | null">
  The cursor to pass as `cursor` on your next request to retrieve the following page. It is `null` when `has_more` is `false`.
</ResponseField>

### Example response

```json theme={null}
{
  "data": [
    {
      "object": "diagram",
      "id": "3f9c2b1a-7d4e-4a2c-9b6f-1e8d5c0a2f31",
      "name": "Onboarding flow",
      "folder_id": null,
      "organization_id": null,
      "created_by": "a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
      "created_at": "2026-06-07T14:21:09Z",
      "updated_at": "2026-06-07T14:21:09Z"
    }
  ],
  "has_more": true,
  "next_cursor": "ZXlKamNtVmhkR1ZrWDJGMElqb2lNakF5Tmkwd05pMHdOMVF4TkRveU1Ub3dPVm9p"
}
```

## Fetching a single page

<CodeGroup>
  ```bash cURL theme={null}
  curl https://justflow.it/api/v1/diagrams?limit=20 \
    -H "Authorization: Bearer jfi_sk_live_..."
  ```

  ```javascript JavaScript (fetch) theme={null}
  const res = await fetch("https://justflow.it/api/v1/diagrams?limit=20", {
    headers: {
      Authorization: "Bearer jfi_sk_live_...",
    },
  });

  const page = await res.json();
  console.log(page.data);        // array of diagrams
  console.log(page.has_more);    // boolean
  console.log(page.next_cursor); // cursor for the next page (or null)
  ```

  ```python Python (requests) theme={null}
  import requests

  res = requests.get(
      "https://justflow.it/api/v1/diagrams",
      headers={"Authorization": "Bearer jfi_sk_live_..."},
      params={"limit": 20},
  )

  page = res.json()
  print(page["data"])        # list of diagrams
  print(page["has_more"])    # bool
  print(page["next_cursor"]) # cursor for the next page (or None)
  ```
</CodeGroup>

## Looping through every page

To retrieve a full collection, keep requesting while `has_more` is `true`, passing the previous `next_cursor` as the next `cursor`.

<Steps>
  <Step title="Request the first page">
    Call the list endpoint with your desired `limit` and **no** `cursor`.
  </Step>

  <Step title="Process the data">
    Handle the items in `data`.
  </Step>

  <Step title="Check has_more">
    If `has_more` is `false`, you've reached the end. Otherwise continue.
  </Step>

  <Step title="Follow next_cursor">
    Request the next page with `cursor` set to the previous response's `next_cursor`, and repeat from step 2.
  </Step>
</Steps>

<CodeGroup>
  ```javascript JavaScript (fetch) theme={null}
  async function listAllDiagrams() {
    const all = [];
    let cursor = null;

    do {
      const url = new URL("https://justflow.it/api/v1/diagrams");
      url.searchParams.set("limit", "100");
      if (cursor) url.searchParams.set("cursor", cursor);

      const res = await fetch(url, {
        headers: { Authorization: "Bearer jfi_sk_live_..." },
      });

      const page = await res.json();
      all.push(...page.data);
      cursor = page.next_cursor;

      // Loop while there are more pages
      if (!page.has_more) break;
    } while (cursor);

    return all;
  }
  ```

  ```python Python (requests) theme={null}
  import requests

  def list_all_diagrams():
      items = []
      cursor = None

      while True:
          params = {"limit": 100}
          if cursor:
              params["cursor"] = cursor

          res = requests.get(
              "https://justflow.it/api/v1/diagrams",
              headers={"Authorization": "Bearer jfi_sk_live_..."},
              params=params,
          )

          page = res.json()
          items.extend(page["data"])
          cursor = page["next_cursor"]

          if not page["has_more"]:
              break

      return items
  ```
</CodeGroup>

<Tip>
  Use the largest `limit` (`100`) when bulk-syncing to minimize the number of round trips. Keep your loop driven by `has_more` rather than by an empty `data` array.
</Tip>

## Invalid or expired cursors

Cursors are opaque tokens — never construct, modify, or persist them across long periods. If you pass a malformed or expired `cursor`, the API responds with **400** and an `invalid_cursor` error.

<Warning>
  Always start a fresh traversal from the first page (no `cursor`) rather than reusing a stale `next_cursor` from an earlier run.
</Warning>

```json 400 Bad Request theme={null}
{
  "error": {
    "type": "invalid_request_error",
    "code": "invalid_cursor",
    "message": "The supplied cursor is invalid or has expired.",
    "param": "cursor"
  },
  "request_id": "req_8Kt2yq4vN1aBc3De"
}
```

<Accordion title="Why cursor-based instead of page numbers?">
  Cursor pagination stays correct even when items are created or deleted between requests, since each cursor encodes a stable position in the `created_at desc` ordering. Offset/page-number pagination can skip or duplicate items when the underlying collection changes mid-traversal.
</Accordion>
