SocialCrawl

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
  }
}
FieldMeaning
pagination.next_cursorOpaque token for the next page. Send it back as cursor. null at the end of the list.
pagination.has_moretrue while more pages exist, false on the last page. This is your stop signal.
pagination.page_sizeHow 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:

  1. Send next_cursor back as the EXACT string you received, under the key cursor. Don't decode, trim, or re-encode it; it's an opaque token.
  2. Stop when has_more is false (equivalently, when next_cursor is null). Don't infer the end from an empty page or from total.

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 previous next_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 pages if 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_more flips to false when 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

EndpointPathSend back asStylePage size
Search the Apple App Store listings database (paginated)GET /v1/app_store/app-listings-searchcursorcursorlimit

Content Analysis

EndpointPathSend back asStylePage size
Search web citations of a keyword with per-mention sentimentGET /v1/content_analysis/searchcursorcursorlimit

Facebook

EndpointPathSend back asStylePage size
List Facebook Ad Library company adsGET /v1/facebook/adlibrary/company/adscursorcursorโ€”
Search Facebook Ad LibraryGET /v1/facebook/adlibrary/search/adscursorcursorโ€”
List Facebook events for a cityGET /v1/facebook/eventscursorcursorโ€”
Search Facebook events by keywordGET /v1/facebook/events/searchcursorcursorโ€”
Search Facebook Marketplace listingsGET /v1/facebook/marketplace/searchcursorcursorcount
List replies to a Facebook post commentGET /v1/facebook/post/comment/repliescursorcursorโ€”
List Facebook post commentsGET /v1/facebook/post/commentscursorcursorโ€”
List a Facebook page's eventsGET /v1/facebook/profile/eventscursorcursorโ€”
Facebook profile, recent posts, and computed analytics in one call.GET /v1/facebook/profile/fullcursorcursorโ€”
List Facebook profile photosGET /v1/facebook/profile/photoscursorcursorโ€”
List Facebook page postsGET /v1/facebook/profile/postscursorcursorโ€”
List Facebook profile reelsGET /v1/facebook/profile/reelscursorcursorโ€”
Facebook profile reels with exact views, likes, comments, and shares merged in, in one call.GET /v1/facebook/profile/reels/fullcursorcursorlimit

GitHub

EndpointPathSend back asStylePage size
Get comments on an issue or pull requestGET /v1/github/issue/commentscursorpageper_page
List a GitHub user's repositoriesGET /v1/github/profile/reposcursorpageper_page
List a repository's issues (and PRs)GET /v1/github/repo/issuescursorpageper_page
List a repository's releasesGET /v1/github/repo/releasescursorpageper_page
Search GitHub issues and pull requestsGET /v1/github/searchcursorpageper_page

Google

EndpointPathSend back asStylePage size
List Google ads by companyGET /v1/google/company/adscursorcursorโ€”
Google web searchGET /v1/google/searchcursorpageโ€”

Google Play

EndpointPathSend back asStylePage size
Search the Google Play listings database (paginated)GET /v1/google_play/app-listings-searchcursorcursorlimit

Hacker News

EndpointPathSend back asStylePage size
Search Hacker NewsGET /v1/hackernews/searchcursorpageโ€”

Instagram

EndpointPathSend back asStylePage size
List Instagram reels using an audio trackGET /v1/instagram/audio/reelscursorcursorโ€”
List Instagram followersGET /v1/instagram/followerscursorcursorโ€”
List Instagram followingGET /v1/instagram/followingcursorcursorโ€”
List recent posts at an Instagram locationGET /v1/instagram/location/postscursorcursorโ€”
List Instagram post commentsGET /v1/instagram/post/commentscursorcursorโ€”
Instagram profile, recent posts, and computed analytics in one call.GET /v1/instagram/profile/fullcursorcursorโ€”
List Instagram user postsGET /v1/instagram/profile/postscursorcursorโ€”
Instagram posts with views, likes, comments, and per-post share counts where available, in one call.GET /v1/instagram/profile/posts/fullcursorcursorlimit
List Instagram user reelsGET /v1/instagram/profile/reelscursorcursorโ€”
Instagram reels with views, likes, comments, and per-reel share counts where available, in one call.GET /v1/instagram/profile/reels/fullcursorcursorlimit
Search Instagram posts by hashtagGET /v1/instagram/search/hashtagcursorcursorโ€”
Search Instagram musicGET /v1/instagram/search/musiccursorcursorโ€”
Search Instagram profiles by keywordGET /v1/instagram/search/profilescursorcursorโ€”
Search Instagram reelsGET /v1/instagram/search/reelscursorpageโ€”
List posts an Instagram user is tagged inGET /v1/instagram/taggedcursorcursorโ€”

