Pagination and sorting
Read lists a page at a time, understand result counts, and choose a predictable order.
List endpoints return a page of records, not necessarily every matching record. Use limit and offset to move through the list, and sort to choose its order. This guide uses List Stories; check each endpoint's reference for the parameters and sort fields it supports.
Use the token you loaded in the first request walkthrough. The requests here require stories:read when using a personal token.
Request one page
curl --fail-with-body --silent --show-error --get 'https://api.prezly.com/v2/stories' \
--header "Authorization: Bearer $PREZLY_API_TOKEN" \
--header 'Accept: application/json' \
--data-urlencode 'limit=50' \
--data-urlencode 'offset=0' \
--data-urlencode 'sort=-created_at' \
--data-urlencode 'query={"status":{"$in":["published"]}}'limitis the maximum number of records requested for this page. Start with a modest size such as 50. The documented default is 20; consult the endpoint reference for its limits.offsetis how many matching records to skip. It starts at zero and is not a page number. With a limit of 50, the next offsets are 50, 100, and 150.sort=-created_atputs newer stories first. The query selects published stories; Filtering results explains its syntax.
A shortened, illustrative response might be:
{
"stories": [
{
"uuid": "22222222-2222-4222-8222-222222222222",
"title": "Acme introduces its new collection"
}
],
"pagination": {
"offset": 0,
"limit": 50,
"matched_records_number": 83,
"total_records_number": 120
}
}The array is abridged here. A real page can contain up to the returned limit records.
Understand the counts
| Field | Meaning |
|---|---|
offset | The number of matching records skipped for this page. |
limit | The page size used by the server. |
matched_records_number | The number matching this request's filters and search within your access. It is not the number in this page. |
total_records_number | The total within the endpoint's base scope and your access, before this request's filtering and search. |
For the example above, a filter has narrowed 120 available stories to 83 matches. Request offset=50 to retrieve the remaining matches. Use the matched count to decide whether more pages remain; the total count would make you request unnecessary pages. With no filter or search, these counts will normally agree.
An empty array is still a successful response. It can mean no matches, an offset beyond the last match, or a list that changed between requests.
Choose the sort order
Prefix a supported field with + for ascending order or - for descending order:
| Sort value | Order |
|---|---|
+title | Title, ascending. |
-created_at | Most recently created first. |
-published_at | Most recently published first. Filter to published stories if that is what you need. |
Encode the parameter rather than concatenating it into a URL: an unencoded + can be interpreted as a space. cURL's --data-urlencode 'sort=+title' and JavaScript's URLSearchParams preserve it correctly.
Supported fields vary by endpoint. For example, a JSON response field is not automatically a valid sort field. Set the sort explicitly when order matters and keep it unchanged as you request subsequent pages.
Read all matching stories with JavaScript
This Node.js 22+ example reads pages sequentially. Load PREZLY_API_TOKEN, save the code as list-stories.mjs, and run node list-stories.mjs. It prints UUIDs and titles; it does not modify stories.
const token = process.env.PREZLY_API_TOKEN;
if (!token) throw new Error('Set PREZLY_API_TOKEN before running this script.');
let offset = 0;
while (true) {
const url = new URL('https://api.prezly.com/v2/stories');
url.search = new URLSearchParams({
limit: '50',
offset: String(offset),
sort: '-created_at',
query: JSON.stringify({ status: { $in: ['published'] } }),
}).toString();
const response = await fetch(url, {
headers: { Authorization: `Bearer ${token}`, Accept: 'application/json' },
signal: AbortSignal.timeout(15_000),
});
if (!response.ok) throw new Error(`Prezly returned HTTP ${response.status}.`);
const { stories, pagination } = await response.json();
for (const story of stories) console.log(story.uuid, story.title);
const nextOffset = pagination.offset + pagination.limit;
if (stories.length === 0 || nextOffset >= pagination.matched_records_number) break;
offset = nextOffset;
}The loop uses the server's returned offset and limit, stops on an empty page, and makes no further requests after an HTTP error. Keep any filtering identical across pages.
Offset pagination is not a snapshot. If records are created, removed, or change their sort position during the traversal, records can move between pages. For an import, deduplicate by UUID and plan how to reconcile later changes. Do not treat the pagination counts as a permanent inventory of the account.