# Pagination

Our API supports **cursor-based pagination**, inspired by the JSON:API pagination specification. This approach is efficient and scalable for large datasets.

Each paginated response contains a `data` array and `links` for navigating through pages.

***

### 📦 Response Format

```json
{
  "data": <...>,
  "links": {
    "self": "https://api.example.com/resources?cursor=abcdef",
    "next": "https://api.example.com/resources?cursor=ghijkl"
  }
}
```

#### 🔗 `links.self`

URL for the current page. Safe to cache or re-fetch.

#### 🔗 `links.next`

URL for the next page. If absent or `null`, you've reached the end.

#### 🧭 `cursor`

A **cursor** is an opaque string provided by the server to mark a position in the dataset. You must use it exactly as returned.\
**Do not attempt to decode or generate it manually.**

***

### 🔐 Authentication Required

Before making any paginated requests, make sure your client is authenticated properly.

➡️ **See the Authentication section for details on required headers.**

***

{% tabs %}
{% tab title="cURL" %}

#### 🟡 cURL

```bash
curl "https://api.example.com/resources" \
  -H "X-API-KEY: your-api-key" \
  -H "X-API-SECRET: your-api-secret"
```

Then use the `links.next` URL:

```bash
curl "https://api.example.com/resources?page[cursor]=ghijkl" \
  -H "X-API-KEY: your-api-key" \
  -H "X-API-SECRET: your-api-secret"
```

{% endtab %}

{% tab title="Typescript" %}

#### 🟦 TypeScript (e.g. in a React or Node.js app)

```ts
async function fetchPage(url: string) {
  const res = await fetch(url, {
    headers: {
      'X-API-KEY': 'your-api-key',
      'X-API-SECRET': 'your-api-secret',
    },
  });
  const body = await res.json();

  console.log('Current page:', body.data);

  if (body.links?.next) {
    console.log('Next page URL:', body.links.next);
    // await fetchPage(body.links.next);
  }
}

fetchPage('https://api.example.com/resources');
```

{% endtab %}

{% tab title="Java" %}

#### 🟠 Java (with `HttpClient` and Jackson)

```java
HttpClient client = HttpClient.newHttpClient();
ObjectMapper mapper = new ObjectMapper();

void fetchPage(String url) throws IOException, InterruptedException {
    HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create(url))
        .header("X-API-KEY", "your-api-key")
        .header("X-API-SECRET", "your-api-secret")
        .GET()
        .build();

    HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
    JsonNode json = mapper.readTree(response.body());

    System.out.println("Current page: " + json.get("data"));

    JsonNode next = json.path("links").path("next");
    if (!next.isMissingNode()) {
        System.out.println("Next page URL: " + next.asText());
        // fetchPage(next.asText());
    }
}
```

{% endtab %}

{% tab title=".NET" %}

#### 🟦 .NET (C# with `HttpClient`)

```csharp
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;

async Task FetchPageAsync(string url)
{
    var client = new HttpClient();
    client.DefaultRequestHeaders.Add("X-API-KEY", "your-api-key");
    client.DefaultRequestHeaders.Add("X-API-SECRET", "your-api-secret");

    var response = await client.GetStringAsync(url);

    using var doc = JsonDocument.Parse(response);
    var root = doc.RootElement;

    Console.WriteLine("Current page: " + root.GetProperty("data"));

    if (root.TryGetProperty("links", out var links) &&
        links.TryGetProperty("next", out var next))
    {
        Console.WriteLine("Next page URL: " + next.GetString());
        // await FetchPageAsync(next.GetString());
    }
}
```

{% endtab %}
{% endtabs %}

### ✅ Best Practices

* **Always use the `links.next` URL** for pagination.
* **Never parse or construct cursors manually.**
* **Stop paginating** when `links.next` is missing or `null`.
* **Ensure authentication headers are included** — see the Authentication section.
