> For the complete documentation index, see [llms.txt](https://developers.mobile-text-alerts.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://developers.mobile-text-alerts.com/api-basics/pagination-of-large-results.md).

# Pagination of Large Results

Many of the Mobile Text Alerts API endpoints return **lists** of records — [subscribers](/api-reference/subscribers.md#get-subscribers), [deliveries](/api-reference/deliveries.md#get-deliveries), [webhooks](/api-reference/webhooks.md#list-webhooks), [groups](/api-reference/groups.md#get-groups), [dedicated numbers](/api-reference/dedicated-numbers.md#get-dedicated-numbers), [templates](/api-reference/templates.md#get-templates), and more. To keep responses fast and predictable, these endpoints do not return every matching record at once. Instead, they return one **page** of results at a time, along with the metadata you need to request the next page.

All endpoints that return a list of results accept the following pagination parameters: `page`, `pageSize`, `sortBy`, and `sortDirection`. They also return the same structured paginated response. Once you learn the structure, it works the same way across all list-returning endpoints.

{% hint style="info" %}
**Why pagination matters at scale**

A `GET /subscribers` call that returns 10,000+ subscribers would be slow and unwieldy as a single response. Pagination lets you pull those records in manageable batches. This lets you show progress, retry a single failed page, and stay within [rate limits](/api-basics/rate-limits.md) instead of timing out on one giant request.
{% endhint %}

## Structure of a paginated response

All paginated endpoints wrap their results in a `data` object containing the page of records plus pagination metadata.

#### **Example paginated response structure:**

```json
{
  "data": {
    "rows": [
      { "id": 109021633, "firstName": "Alex", "lastName": "Johnson" },
      { "id": 109021634, "firstName": "Sam", "lastName": "Lee" }
    ],
    "page": 0,
    "pageSize": 25,
    "total": 1340
  }
}
```

<table><thead><tr><th width="130">Field</th><th width="129.5">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>rows</code></td><td><code>array</code></td><td>The array of records, the structure depends on the endpoint that is called (subscribers, deliveries, etc.).</td></tr><tr><td><code>page</code></td><td><code>number</code></td><td><p>The current page number.</p><p><em><strong>Note:</strong> Pages are 0-indexed.</em></p></td></tr><tr><td><code>pageSize</code></td><td><code>number</code></td><td><p>The number of records returned per page.</p><p><em>If not specified, a <strong>default page size of</strong><strong> </strong><strong><code>25</code></strong><strong> </strong><strong>is used.</strong> Maximum value: <code>1000</code>.</em></p></td></tr><tr><td><code>total</code></td><td><code>number</code></td><td>The total number of records matching your query across <em>all</em> pages — not just the current page. Use this to calculate how many pages you need to fetch.</td></tr></tbody></table>

{% hint style="warning" %}
**0-Indexing**

Because the first page is `page: 0`, the records on page `p` cover positions `p * pageSize` through `(p + 1) * pageSize - 1`. This matters for your loop condition ([see below](#how-to-fetch-all-pages-in-a-loop)). So requesting `page: 1` returns the *second* page.
{% endhint %}

## Request a specific page

To request a specific page of a paginated response, pass the desired `page` and `pageSize` as query parameters on any list endpoint.

#### Example

To retrieve the **third** page of the [Subscribers](/api-reference/subscribers.md#get-subscribers) list (remember: 0-indexed) at 50 records per page:

```bash
curl --location 'https://api.mobile-text-alerts.com/v3/subscribers?page=2&pageSize=50' \
  --header 'Authorization: Bearer <APIKey>'
```

This returns records 100–149 (positions `2 * 50` through `3 * 50 - 1`).

## Sort page results

The two parameters below control the order in which records are returned. Note that sorting is applied **before** pagination, so the order is stable across pages.

<table><thead><tr><th width="176.5">Parameter</th><th width="189.5">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>sortBy</code></td><td><code>string</code></td><td>The field to sort by. The set of sortable fields varies by endpoint (for example, the <code>/deliveries</code> endpoint sorts by <code>date</code>).</td></tr><tr><td><code>sortDirection</code></td><td><code>ASC</code> | <code>DESC</code></td><td>The sort direction. Indicate <code>ASC</code> for ascending or <code>DESC</code> for descending.</td></tr></tbody></table>

How a field sorts depends on its underlying type:

* **text fields** (such as `firstName` or `email`) sort alphabetically
* **numeric and date fields** (such as `number` or `date`) sort by numeric/chronological value

Check the individual endpoint's reference page for the fields it accepts in `sortBy`.

#### Example

To retrieve the list of [Deliveries](/api-reference/deliveries.md#get-deliveries) sorted by date in descending order:

```bash
curl --location 'https://api.mobile-text-alerts.com/v3/deliveries?sortBy=date&sortDirection=DESC' \
  --header 'Authorization: Bearer <APIKey>'
```

## How to fetch all pages

This pattern is the same for all list endpoint results: start at `page: 0`, read `total` from the first response, then keep incrementing `page` until you have fetched every record.

#### Example

The example below pages through [Subscribers](/api-reference/subscribers.md#get-subscribers), but you can change the path to any list endpoint.

Pages are 0-indexed, so there are records to retrieve as long as `page * pageSize < total` — so you stop once `page * pageSize >= total`.

{% tabs %}
{% tab title="Node.js" %}
**Requirements:** Node.js `18+` (native `fetch`) and an `MTA_API_KEY` environment variable.

```js
const BASE_URL = "https://api.mobile-text-alerts.com/v3";
const API_KEY = process.env.MTA_API_KEY;

async function fetchAllSubscribers({
  pageSize = 100,
  query,
  sortBy,
  sortDirection,
} = {}) {
  const all = [];
  let page = 0;
  let total = Infinity; // unknown until the first response

  while (page * pageSize < total) {
    const params = new URLSearchParams({ page, pageSize });
    if (query) params.set("query", query);
    if (sortBy) params.set("sortBy", sortBy);
    if (sortDirection) params.set("sortDirection", sortDirection);

    const res = await fetch(`${BASE_URL}/subscribers?${params}`, {
      headers: { Authorization: `Bearer ${API_KEY}` },
    });
    if (!res.ok) throw new Error(`Request failed: ${res.status}`);

    const { data } = await res.json();
    total = data.total;       // cache total from the first response onward
    all.push(...data.rows);

    if (data.rows.length === 0) break; // safety: stop on an empty page
    page += 1;

    // Be gentle on rate limits when fetching many pages
    await new Promise((r) => setTimeout(r, 250));
  }

  return all;
}

fetchAllSubscribers({ pageSize: 100 })
  .then((rows) => console.log(`Fetched ${rows.length} subscribers`))
  .catch((err) => console.error(err));
```

{% endtab %}

{% tab title="Python" %}
**Requirements:** `pip install requests` and an `MTA_API_KEY` environment variable.

```python
import os
import time
import requests

BASE_URL = "https://api.mobile-text-alerts.com/v3"
API_KEY = os.getenv("MTA_API_KEY")


def fetch_all_subscribers(page_size=100, query=None,
                          sort_by=None, sort_direction=None):
    rows = []
    page = 0
    total = None  # unknown until the first response

    while total is None or page * page_size < total:
        params = {"page": page, "pageSize": page_size}
        if query:
            params["query"] = query
        if sort_by:
            params["sortBy"] = sort_by
        if sort_direction:
            params["sortDirection"] = sort_direction

        resp = requests.get(
            f"{BASE_URL}/subscribers",
            params=params,
            headers={"Authorization": f"Bearer {API_KEY}"},
        )
        resp.raise_for_status()

        data = resp.json()["data"]
        total = data["total"]      # cache total from the first response onward
        rows.extend(data["rows"])

        if not data["rows"]:        # safety: stop on an empty page
            break
        page += 1

        time.sleep(0.25)            # be gentle on rate limits

    return rows


if __name__ == "__main__":
    subscribers = fetch_all_subscribers(page_size=100)
    print(f"Fetched {len(subscribers)} subscribers")
```

{% endtab %}
{% endtabs %}

## Filter results

You can narrow a list before paginating it, so you only page through the records you actually need.

* **`query`** — A free-text search string.
  * For example, on [`GET /subscribers`](/api-reference/subscribers.md#get-subscribers), the `query` value matches against `firstName`, `lastName`, `number`, and `email` (and custom subscriber fields), returning any subscriber where it appears.
* **`filters`** — A structured object for endpoint-specific filtering. The available filter keys vary by endpoint.
  * For example, [`GET /deliveries`](/api-reference/deliveries.md#get-deliveries) accepts `filters[status]`, `filters[type]`, `filters[startDate]`, and `filters[endDate]`. Pass them as bracketed query parameters (`?filters[status]=delivered`).

Filter options differ by endpoint. See each [endpoint's reference](/api-reference/analytics.md) page for the full list. Filters and sorting can be combined with pagination in the same request.

## Paginated list best practices

* **Use reasonable page sizes** — Typically, use 50–100 records per page. This strikes a good balance between fewer requests and faster response handling. The maximum is `1000`, but larger page sizes increase response size and latency per request.
* **Respect rate limits** — Most endpoints allow [30 requests per minute per IP](/api-basics/rate-limits.md), and [`GET /subscribers`](/api-reference/subscribers.md#get-subscribers) is further limited to **15 requests every 15 seconds**. When fetching many pages, add a short delay between requests and watch the `X-RateLimit-Remaining` and `Retry-After` response headers to avoid a `429` response.
* **Cache `total` from the first response** — Read `total` once on the first page and reuse it to calculate the number of pages and report progress (for example, `page * pageSize / total`) rather than re-deriving it on every request.
* **Use bulk endpoints for very large writes** — Pagination is for *reading*. If you need to add or remove large numbers of records, use the bulk endpoints where available, such as [Bulk Create/Update Subscribers](/tutorials/manage-subscribers/bulk-create-update-subscribers.md) (`POST`/`PATCH /subscribers/bulk`) or [Bulk Add/Delete group subscribers](/tutorials/manage-subscribers/group-subscribers/groups-bulk-add-delete-subscribers.md). These accept up to 1,000 records per request and are far more efficient than per-record calls.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://developers.mobile-text-alerts.com/api-basics/pagination-of-large-results.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
