# Pagination and rate limits (/developers/pagination-and-rate-limits)

Every v1 list returns one page at a time. Every request counts toward a few rate limits; when you go
over one, Fianto answers 429 and tells you how long to wait.

## Pagination [#pagination]

A list answers with `items` and `next_cursor`:

```text
{ "items": [ … ], "next_cursor": … }
```

| Parameter | Rules                                                                   |
| --------- | ----------------------------------------------------------------------- |
| `limit`   | 1 to 100. Default 20                                                    |
| `cursor`  | The `next_cursor` of the previous page. Leave it out for the first page |

* Orders, payments, subscriptions, products and prices use numeric cursors. A full last page still
  returns a cursor, and the page after it is empty. Stop on an empty page, or on `null`.
* Events use `evt_…` ids as cursors, and return `next_cursor: null` when there are no more.

```bash
curl "https://api.fianto.xyz/v1/orders?limit=50" \
  --user "$FIANTO_APP_ID:$FIANTO_APP_SECRET"

# the next page
curl "https://api.fianto.xyz/v1/orders?limit=50&cursor=NEXT_CURSOR" \
  --user "$FIANTO_APP_ID:$FIANTO_APP_SECRET"
```

### In the SDK [#in-the-sdk]

A `list()` call returns a `PagePromise`. `await` it for one page, or `for await` it to walk every item.
The SDK fetches the next page only as you read, and stops on `next_cursor: null` or an empty page.

```ts
import { Fianto } from '@fianto/sdk';

const fianto = new Fianto();

// One page.
const page = await fianto.payments.list({ limit: 50 });
console.log(page.items.length, page.next_cursor);

// Every item, page by page.
for await (const payment of fianto.payments.list({ limit: 100 })) {
  console.log(payment.id, payment.status);
}
```

## Rate limits [#rate-limits]

| Limit                               | Scope                                                    |
| ----------------------------------- | -------------------------------------------------------- |
| 300 requests per minute             | Per IP address                                           |
| 120 writes and 600 reads per minute | Per application                                          |
| 300 writes per minute               | Per merchant, across all its applications                |
| 10 test events per hour             | Per application, shared with the dashboard's test button |

Over a limit, Fianto answers 429 with a `Retry-After` header and the code `rate_limited`: wait that
many seconds, then retry.

The SDK retries a 429 on its own, waiting for `Retry-After`. When it gives up, after its retries or at
once when `Retry-After` asks for more than 10 seconds, it throws a `RateLimitError` with the wait in
`retryAfterSeconds`:

```ts
import { Fianto, RateLimitError } from '@fianto/sdk';

const fianto = new Fianto();

export async function recentOrders() {
  try {
    return await fianto.orders.list({ limit: 20 });
  } catch (err) {
    if (err instanceof RateLimitError) {
      console.warn(`Rate limited; retry in ${err.retryAfterSeconds ?? 'a few'} seconds`);
    }
    throw err;
  }
}
```

## See also [#see-also]

- [Errors](/developers/errors): The error body and what the SDK retries.

- [API overview](/developers/overview): The 20 endpoints and what each application sees.

- [Testing](/developers/testing): Send test events within the hourly budget.