Kwai

EndpointPathSend back asStylePage size
List a Kwai user's postsGET /v1/kwai/user/postscursorcursorcount

LinkedIn

EndpointPathSend back asStylePage size
Search LinkedIn adsGET /v1/linkedin/ads/searchcursorcursorโ€”
List a company's job postingsGET /v1/linkedin/company/jobscursorpageโ€”
List people at a LinkedIn companyGET /v1/linkedin/company/peoplecursorpageโ€”
List LinkedIn company postsGET /v1/linkedin/company/postscursorpageโ€”
List posts in a LinkedIn groupGET /v1/linkedin/group/postscursorpageโ€”
Get LinkedIn post commentsGET /v1/linkedin/post/commentscursorpageโ€”
List replies to a LinkedIn commentGET /v1/linkedin/post/comments/repliescursorcursorโ€”
List reactors on a LinkedIn postGET /v1/linkedin/post/reactionscursorpageโ€”
List reposts of a LinkedIn postGET /v1/linkedin/post/repostscursorcursorโ€”
List a member's licenses and certificationsGET /v1/linkedin/profile/certificationscursorpageโ€”
List a member's commentsGET /v1/linkedin/profile/commentscursorcursorโ€”
List a member's education historyGET /v1/linkedin/profile/educationscursorpageโ€”
List a member's work experiencesGET /v1/linkedin/profile/experiencescursorpageโ€”
LinkedIn company profile, recent posts, and computed analytics in one call.GET /v1/linkedin/profile/fullcursorcursorโ€”
List a member's honors and awardsGET /v1/linkedin/profile/honorscursorpageโ€”
List a member's image postsGET /v1/linkedin/profile/imagescursorcursorโ€”
List companies a member followsGET /v1/linkedin/profile/interests/companiescursorpageโ€”
List groups a member followsGET /v1/linkedin/profile/interests/groupscursorpageโ€”
List a LinkedIn member's postsGET /v1/linkedin/profile/postscursorcursorโ€”
List a member's publicationsGET /v1/linkedin/profile/publicationscursorpageโ€”
List posts a LinkedIn member reacted toGET /v1/linkedin/profile/reactionscursorcursorโ€”
List recommendations for a memberGET /v1/linkedin/profile/recommendationscursorpageโ€”
List a member's skillsGET /v1/linkedin/profile/skillscursorpageโ€”
List a member's video postsGET /v1/linkedin/profile/videoscursorcursorโ€”
List a member's volunteer experiencesGET /v1/linkedin/profile/volunteerscursorpageโ€”
Search LinkedIn jobsGET /v1/linkedin/search/jobscursorpageโ€”
Search LinkedIn peopleGET /v1/linkedin/search/peoplecursorpageโ€”
Search public LinkedIn posts by keywordGET /v1/linkedin/search/postscursorpageโ€”
Search LinkedIn schoolsGET /v1/linkedin/search/schoolscursorpageโ€”
EndpointPathSend back asStylePage size
Search Naver BlogGET /v1/naver/blog/searchcursoroffsetdisplay
Search Naver BookGET /v1/naver/book/searchcursoroffsetdisplay
One query across the Korean internet (6 Naver corpora) + optional digest.GET /v1/naver/briefcursorcursordisplay
Search Naver Cafe articlesGET /v1/naver/cafearticle/searchcursoroffsetdisplay
Search Naver Academic Documents (์ „๋ฌธ์ž๋ฃŒ)GET /v1/naver/doc/searchcursoroffsetdisplay
Search Naver EncyclopediaGET /v1/naver/encyc/searchcursoroffsetdisplay
Search Naver ImageGET /v1/naver/image/searchcursoroffsetdisplay
Search Naver KnowledgeiN (์ง€์‹iN)GET /v1/naver/kin/searchcursoroffsetdisplay
Search Naver Local (์žฅ์†Œ ๊ฒ€์ƒ‰)GET /v1/naver/local/searchcursoroffsetdisplay
Search Naver NewsGET /v1/naver/news/searchcursoroffsetdisplay
Search Naver ShoppingGET /v1/naver/shop/searchcursoroffsetdisplay
Search Naver Web (์›น๋ฌธ์„œ)GET /v1/naver/webkr/searchcursoroffsetdisplay

