Pagination
Every list endpoint returns a page object alongside data:
{
"data": [ /* story objects */ ],
"page": {
"page": 1,
"page_size": 2,
"offset": 0,
"total_count": 2,
"total_pages": 1,
"has_more": false,
"cursor": null,
"next_cursor": null
}
}The page object
| Field | Type | Meaning |
|---|---|---|
page | number | Current page number, starting at 1. |
page_size | number | Records in this response. Maximum 100. |
offset | number | Zero-based offset of the first record. |
total_count | number | Total records matching the query. |
total_pages | number | Total pages at the current page_size. |
has_more | boolean | Whether further records exist. Prefer this over comparing page numbers. |
cursor | string | null | Cursor that produced this page, if any. |
next_cursor | string | null | Opaque cursor for the next page. null when exhausted. |
Two modes
Three parameters control paging: page, offset, and cursor. If cursor is supplied, offset is ignored.
Cursor — use this for walking a feed
Cursors are stable against inserts. New stories arriving mid-walk won't cause you to skip or repeat records. Use cursors for backfills and sequential ingestion.
# First page
curl -sS -G "https://theneuralledger.com/v1/news" \
-H "Authorization: Bearer $TNL_API_KEY" \
--data-urlencode "page_size=100"
# Subsequent pages: pass page.next_cursor back as cursor
curl -sS -G "https://theneuralledger.com/v1/news" \
-H "Authorization: Bearer $TNL_API_KEY" \
--data-urlencode "page_size=100" \
--data-urlencode "cursor=eyJvZmZzZXQiOjUwfQ"Treat the cursor as opaque. Its encoding is not part of the contract and may change.
Offset — use this for jumpable UI
page or offset suits numbered pagination where a user clicks page 7 directly. On a fast-moving feed, offsets drift as new stories arrive — records can repeat or be skipped between requests.
curl -sS -G "https://theneuralledger.com/v1/news" \
-H "Authorization: Bearer $TNL_API_KEY" \
--data-urlencode "page=3" \
--data-urlencode "page_size=50"Draining a feed correctly
async function* allStories(params = {}) {
let cursor = null;
do {
const query = new URLSearchParams({ ...params, page_size: '100' });
if (cursor) query.set('cursor', cursor);
const response = await fetch(`https://theneuralledger.com/v1/news?${query}`, {
headers: { Authorization: `Bearer ${process.env.TNL_API_KEY}` },
});
if (response.status === 429) throw new Error('Monthly limit exceeded');
if (!response.ok) throw new Error(`TNL API ${response.status}`);
const { data, page } = await response.json();
yield* data;
cursor = page.next_cursor;
} while (cursor);
}Stop on next_cursor === null, not on an empty data array — and note that each page costs one API call against your monthly allowance.
Pair pagination with field shaping
A 100-record page carrying full body, sources, and claims is large. Add fields or include to keep responses small. See Filtering & field shaping.