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