Pagination
One rule for every list endpoint. Send `pagination.next_cursor` back as `cursor` and stop when `has_more` is false. Identical on all 123 paginatable endpoints, every platform.
Pagination
Every list endpoint on every platform paginates the same way. Learn it once and it works on all 123 paginatable endpoints: TikTok, Instagram, YouTube, Reddit, LinkedIn, Naver, all of them.
The one rule
Read pagination.next_cursor from the response, send it back as the cursor query parameter, and stop when pagination.has_more is false.
That is the entire contract. You never construct a cursor, decode a token, or look up a platform's native pagination param. The API hands you an opaque token and takes it straight back.
The response block
Every list response carries a top-level pagination block:
{
"success": true,
"platform": "tiktok",
"endpoint": "/v1/tiktok/profile/videos",
"data": {
"items": [ /* … this page of results … */ ],
"total": 1247
},
"credits_used": 1,
"credits_remaining": 98,
"request_id": "req-abc123",
"cached": false,
"pagination": {
"next_cursor": "sc.eyJ2IjoyLCJjIjoiNzM4…",
"has_more": true,
"page_size": 30
}
}| Field | Meaning |
|---|---|
pagination.next_cursor | Opaque token for the next page. Send it back as cursor. null at the end of the list. |
pagination.has_more | true while more pages exist, false on the last page. This is your stop signal. |
pagination.page_size | How many items came back in data.items on this page. |
The token is a single sc. string that works identically for cursor-, page-, and offset-style upstreams, so your loop is byte-for-byte the same everywhere. (data.total, when present, is the upstream's own result count, useful for a progress bar, but never use it to decide when to stop; has_more is the only always-correct signal.)
A worked round-trip
Request page 1 (no cursor):
curl "https://www.socialcrawl.dev/v1/tiktok/profile/videos?handle=mrbeast" \
-H "x-api-key: YOUR_API_KEY"The response comes back with "pagination": { "next_cursor": "sc.eyJ2Ijoy…", "has_more": true, "page_size": 30 }.
Request page 2 (pass that next_cursor back as cursor):
curl "https://www.socialcrawl.dev/v1/tiktok/profile/videos?handle=mrbeast&cursor=sc.eyJ2Ijoy…" \
-H "x-api-key: YOUR_API_KEY"Keep going until a response returns "has_more": false (and "next_cursor": null). Swap tiktok/profile/videos for any list endpoint in the table below and this is unchanged.
The loop
Identical for every list endpoint on every platform:
async function* paginate(path: string, params: Record<string, string>) {
let cursor: string | null = null;
do {
const url = new URL(`https://www.socialcrawl.dev/v1/${path}`);
for (const [k, v] of Object.entries(params)) url.searchParams.set(k, v);
if (cursor) url.searchParams.set("cursor", cursor); // <-- always "cursor"
const res = await fetch(url, {
headers: { "x-api-key": process.env.SOCIALCRAWL_KEY! },
});
const json = await res.json();
for (const item of json.data.items) yield item;
cursor = json.pagination.next_cursor; // <-- send back verbatim next round
} while (cursor); // <-- pagination.has_more === false ends the loop
}
// Usage: same call shape for TikTok, Instagram, YouTube, Reddit, …
for await (const video of paginate("tiktok/profile/videos", { handle: "mrbeast" })) {
// … handle each item
}Two things to get right:
- Send
next_cursorback as the EXACT string you received, under the keycursor. Don't decode, trim, or re-encode it; it's an opaque token. - Stop when
has_moreisfalse(equivalently, whennext_cursorisnull). Don't infer the end from an empty page or fromtotal.
Recipe: drain a full account safely
The real-world loop is "pull every post/comment/follower an account has" without burning credits or tripping limits. Walk pages until has_more is false, and keep three things in mind:
// Drain every video for a handle, then stop.
async function drain(path: string, params: Record<string, string>) {
const all: unknown[] = [];
let cursor: string | null = null;
let pages = 0;
do {
const url = new URL(`https://www.socialcrawl.dev/v1/${path}`);
for (const [k, v] of Object.entries(params)) url.searchParams.set(k, v);
if (cursor) url.searchParams.set("cursor", cursor);
const res = await fetch(url, {
headers: { "x-api-key": process.env.SOCIALCRAWL_KEY! },
});
// Respect backpressure: on 429 (CONCURRENCY_LIMIT) or 503 back off and retry
// the SAME cursor; never advance past a page you didn't successfully read.
if (res.status === 429 || res.status === 503) {
const wait = Number(res.headers.get("retry-after") ?? "2") * 1000;
await new Promise((r) => setTimeout(r, wait));
continue;
}
const json = await res.json();
all.push(...json.data.items);
cursor = json.pagination.next_cursor;
pages += 1;
} while (cursor);
return { items: all, pages };
}- Concurrency. You can drain many accounts in parallel, but a single API key allows at most 50 concurrent requests. Exceed it and you get a
429 CONCURRENCY_LIMIT(0 credits charged). Cap your worker pool well under 50; within one account, pages are inherently sequential (each needs the previousnext_cursor). - Credit budget. Each page is a normal billed request: one page of a 1-credit endpoint costs 1 credit, so draining a 50-page feed costs about 50 credits. Multiply pages by the endpoint's per-call cost (see Endpoint pricing) before you loop, and cap
pagesif you only need the most recent N results. Cache hits on re-runs cost 0 credits. - Bounded depth. Page- and offset-style endpoints (Naver, LinkedIn, the app stores) have a finite maximum depth;
has_moreflips tofalsewhen you reach it. Cursor-style feeds end when the upstream runs out of items.
Sending the wrong param is a 400, not a silent bug
You never need a platform's native pagination param name: always send cursor. If you do send a foreign cursor param (say you copy max_id off an Instagram response onto a TikTok endpoint), you get a 400 INVALID_REQUEST with a did_you_mean: "cursor" hint and zero credits charged, instead of silently receiving page 1 again.
Stale or malformed cursors
If you pass a cursor the upstream rejects (expired, hand-edited, copied across endpoints), you'll get a 502 UPSTREAM_ERROR or 400 INVALID_PARAMETER envelope. Credits are refunded on upstream errors (see Credits → When are credits refunded?). Start over with no cursor.
Page size
Most endpoints let the upstream decide: you'll typically get 10 to 30 items per page (pagination.page_size tells you exactly). Where a page-size control exists (see the Page size column below), pass a universal limit and the API maps it to the upstream's native name (Naver display, GitHub per_page, and so on), capped at that endpoint's maximum. A — in that column means no page-size control: to get fewer items, slice client-side; to get more, paginate another page.
Every paginatable endpoint
123 endpoints paginate today (83 cursor-based, 29 page-based, 11 offset-based). Every one of them takes the universal cursor input and returns the same pagination block (next_cursor / has_more / page_size): no per-platform special cases. The Style and Page size columns are informational only; you always send cursor.
Apple App Store
| Endpoint | Path | Send back as | Style | Page size |
|---|---|---|---|---|
| Search the Apple App Store listings database (paginated) | GET /v1/app_store/app-listings-search | cursor | cursor | limit |
Content Analysis
| Endpoint | Path | Send back as | Style | Page size |
|---|---|---|---|---|
| Search web citations of a keyword with per-mention sentiment | GET /v1/content_analysis/search | cursor | cursor | limit |
| Endpoint | Path | Send back as | Style | Page size |
|---|---|---|---|---|
| List Facebook Ad Library company ads | GET /v1/facebook/adlibrary/company/ads | cursor | cursor | — |
| Search Facebook Ad Library | GET /v1/facebook/adlibrary/search/ads | cursor | cursor | — |
| List Facebook events for a city | GET /v1/facebook/events | cursor | cursor | — |
| Search Facebook events by keyword | GET /v1/facebook/events/search | cursor | cursor | — |
| Search Facebook Marketplace listings | GET /v1/facebook/marketplace/search | cursor | cursor | count |
| List replies to a Facebook post comment | GET /v1/facebook/post/comment/replies | cursor | cursor | — |
| List Facebook post comments | GET /v1/facebook/post/comments | cursor | cursor | — |
| List a Facebook page's events | GET /v1/facebook/profile/events | cursor | cursor | — |
| Facebook profile, recent posts, and computed analytics in one call. | GET /v1/facebook/profile/full | cursor | cursor | — |
| List Facebook profile photos | GET /v1/facebook/profile/photos | cursor | cursor | — |
| List Facebook page posts | GET /v1/facebook/profile/posts | cursor | cursor | — |
| List Facebook profile reels | GET /v1/facebook/profile/reels | cursor | cursor | — |
| Facebook profile reels with exact views, likes, comments, and shares merged in, in one call. | GET /v1/facebook/profile/reels/full | cursor | cursor | limit |
GitHub
| Endpoint | Path | Send back as | Style | Page size |
|---|---|---|---|---|
| Get comments on an issue or pull request | GET /v1/github/issue/comments | cursor | page | per_page |
| List a GitHub user's repositories | GET /v1/github/profile/repos | cursor | page | per_page |
| List a repository's issues (and PRs) | GET /v1/github/repo/issues | cursor | page | per_page |
| List a repository's releases | GET /v1/github/repo/releases | cursor | page | per_page |
| Search GitHub issues and pull requests | GET /v1/github/search | cursor | page | per_page |
| Endpoint | Path | Send back as | Style | Page size |
|---|---|---|---|---|
| List Google ads by company | GET /v1/google/company/ads | cursor | cursor | — |
| Google web search | GET /v1/google/search | cursor | page | — |
Google Play
| Endpoint | Path | Send back as | Style | Page size |
|---|---|---|---|---|
| Search the Google Play listings database (paginated) | GET /v1/google_play/app-listings-search | cursor | cursor | limit |
Hacker News
| Endpoint | Path | Send back as | Style | Page size |
|---|---|---|---|---|
| Search Hacker News | GET /v1/hackernews/search | cursor | page | — |
| Endpoint | Path | Send back as | Style | Page size |
|---|---|---|---|---|
| List Instagram reels using an audio track | GET /v1/instagram/audio/reels | cursor | cursor | — |
| List Instagram followers | GET /v1/instagram/followers | cursor | cursor | — |
| List Instagram following | GET /v1/instagram/following | cursor | cursor | — |
| List recent posts at an Instagram location | GET /v1/instagram/location/posts | cursor | cursor | — |
| List Instagram post comments | GET /v1/instagram/post/comments | cursor | cursor | — |
| Instagram profile, recent posts, and computed analytics in one call. | GET /v1/instagram/profile/full | cursor | cursor | — |
| List Instagram user posts | GET /v1/instagram/profile/posts | cursor | cursor | — |
| Instagram posts with views, likes, comments, and per-post share counts where available, in one call. | GET /v1/instagram/profile/posts/full | cursor | cursor | limit |
| List Instagram user reels | GET /v1/instagram/profile/reels | cursor | cursor | — |
| Instagram reels with views, likes, comments, and per-reel share counts where available, in one call. | GET /v1/instagram/profile/reels/full | cursor | cursor | limit |
| Search Instagram posts by hashtag | GET /v1/instagram/search/hashtag | cursor | cursor | — |
| Search Instagram music | GET /v1/instagram/search/music | cursor | cursor | — |
| Search Instagram profiles by keyword | GET /v1/instagram/search/profiles | cursor | cursor | — |
| Search Instagram reels | GET /v1/instagram/search/reels | cursor | page | — |
| List posts an Instagram user is tagged in | GET /v1/instagram/tagged | cursor | cursor | — |
Kwai
| Endpoint | Path | Send back as | Style | Page size |
|---|---|---|---|---|
| List a Kwai user's posts | GET /v1/kwai/user/posts | cursor | cursor | count |
| Endpoint | Path | Send back as | Style | Page size |
|---|---|---|---|---|
| Search LinkedIn ads | GET /v1/linkedin/ads/search | cursor | cursor | — |
| List a company's job postings | GET /v1/linkedin/company/jobs | cursor | page | — |
| List people at a LinkedIn company | GET /v1/linkedin/company/people | cursor | page | — |
| List LinkedIn company posts | GET /v1/linkedin/company/posts | cursor | page | — |
| List posts in a LinkedIn group | GET /v1/linkedin/group/posts | cursor | page | — |
| Get LinkedIn post comments | GET /v1/linkedin/post/comments | cursor | page | — |
| List replies to a LinkedIn comment | GET /v1/linkedin/post/comments/replies | cursor | cursor | — |
| List reactors on a LinkedIn post | GET /v1/linkedin/post/reactions | cursor | page | — |
| List reposts of a LinkedIn post | GET /v1/linkedin/post/reposts | cursor | cursor | — |
| List a member's licenses and certifications | GET /v1/linkedin/profile/certifications | cursor | page | — |
| List a member's comments | GET /v1/linkedin/profile/comments | cursor | cursor | — |
| List a member's education history | GET /v1/linkedin/profile/educations | cursor | page | — |
| List a member's work experiences | GET /v1/linkedin/profile/experiences | cursor | page | — |
| LinkedIn company profile, recent posts, and computed analytics in one call. | GET /v1/linkedin/profile/full | cursor | cursor | — |
| List a member's honors and awards | GET /v1/linkedin/profile/honors | cursor | page | — |
| List a member's image posts | GET /v1/linkedin/profile/images | cursor | cursor | — |
| List companies a member follows | GET /v1/linkedin/profile/interests/companies | cursor | page | — |
| List groups a member follows | GET /v1/linkedin/profile/interests/groups | cursor | page | — |
| List a LinkedIn member's posts | GET /v1/linkedin/profile/posts | cursor | cursor | — |
| List a member's publications | GET /v1/linkedin/profile/publications | cursor | page | — |
| List posts a LinkedIn member reacted to | GET /v1/linkedin/profile/reactions | cursor | cursor | — |
| List recommendations for a member | GET /v1/linkedin/profile/recommendations | cursor | page | — |
| List a member's skills | GET /v1/linkedin/profile/skills | cursor | page | — |
| List a member's video posts | GET /v1/linkedin/profile/videos | cursor | cursor | — |
| List a member's volunteer experiences | GET /v1/linkedin/profile/volunteers | cursor | page | — |
| Search LinkedIn jobs | GET /v1/linkedin/search/jobs | cursor | page | — |
| Search LinkedIn people | GET /v1/linkedin/search/people | cursor | page | — |
| Search public LinkedIn posts by keyword | GET /v1/linkedin/search/posts | cursor | page | — |
| Search LinkedIn schools | GET /v1/linkedin/search/schools | cursor | page | — |
Naver
| Endpoint | Path | Send back as | Style | Page size |
|---|---|---|---|---|
| Search Naver Blog | GET /v1/naver/blog/search | cursor | offset | display |
| Search Naver Book | GET /v1/naver/book/search | cursor | offset | display |
| One query across the Korean internet (6 Naver corpora) + optional digest. | GET /v1/naver/brief | cursor | cursor | display |
| Search Naver Cafe articles | GET /v1/naver/cafearticle/search | cursor | offset | display |
| Search Naver Academic Documents (전문자료) | GET /v1/naver/doc/search | cursor | offset | display |
| Search Naver Encyclopedia | GET /v1/naver/encyc/search | cursor | offset | display |
| Search Naver Image | GET /v1/naver/image/search | cursor | offset | display |
| Search Naver KnowledgeiN (지식iN) | GET /v1/naver/kin/search | cursor | offset | display |
| Search Naver Local (장소 검색) | GET /v1/naver/local/search | cursor | offset | display |
| Search Naver News | GET /v1/naver/news/search | cursor | offset | display |
| Search Naver Shopping | GET /v1/naver/shop/search | cursor | offset | display |
| Search Naver Web (웹문서) | GET /v1/naver/webkr/search | cursor | offset | display |
| Endpoint | Path | Send back as | Style | Page size |
|---|---|---|---|---|
| Search Pinterest pins | GET /v1/pinterest/search | cursor | cursor | — |
Prism
| Endpoint | Path | Send back as | Style | Page size |
|---|---|---|---|---|
| Every comment on a post, replies nested, server-paginated to completion. | GET /v1/prism/comments | cursor | cursor | limit |
| A Truth Social handle's pulse — profile, recent posts, per-post detail drill, and the news echo, in one call. | GET /v1/prism/truthsocial-pulse | cursor | cursor | — |
| One person's public posts across X, Threads, Bluesky, and Truth Social, time-merged. | GET /v1/prism/voice | cursor | cursor | — |
| Endpoint | Path | Send back as | Style | Page size |
|---|---|---|---|---|
| Reddit VoC sweep: one keyword → threads across all of Reddit with subreddit attribution and top comments inline. | GET /v1/reddit/omni-search | cursor | cursor | — |
| List Reddit post comments | GET /v1/reddit/post/comments | cursor | cursor | — |
| Search Reddit posts | GET /v1/reddit/search | cursor | cursor | — |
| List Reddit subreddit posts | GET /v1/reddit/subreddit | cursor | cursor | — |
| Search within a subreddit | GET /v1/reddit/subreddit/search | cursor | cursor | — |
Rumble
| Endpoint | Path | Send back as | Style | Page size |
|---|---|---|---|---|
| List videos for a Rumble channel | GET /v1/rumble/channel/videos | cursor | cursor | — |
| Search Rumble videos | GET /v1/rumble/search | cursor | cursor | — |
Spotify
| Endpoint | Path | Send back as | Style | Page size |
|---|---|---|---|---|
| List a Spotify podcast's episodes | GET /v1/spotify/podcast/episodes | cursor | cursor | — |
TikTok
| Endpoint | Path | Send back as | Style | Page size |
|---|---|---|---|---|
| List TikTok post comments | GET /v1/tiktok/post/comments | cursor | cursor | — |
| TikTok profile, recent posts, and computed analytics in one call. | GET /v1/tiktok/profile/full | cursor | cursor | — |
| List TikTok user videos | GET /v1/tiktok/profile/videos | cursor | cursor | — |
| Search TikTok videos by keyword | GET /v1/tiktok/search | cursor | cursor | — |
| Search TikTok by hashtag | GET /v1/tiktok/search/hashtag | cursor | cursor | — |
| TikTok top search results | GET /v1/tiktok/search/top | cursor | cursor | — |
| Search TikTok users | GET /v1/tiktok/search/users | cursor | cursor | — |
| List TikTok videos using a song | GET /v1/tiktok/song/videos | cursor | cursor | — |
| List TikTok user followers | GET /v1/tiktok/user/followers | cursor | cursor | — |
| List TikTok comment replies | GET /v1/tiktok/video/comment/replies | cursor | cursor | — |
TikTok Shop
| Endpoint | Path | Send back as | Style | Page size |
|---|---|---|---|---|
| Search TikTok Shop products | GET /v1/tiktokshop/search | cursor | page | — |
| List TikTok user showcase products | GET /v1/tiktokshop/user/showcase | cursor | cursor | — |
Truth Social
| Endpoint | Path | Send back as | Style | Page size |
|---|---|---|---|---|
| List Truth Social user posts | GET /v1/truthsocial/user/posts | cursor | cursor | — |
Twitter/X
| Endpoint | Path | Send back as | Style | Page size |
|---|---|---|---|---|
| X (Twitter) profile, recent posts, and computed analytics in one call. | GET /v1/twitter/profile/full | cursor | cursor | — |
Web Scraping
| Endpoint | Path | Send back as | Style | Page size |
|---|---|---|---|---|
| List async web jobs | GET /v1/web/jobs | cursor | cursor | limit |
| List web monitors | GET /v1/web/monitors | cursor | cursor | limit |
YouTube
| Endpoint | Path | Send back as | Style | Page size |
|---|---|---|---|---|
| List a YouTube channel's community posts | GET /v1/youtube/channel/community-posts | cursor | cursor | — |
| List a YouTube channel's live streams | GET /v1/youtube/channel/lives | cursor | cursor | — |
| List a YouTube channel's playlists | GET /v1/youtube/channel/playlists | cursor | cursor | — |
| List YouTube channel shorts | GET /v1/youtube/channel/shorts | cursor | cursor | — |
| List YouTube channel videos | GET /v1/youtube/channel/videos | cursor | cursor | — |
| Get YouTube playlist | GET /v1/youtube/playlist | cursor | cursor | — |
| List the videos in a YouTube playlist | GET /v1/youtube/playlist/items | cursor | cursor | — |
| YouTube profile, recent posts, and computed analytics in one call. | GET /v1/youtube/profile/full | cursor | cursor | — |
| Search YouTube | GET /v1/youtube/search | cursor | cursor | — |
| Advanced YouTube video search | GET /v1/youtube/search/advanced | cursor | cursor | — |
| Search YouTube by hashtag | GET /v1/youtube/search/hashtag | cursor | cursor | — |
| List YouTube comment replies | GET /v1/youtube/video/comment/replies | cursor | cursor | — |
| List YouTube video comments | GET /v1/youtube/video/comments | cursor | cursor | — |
| Get trending YouTube videos | GET /v1/youtube/videos/trending | cursor | cursor | — |
See also
- Response schema → List endpoints — the envelope shape in one sentence.
- Quickstart — your first request in under a minute.
- Endpoint pricing — every endpoint and what each call costs.
- Error handling — refund rules for stale-cursor errors, and the concurrency limit.
Metric substitutions and structural nulls
Where a platform's upstream cannot supply a canonical field, SocialCrawl returns an honest null or a documented substitute rather than a fabricated value. This page lists every intentional substitution and structural null by platform.
Credits
How SocialCrawl's credit-based billing works — tiers, welcome bonus, refunds, and free cache hits
