Answers
What is next_cursor, and why can't I jump straight to page 5?
Short answer
next_cursor is an opaque bookmark for where the last page stopped; you send it back unchanged as cursor to get the next page. The platforms' own feeds only know what comes after a point, so page 5 means fetching pages 1 to 4 first. Each page is one call and one charge: five pages of Douyin comments cost $0.01 on okdata.
Every list endpoint returns one page at a time, with a page object next to the items:
"page": { "next_cursor": "c1.k3Vq9TzR0bYw2LmXe8NdPa", "has_more": true }
next_cursor is a bookmark. It records where this page stopped, in a form only the server can read. To get the next page, send it back as the cursor parameter, exactly as you received it. When has_more is false, the list is finished and there is no cursor.
Why there is no "page=5"
Offset paging (page=5, offset=80) needs a server that can count and skip. The platforms do not serve their feeds that way. Douyin returns a pointer with each batch of comments, and the next batch is whatever follows it. Xiaohongshu search does number its pages, but each request must also carry session values that the previous page returned, so page 5 cannot be the first request. Slack's engineering team describes the same trade-off for its own API: cursors stay correct when items are added while you page, but there is no total page count and the client cannot jump to a specific page. X's API works the same way with its next_token.
So page 5 is reachable only through pages 1 to 4. A cursor also belongs to the request it came from: send it to the same endpoint with the same query, id and sort. view and fields may change between pages. A cursor that belongs to another request is refused as cursor_mismatch, and one past its lifetime as cursor_expired, before anything is charged.
What paging costs
One call returns one page, and each page is billed as its own call.
| Job | Calls | Cost |
|---|---|---|
| First page of a video's comments, about 20 | 1 | $0.002 |
| Comments up to page 5 | 5 | $0.01 |
Xiaohongshu search for 防晒霜, 5 pages |
5 | $0.10 |
| The same search, 20 pages | 20 | $0.40 |
Decide how many pages you need before you start, and stop as soon as you have them. A fixed page cap in code is cheaper than a promise to remember.
Example
import os, requests
URL = "https://api.okdata.online/v1/douyin/videos/comments/list"
KEY = os.environ["OKDATA_API_KEY"]
def comments(video_id: str, max_pages: int = 5):
cursor, seen = None, set()
for n in range(max_pages):
params = {"id": video_id, "fields": "id,text,likes,published_at"}
if cursor:
params["cursor"] = cursor
r = requests.get(URL, params=params, timeout=60, headers={
"Authorization": f"Bearer {KEY}",
"Idempotency-Key": f"dy-comments-{video_id}-{n}-{cursor or 'first'}",
}).json()
if r["code"] != "ok":
raise RuntimeError(f'{r["code"]}: {r["message"]}')
for c in r["data"]["items"]:
if c["id"] not in seen:
seen.add(c["id"])
yield c
if not r["page"]["has_more"]:
return
cursor = r["page"]["next_cursor"]
rows = list(comments("7412345678901234567", max_pages=5))
print(len(rows), "comments")
The idempotency key includes the cursor, so a retry after a timeout replays that page instead of paying for it twice.
Limits
- Use a cursor to continue a listing now, not as a bookmark to resume next week. If one is refused as
cursor_expired, start again without a cursor. - Lists move while you page. New comments arrive and rankings shift, so deduplicate by
idas the loop above does. - Platforms cap how deep a list goes; see why Xiaohongshu search stops after a couple of hundred notes and why you get fewer Douyin comments than the counter shows.
- The requests page has the full paging contract; the endpoint pages, such as Douyin video comments and Xiaohongshu note search, list each endpoint's page size.
Sources
Where people asked this (read 2026-11-12)
Updated