Docs
Pagination
How list endpoints return items in pages, how to fetch the next page with a cursor, and how pages are priced.
Some endpoints return a list of items, a page at a time. Every list endpoint pages the same way, so you write the loop once.
The response
A successful list response has three keys: data, an array of items in the resource's
shared schema; page, which says whether more items exist and how to get them; and meta, as on
every response:
{
"data": [
"…"
],
"page": {
"has_more": true,
"next_cursor": "…",
"next_cursor_expires_at": "…"
},
"meta": {
"request_id": "…",
"platform": "…",
"source": "…",
"is_cached": true,
"credits_charged": 0,
"latency_ms": 0,
"fetched_at": "…"
}
}| Field | Type | Description |
|---|---|---|
page.has_more | boolean | True when another page exists; then next_cursor is set. An empty page with has_more true is not the end. |
page.next_cursor | string or null | Pass as cursor, with the same other parameters, to get the next page; null at the end. |
page.next_cursor_expires_at | string (date-time) or null | When next_cursor stops working, RFC 3339 UTC; null at the end. Don't store cursors. |
There are no totals: platforms don't report them reliably, and counting would cost extra requests.
Parameters
Every list endpoint accepts these on top of its own parameters and the common options:
| Parameter | Type | Description |
|---|---|---|
limit | integermin 1 | Items per page, 1 to the endpoint's page size (the default and maximum; larger values are reduced). Reduces payload, not price: each page costs the same. |
cursor | string | page.next_cursor from the previous response. Send the same other parameters; omit to start from the first page. |
Getting the next page
- Send the first request without
cursor. - If
page.has_moreistrue, send the same request again withcursorset topage.next_cursorfrom the previous response. - Stop when
page.has_moreisfalse.
Keep every other parameter the same: only limit and the common options such as max_age
may change between pages. A page can be empty and still have page.has_more set to true; keep going.
const items = [];
let cursor = null;
do {
const params = new URLSearchParams({ ...yourParams, ...(cursor ? { cursor: cursor } : {}) });
const res = await fetch(`https://staging.socialtap.dev/v1/<platform>/<endpoint>?${params}`, {
headers: { "x-api-key": process.env.SOCIALTAP_API_KEY },
});
if (!res.ok) throw new Error((await res.json()).error.code);
const body = await res.json();
items.push(...body.data);
cursor = body.page.next_cursor;
} while (cursor);Cursors
Cursors are opaque and short-lived: don't parse, build or store them. Each one stops working at
page.next_cursor_expires_at. A cursor that is malformed, expired, from another endpoint, or sent with
different parameters fails with invalid_cursor (HTTP 400) and costs nothing; repeat
the original request without a cursor to start again.
Order and page size
Each list endpoint states the order of its items and its page size: the default and the largest limit
it accepts (larger values are reduced). One request reads one page from the platform, so a list of N items takes about
N ÷ page size requests when you leave limit out.
Pricing
A list endpoint's price is per page: every successful page costs the same, however many items it holds and
whatever limit you send (a smaller limit reduces the payload, not the price). Empty pages are charged
like any other successful page. Failed requests, including invalid_cursor, are free, and so are pages served
from the cache. See Credits and caching.
A smaller limit costs more
A limit below the page size does not split one charge into parts. The request returns the first
limit items of a platform page, and the next request, with its cursor, serves the rest of that same
platform page: it is fetched again and charged as a page each time. Walking a page in parts therefore costs one page
per part, and because the platform page is read again for every part, items can shift between those requests if the
platform adds or removes items in the meantime, so an item may repeat or be skipped.
Omitting limit is the cheapest way to walk a list: every request returns a full page, and each
platform page is read and charged once. Use a smaller limit only when you need fewer items than a page
holds and will stop there.
Errors
On top of the errors every endpoint can return, list endpoints have these:
| Code | HTTP | Retry | Meaning | What to do |
|---|---|---|---|---|
invalid_cursor | 400 | No | The cursor is malformed, expired, belongs to another endpoint, or the other parameters differ from the request that issued it. | Repeat the original request without cursor to start the list again. |
A rejected cursor costs nothing; repeat the original request without cursor to start again.