Pagination
Every list — brands, links, testimonials, tags, widgets, reels, API keys, webhook endpoints, a webhook's deliveries — is a page in the same envelope:
{
"data": [
{ "id": "sub_01K5A3M2Q8WJ4T6V8X0Y2A4C6E", "…": "…" },
{ "id": "sub_01K5A2Z9H6N3P7R1T5V9X3Z7B1", "…": "…" }
],
"next_cursor": "eyJ0IjoiMjAyNi0wOS0xNFQxNzoyNTozMS4wMDBaIiwiaSI6InN1Yl8wMUs1QTJaOUg2TjNQN1IxVDVWOVgzWjdCMSJ9"
}
limitasks for up to 100 rows (the default is 25).next_cursorisnullon the last page. Otherwise pass it back ascursorfor the next one.- A cursor is opaque. Do not build one, read one, or keep one for later: it is valid for the query that produced it, and its format can change.
- Pages are in a fixed order, newest first unless the route says otherwise, and they are keyed on that order, not counted. A testimonial that arrives while you page does not shift the pages you have not read yet, and nothing is skipped or read twice.
- Filters (
?brand_id=…&status=ready) go on every page's request, the same each time.
Reading every page
cursor=""
while :; do
page=$(curl -s "https://api.akteora.com/v1/submissions?limit=100${cursor:+&cursor=$cursor}" \
-H "Authorization: Bearer $AKTEORA_API_KEY")
echo "$page" | jq -c '.data[]'
cursor=$(echo "$page" | jq -r '.next_cursor // empty')
[ -z "$cursor" ] && break
done
import { createAkteoraClient, unwrap, type Submission } from '@akteora/sdk'
const akteora = createAkteoraClient({ baseUrl: 'https://api.akteora.com', apiKey: KEY })
async function* everyTestimonial(): AsyncGenerator<Submission> {
let cursor: string | undefined
do {
const page = await unwrap(
akteora.GET('/v1/submissions', { params: { query: { limit: 100, cursor } } }),
)
yield* page.data
cursor = page.next_cursor ?? undefined
} while (cursor !== undefined)
}
A search (?q=) is ordered by relevance instead, and its cursor carries that order.
To read a very large set once — for a spreadsheet or a migration — an export is kinder than a thousand pages: one request, one file.