Pinterest

EndpointPathSend back asStylePage size
Search Pinterest pinsGET /v1/pinterest/searchcursorcursorโ€”

Prism

Reddit

EndpointPathSend back asStylePage size
Reddit VoC sweep: one keyword โ†’ threads across all of Reddit with subreddit attribution and top comments inline.GET /v1/reddit/omni-searchcursorcursorโ€”
List Reddit post commentsGET /v1/reddit/post/commentscursorcursorโ€”
Search Reddit postsGET /v1/reddit/searchcursorcursorโ€”
List Reddit subreddit postsGET /v1/reddit/subredditcursorcursorโ€”
Search within a subredditGET /v1/reddit/subreddit/searchcursorcursorโ€”

Rumble

EndpointPathSend back asStylePage size
List videos for a Rumble channelGET /v1/rumble/channel/videoscursorcursorโ€”
Search Rumble videosGET /v1/rumble/searchcursorcursorโ€”

Spotify

EndpointPathSend back asStylePage size
List a Spotify podcast's episodesGET /v1/spotify/podcast/episodescursorcursorโ€”

TikTok

EndpointPathSend back asStylePage size
List TikTok post commentsGET /v1/tiktok/post/commentscursorcursorโ€”
TikTok profile, recent posts, and computed analytics in one call.GET /v1/tiktok/profile/fullcursorcursorโ€”
List TikTok user videosGET /v1/tiktok/profile/videoscursorcursorโ€”
Search TikTok videos by keywordGET /v1/tiktok/searchcursorcursorโ€”
Search TikTok by hashtagGET /v1/tiktok/search/hashtagcursorcursorโ€”
TikTok top search resultsGET /v1/tiktok/search/topcursorcursorโ€”
Search TikTok usersGET /v1/tiktok/search/userscursorcursorโ€”
List TikTok videos using a songGET /v1/tiktok/song/videoscursorcursorโ€”
List TikTok user followersGET /v1/tiktok/user/followerscursorcursorโ€”
List TikTok comment repliesGET /v1/tiktok/video/comment/repliescursorcursorโ€”

TikTok Shop

EndpointPathSend back asStylePage size
Search TikTok Shop productsGET /v1/tiktokshop/searchcursorpageโ€”
List TikTok user showcase productsGET /v1/tiktokshop/user/showcasecursorcursorโ€”

Truth Social

EndpointPathSend back asStylePage size
List Truth Social user postsGET /v1/truthsocial/user/postscursorcursorโ€”

Twitter/X

EndpointPathSend back asStylePage size
X (Twitter) profile, recent posts, and computed analytics in one call.GET /v1/twitter/profile/fullcursorcursorโ€”

Web Scraping

EndpointPathSend back asStylePage size
List async web jobsGET /v1/web/jobscursorcursorlimit
List web monitorsGET /v1/web/monitorscursorcursorlimit

YouTube

EndpointPathSend back asStylePage size
List a YouTube channel's community postsGET /v1/youtube/channel/community-postscursorcursorโ€”
List a YouTube channel's live streamsGET /v1/youtube/channel/livescursorcursorโ€”
List a YouTube channel's playlistsGET /v1/youtube/channel/playlistscursorcursorโ€”
List YouTube channel shortsGET /v1/youtube/channel/shortscursorcursorโ€”
List YouTube channel videosGET /v1/youtube/channel/videoscursorcursorโ€”
Get YouTube playlistGET /v1/youtube/playlistcursorcursorโ€”
List the videos in a YouTube playlistGET /v1/youtube/playlist/itemscursorcursorโ€”
YouTube profile, recent posts, and computed analytics in one call.GET /v1/youtube/profile/fullcursorcursorโ€”
Search YouTubeGET /v1/youtube/searchcursorcursorโ€”
Advanced YouTube video searchGET /v1/youtube/search/advancedcursorcursorโ€”
Search YouTube by hashtagGET /v1/youtube/search/hashtagcursorcursorโ€”
List YouTube comment repliesGET /v1/youtube/video/comment/repliescursorcursorโ€”
List YouTube video commentsGET /v1/youtube/video/commentscursorcursorโ€”
Get trending YouTube videosGET /v1/youtube/videos/trendingcursorcursorโ€”

See also

Pagination | SocialCrawl