Paging
Every list, on every surface, comes back in the same envelope:
{ "items": [...], "total": 1204, "next_cursor": "50" }
| Field | Means |
|---|---|
items | This page of rows. |
total | How many match in all, when that is cheap to know. Null means we do not know, not zero. |
next_cursor | Pass back as cursor for the next page. Null means you have everything. |
The rule
Follow next_cursor until it comes back null. That is the only stop condition.
In particular, a page shorter than your limit does not mean you are done.
That heuristic is the most common way a client silently loses rows, so nothing here relies on it.
cursor=""
while :; do
page=$(curl -sG -H "Authorization: Bearer $SPUTNIK_TOKEN" \
--data-urlencode "q=your company" \
--data-urlencode "limit=100" \
--data-urlencode "cursor=$cursor" \
"https://sputnikintelligence.com/api/v1/posts/search")
echo "$page" | jq -c '.items[]'
cursor=$(echo "$page" | jq -r '.next_cursor // empty')
[ -z "$cursor" ] && break
done
The cursor is opaque
Do not build one, parse one, or assume it is a number. It is a token we issued, and what is inside
it differs between endpoints. Sending one we did not issue is a 422 with
code: invalid_cursor.
limit
1 to 100. Defaults to 50, or 20 for people and publications. Asking for more than 100 is a
422 rather than a silent clamp — if we quietly gave you 100 when you asked for 500,
you would have no way to tell.
Two endpoints are capped, not paged
GET /topics and GET /sponsors return at most 100 rows,
next_cursor is always null, and passing cursor is a 422
saying so. Narrow with q instead.
The reason is that the unfiltered ranking is "most used in the last 90 days", which stays fast
however large the corpus grows, while paging deep into a million topics would not — and most of
those topics appear on a single post, so nobody is reading page forty. When the cap is hit,
total comes back null to tell you there were more.
Alert hits are a stream, not a page
GET /alert-hits uses the same envelope for something different. Its
next_cursor is a resume token: store it, pass it back next time, and
you get only what has arrived since.
It keeps coming back non-null even when there is nothing new — that is what makes it resumable. Poll it on a schedule rather than looping until null, which for this endpoint would never happen.