# ScraperSocial — full documentation (plain text for agents) > REST API for publicly available social media data across TikTok, Instagram, > Facebook, X/Twitter, and LinkedIn. Base URL: https://api.scrapersocial.com > Auth: Authorization: Bearer sk_live_... Signup: https://scrapersocial.com/signup > MCP server: https://mcp.scrapersocial.com OpenAPI: https://scrapersocial.com/openapi.json --- # ScraperSocial documentation > Simple, affordable APIs for social media data — transcripts, stats, comments and profiles from TikTok, Instagram, Facebook, X and LinkedIn. ScraperSocial is one REST API for publicly available social media data across five platforms: **TikTok, Instagram, Facebook, X/Twitter, and LinkedIn**. Every endpoint takes a URL, handle, or query and returns clean JSON in seconds — popular content in milliseconds. ## Make your first call ```bash curl "https://api.scrapersocial.com/v1/tiktok/transcript?url=https://www.tiktok.com/@tiktok/video/7231338487075638570" \ -H "Authorization: Bearer sk_live_..." ``` No key yet? [Get your free API key](https://scrapersocial.com/signup) — 100 trial credits, no card. ## Where to go next - [Quickstart](/quickstart/) — first call in under 5 minutes - [Authentication](/authentication/) — API keys and headers - API reference: [TikTok](/api/tiktok/) · [Instagram](/api/instagram/) · [Facebook](/api/facebook/) · [X/Twitter](/api/twitter/) · [LinkedIn](/api/linkedin/) - [For AI agents](/ai-agents/) — MCP server, tool definitions, recipes ## The envelope Every response uses the same shape: ```json { "data": { "platform": "tiktok", "text": "..." }, "meta": { "count": 20, "cursor": "eyJwYWdlIjoyfQ", "has_more": true }, "request_id": "req_01JZX4M8Q2TE9W" } ``` `meta` appears on paginated list endpoints. Errors use a matching envelope — see [Errors](/errors/). ## Pricing at a glance Credits are charged per item returned. Simple items cost 1–5 credits; transcripts, AI summaries, downloads, and LinkedIn data cost more. The full schedule is on the [pricing page](https://scrapersocial.com/pricing/#credits), and every response carries `X-Credits-Charged` and `X-Credits-Remaining` headers. --- # Quickstart > From signup to your first transcript in under five minutes — get a key, paste a URL, read clean JSON. Five minutes, three steps, no credit card. ## 1. Get your key [Sign up](https://scrapersocial.com/signup) with email or GitHub. Your API key (`sk_live_...`) and 100 free trial credits are on screen immediately. ## 2. Make your first call Pick any public TikTok video and ask for its transcript: ```bash curl "https://api.scrapersocial.com/v1/tiktok/transcript?url=https://www.tiktok.com/@tiktok/video/7231338487075638570" \ -H "Authorization: Bearer sk_live_..." ``` Or from Node: ```js const url = new URL("https://api.scrapersocial.com/v1/tiktok/transcript"); url.searchParams.set("url", "https://www.tiktok.com/@tiktok/video/7231338487075638570"); const resp = await fetch(url, { headers: { Authorization: "Bearer sk_live_..." }, }); const { data, request_id } = await resp.json(); console.log(data.text); ``` ## 3. Read the response ```json { "data": { "platform": "tiktok", "entity_id": "7231338487075638570", "language": "en", "text": "Today I want to show you the three settings everyone ignores...", "segments": [ { "start": 0.0, "end": 3.2, "text": "Today I want to show you the three settings" } ] }, "request_id": "req_01JZX4M8Q2TE9W" } ``` That call cost 8 credits (transcripts are premium; simple stats cost 1–5). Check the `X-Credits-Remaining` header to see your balance. ## Next steps - Swap `tiktok/transcript` for any of the ~35 endpoints — same request shape everywhere. Start with the [TikTok reference](/api/tiktok/). - [Authentication](/authentication/) covers key management and rotation. - Building an agent? The [MCP server](/ai-agents/) exposes everything as tools. --- # Authentication > Authenticate with a bearer API key. Create, rotate, and revoke keys from the dashboard. Every request is authenticated with an API key in the `Authorization` header: ```bash curl "https://api.scrapersocial.com/v1/me" \ -H "Authorization: Bearer sk_live_..." ``` Keys start with `sk_live_` and are shown once at creation — store them in a secrets manager, not in source control. ## Managing keys Create, name, and revoke keys at [scrapersocial.com/app/keys](https://scrapersocial.com/app/keys). You can hold several keys at once (one per environment or service), and revoking one takes effect within seconds. ## Checking who you are `/v1/me` returns the account attached to the key — useful in CI to verify a secret is wired correctly: ```json { "data": { "account_id": "acc_01J...", "plan": "annual", "credits_remaining": 11310, "rate_limit_rpm": 300 }, "request_id": "req_01JZX4M8Q2TE9W" } ``` ## Failure modes | Status | Code | Meaning | | --- | --- | --- | | 401 | `unauthorized` | Missing or malformed `Authorization` header | | 401 | `invalid_key` | Key revoked or unknown | | 402 | `insufficient_credits` | Balance is empty — top up or upgrade | For OAuth-based agent access (the MCP server supports OAuth 2.1 with PKCE), see [For AI agents](/ai-agents/). Next: [Errors](/errors/) · [Rate limits](/rate-limits/) --- # Errors > The error envelope, every error code, and how to handle retries with request_id. Errors use a consistent envelope with a machine-readable code: ```json { "error": { "code": "not_found", "message": "No public video found at that URL. It may be private or deleted.", "status": 404 }, "request_id": "req_01JZX4M8Q2TE9W" } ``` Always log `request_id` — quote it in support requests and we can trace the exact call. ## Error codes | Status | Code | When | | --- | --- | --- | | 400 | `invalid_request` | Missing/malformed `url`, `handle`, or `query` | | 401 | `unauthorized` / `invalid_key` | Bad or missing API key | | 402 | `insufficient_credits` | Credit balance too low for the call | | 403 | `forbidden` | Key lacks access to the endpoint | | 404 | `not_found` | Content is private, deleted, or the URL resolves to nothing | | 409 | `job_conflict` | Duplicate async job submission | | 422 | `unprocessable` | URL parsed but the content type is unsupported (e.g. transcript of an image post) | | 429 | `rate_limited` | Plan rpm exceeded — honor `Retry-After` | | 5xx | `upstream_error` / `internal` | Transient fetch failure — safe to retry | ## Retry guidance ```python import httpx, time for attempt in range(3): r = httpx.get(url, headers=headers, timeout=120) if r.status_code == 429: time.sleep(int(r.headers.get("Retry-After", "2"))) continue if r.status_code >= 500: time.sleep(2 ** attempt) continue break ``` Failed calls that return an error **are not charged** — credits are only deducted when data comes back. Next: [Rate limits](/rate-limits/) · [Output formats](/output-formats/) --- # Rate limits > Requests-per-minute limits by plan, the headers that report them, and how to back off cleanly. Limits are per account, in requests per minute: | Plan | Limit | | --- | --- | | Free trial | 20 rpm | | Monthly ($5) | 200 rpm | | Annual ($54) | 300 rpm | | Enterprise | up to 1,500 rpm | Every response reports where you stand: ``` RateLimit-Limit: 200 RateLimit-Remaining: 187 RateLimit-Reset: 42 ``` ## When you hit the limit You get `429 rate_limited` with a `Retry-After` header (seconds). Back off and retry: ```js async function withBackoff(fn) { for (let attempt = 0; attempt < 5; attempt++) { const resp = await fn(); if (resp.status !== 429) return resp; const wait = Number(resp.headers.get("Retry-After") ?? 2 ** attempt); await new Promise((r) => setTimeout(r, wait * 1000)); } throw new Error("rate limit retries exhausted"); } ``` ## Tips - Rate limits count **requests**, not credits — one list call returning 50 comments is one request. - Large pulls belong in [async jobs](/async-jobs/), which run server-side and don't fight your rpm budget. - Need more than 300 rpm? [Contact us](https://scrapersocial.com/contact/) about volume plans. Next: [Pagination](/pagination/) · [Async jobs](/async-jobs/) --- # Pagination > Cursor pagination on list endpoints — limit, meta.cursor, has_more, and per-item billing. List endpoints (comments, channel posts/videos, tweets, search) paginate with an opaque cursor: ```bash curl "https://api.scrapersocial.com/v1/instagram/comments?url=https://www.instagram.com/reel/C8xQvZ2sVAb/&limit=50" \ -H "Authorization: Bearer sk_live_..." ``` ```json { "data": [{ "id": "c_9021", "text": "This fixed it for me, thank you" }], "meta": { "count": 50, "limit": 50, "cursor": "eyJvZmZzZXQiOjUwfQ", "has_more": true }, "request_id": "req_01JZX4M8Q2TE9W" } ``` Pass `meta.cursor` back as `?cursor=` to get the next page. Cursors are opaque and short-lived — don't store them long-term. ## Walking every page ```python import httpx params = {"url": post_url, "limit": 50} items = [] while True: r = httpx.get("https://api.scrapersocial.com/v1/instagram/comments", params=params, headers=headers, timeout=120).json() items += r["data"] if not r["meta"]["has_more"]: break params["cursor"] = r["meta"]["cursor"] ``` ## Billing and limits - Credits are charged **per item returned**: a page of 50 comments at 3 credits each is 150 credits. - `limit` maxes out at 50 for synchronous calls. For more per request, use [async jobs](/async-jobs/) — request `limit` above 50 and the API returns a job. - Page 1 of a list is cached more briefly than deeper pages, so fresh content shows up quickly. See [Caching & freshness](/caching-and-freshness/). Next: [Async jobs](/async-jobs/) · [Field projection](/field-projection/) --- # Output formats > JSON by default, plus csv for list data and text for transcripts — pick with the format parameter. Every endpoint returns JSON by default. Two alternate formats cover the common "I just want the file" cases: | `format` | Works on | Returns | | --- | --- | --- | | `json` (default) | everything | The standard `{ data, meta?, request_id }` envelope | | `csv` | list endpoints | Flattened rows, one item per line, UTF-8 | | `text` | transcript endpoints | The plain transcript, no JSON wrapper | ## CSV for spreadsheets ```bash curl "https://api.scrapersocial.com/v1/tiktok/comments?url=...&limit=50&format=csv" \ -H "Authorization: Bearer sk_live_..." -o comments.csv ``` ```csv id,text,author_handle,likes,posted_at c_9021,"This fixed it for me, thank you",dev.casey,412,2026-07-01T09:12:00Z ``` Nested objects are flattened with underscores (`author.handle` → `author_handle`). ## Plain text for LLM prompts ```bash curl "https://api.scrapersocial.com/v1/tiktok/transcript?url=...&format=text" \ -H "Authorization: Bearer sk_live_..." ``` Returns only the spoken words — ready to paste into a prompt without JSON parsing. Credits are identical across formats. Next: [Field projection](/field-projection/) · [Pagination](/pagination/) --- # Field projection > Trim responses to only the fields you need with the fields parameter — smaller payloads, same credits. Pass `fields` with a comma-separated list of paths to receive only those keys: ```bash curl "https://api.scrapersocial.com/v1/tiktok/stats?url=...&fields=metrics.views,metrics.likes,posted_at" \ -H "Authorization: Bearer sk_live_..." ``` ```json { "data": { "posted_at": "2026-06-30T14:05:00Z", "metrics": { "views": 1204593, "likes": 88410 } }, "request_id": "req_01JZX4M8Q2TE9W" } ``` ## Rules - Dot notation addresses nested keys: `author.handle`, `metrics.views`. - On list endpoints the projection applies to every item in `data`. - Unknown fields are ignored silently (no error) so projections survive schema additions. - `request_id` and `meta` are always included. - Credits are unchanged — projection saves bandwidth and tokens, not billing. ## Why bother? LLM pipelines: a full stats payload is ~40 lines; a projected one is 5. Across a thousand items in a context window, that's the difference between fitting and truncating: ```python r = httpx.get(f"{BASE}/v1/instagram/channel-posts", params={"handle": "instagram", "limit": 50, "fields": "caption,metrics.likes,posted_at"}, headers=H) ``` Next: [Output formats](/output-formats/) · [Caching & freshness](/caching-and-freshness/) --- # Async jobs > Large list pulls run as server-side jobs — submit, poll or get a webhook, then download the results. Synchronous list calls cap at `limit=50`. Ask for more and the API converts the call into a job: ```bash curl "https://api.scrapersocial.com/v1/tiktok/comments?url=...&limit=500" \ -H "Authorization: Bearer sk_live_..." ``` ```json { "data": { "job_id": "job_01JZY0P3XQ", "status": "queued", "poll_url": "https://api.scrapersocial.com/v1/jobs/job_01JZY0P3XQ" }, "request_id": "req_01JZX4M8Q2TE9W" } ``` The response is `202 Accepted`. Credits are charged when results are delivered, per item actually returned. ## Poll for completion ```bash curl "https://api.scrapersocial.com/v1/jobs/job_01JZY0P3XQ" \ -H "Authorization: Bearer sk_live_..." ``` Status moves `queued → running → succeeded | failed`. On success the job payload includes `result_url` (and inline `data` for small results). `GET /v1/jobs` lists your recent jobs. ## Or get a webhook instead Register a [webhook](/webhooks-guide/) for `job.completed` and skip polling: ```json { "type": "job.completed", "data": { "job_id": "job_01JZY0P3XQ", "status": "succeeded", "items": 500 } } ``` Verify the `x-scrapersocial-signature` header before trusting the payload — snippet in the [webhooks guide](/webhooks-guide/). Next: [Webhooks](/webhooks-guide/) · [Jobs API reference](/api/jobs/) --- # Webhooks > Receive job completions and account events at your endpoint, and verify the x-scrapersocial-signature header. Webhooks push events to your HTTPS endpoint so you don't poll. Manage them via the [dashboard](https://scrapersocial.com/app/webhooks) or the [Webhooks API](/api/webhooks/): ```bash curl -X POST "https://api.scrapersocial.com/v1/webhooks" \ -H "Authorization: Bearer sk_live_..." \ -H "Content-Type: application/json" \ -d '{"url": "https://example.com/hooks/scrapersocial", "events": ["job.completed", "credits.low"]}' ``` The response includes a signing secret (`whsec_...`) — shown once, store it safely. ## Events | Event | Fires when | | --- | --- | | `job.completed` | An async job succeeds or fails | | `credits.low` | Balance crosses your low-credit threshold | | `plan.changed` | Subscription upgraded, downgraded, or canceled | ## Verify signatures Every delivery carries `x-scrapersocial-signature: t=,v1=` — an HMAC-SHA256 of `"{t}.{raw_body}"` with your `whsec_` secret: ```js export function verify(rawBody, header, secret) { const { t, v1 } = Object.fromEntries(header.split(",").map((p) => p.split("="))); if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false; // 5 min tolerance const expected = crypto .createHmac("sha256", secret) .update(`${t}.${rawBody}`) .digest("hex"); return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1)); } ``` Compute the HMAC over the **raw** request body (before JSON parsing), and reject timestamps older than a few minutes to block replays. ## Delivery behavior Deliveries expect a 2xx within 10 seconds; anything else is retried with exponential backoff for up to 24 hours. Events include an `id` — treat processing as idempotent. Next: [Verify-webhook recipe](/recipes/verify-webhook/) · [Async jobs](/async-jobs/) --- # Caching & freshness > How the response cache works — the x-cache header, fresh=true, and what each cache class means. Responses arrive in seconds; popular content returns in milliseconds because we cache aggressively. Caching is visible and controllable: - Every response carries `x-cache: hit` or `x-cache: miss`. - Cache hits cost the **same credits** as fresh fetches — the difference is latency. - Add `fresh=true` to any call to bypass the cache at the same price. ```bash curl -sD - "https://api.scrapersocial.com/v1/twitter/stats?url=..." \ -H "Authorization: Bearer sk_live_..." -o /dev/null | grep -i x-cache # x-cache: hit ``` ## Cache classes Each endpoint has a freshness class, shown on its reference page: | Class | Behavior | Typical endpoints | | --- | --- | --- | | Cached forever | Content that never changes | Transcripts, AI summaries | | Cached 7 days | Media metadata (resolved media URLs expire within 1 hour) | Downloads | | Cached 12 hours | Slow-moving profile data | Channel/profile/company stats | | Cached 3 hours | Recent-content lists (page 1 refreshes hourly) | Channel posts, timelines | | Age-aware | Fresher content refreshes faster (15 min floor, 7 day max) | Post stats, comments, search | "Age-aware" scales with how old the content is: stats for a video posted an hour ago refresh every few minutes; stats for a two-year-old video refresh daily. ## When to force freshness ```python r = httpx.get(f"{BASE}/v1/tiktok/stats", params={"url": video_url, "fresh": "true"}, headers=H) ``` Use `fresh=true` for point-in-time measurements (end-of-campaign reporting, billing reconciliations). For dashboards and monitoring, the default freshness windows are almost always the better trade. Next: [Errors](/errors/) · [Rate limits](/rate-limits/) --- # For AI agents > Connect agents via the MCP server at mcp.scrapersocial.com, or wrap endpoints as function-calling tools. ScraperSocial is built to be consumed by agents: transcripts and summaries are first-class, responses are compact JSON with stable keys, and every capability is exposed as a tool. ## Option 1 — MCP server (recommended) One config block gives any MCP client the full surface: ```json { "mcpServers": { "scrapersocial": { "url": "https://mcp.scrapersocial.com", "headers": { "Authorization": "Bearer sk_live_..." } } } } ``` OAuth 2.1 (PKCE) is also supported — clients like claude.ai can connect with no key at all. In Claude Code: ```bash claude mcp add --transport http scrapersocial https://mcp.scrapersocial.com ``` ### Tools exposed `get_transcript`, `get_summary`, `get_post_stats`, `get_comments`, `get_profile`, `get_channel_posts`, `search_tiktok`, `search_instagram_reels`. Each takes a URL or handle (plus `platform` where ambiguous) and returns the same envelope as REST. ## Option 2 — function-calling tools Wrap any endpoint in your framework of choice: ```python def get_tiktok_transcript(url: str) -> dict: """Transcript of a public TikTok video, with timestamped segments.""" r = httpx.get("https://api.scrapersocial.com/v1/tiktok/transcript", params={"url": url}, headers={"Authorization": f"Bearer {KEY}"}, timeout=120) r.raise_for_status() return r.json()["data"] ``` Tips that make agents behave: - Use [`format=text`](/output-formats/) for transcripts headed straight into a prompt. - Use [`fields=`](/field-projection/) projections to keep tool results token-cheap. - Surface `error.message` to the model — messages are written to be self-correcting ("It may be private or deleted"). ## Discovery endpoints Agents can find everything without a human: - [`/llms.txt`](https://scrapersocial.com/llms.txt) and [`/llms-full.txt`](https://scrapersocial.com/llms-full.txt) - [`/openapi.json`](https://scrapersocial.com/openapi.json) (OpenAPI 3.1) - [`/.well-known/mcp/server-card.json`](https://scrapersocial.com/.well-known/mcp/server-card.json) Next: [Recipes](/recipes/transcribe-a-channel/) · [MCP marketing page](https://scrapersocial.com/mcp/) --- # TikTok API > Reference for all nine TikTok endpoints — transcript, summary, stats, comments, channel data, search and download. Nine endpoints under `/v1/tiktok/*`. All are `GET`, authenticated with `Authorization: Bearer sk_live_...`, and return the standard `{ data, meta?, request_id }` envelope. TikTok is the richest surface: transcripts, summaries, stats, comments, channel data, keyword and hashtag search, and downloads. ### GET /v1/tiktok/transcript Full transcript of a TikTok video with timestamped segments. Credits: 8 per video. Caching: Cached forever. Input: ?url= Sample response: ```json { "data": { "platform": "tiktok", "entity_id": "7231338487075638570", "language": "en", "source": "subtitles", "text": "Today I want to show you the three settings everyone ignores when they set this up for the first time...", "segments": [ { "start": 0, "end": 3.2, "text": "Today I want to show you the three settings" }, { "start": 3.2, "end": 5.9, "text": "everyone ignores when they set this up" } ] }, "request_id": "req_01JZX4M8Q2TE9W" } ``` ### GET /v1/tiktok/summary Short AI summary of a TikTok video, built on its transcript. Credits: 10 per video. Caching: Cached forever. Input: ?url= Sample response: ```json { "data": { "platform": "tiktok", "entity_id": "7231338487075638570", "language": "en", "summary": "A quick tutorial covering three commonly missed configuration steps, with a before/after comparison and a call to check the pinned comment for the full guide." }, "request_id": "req_01JZX4M8Q2TE9W" } ``` ### GET /v1/tiktok/stats Views, likes, comments and shares for a single TikTok video. Credits: 3 per video. Caching: Age-aware cache (15 min - 7 days). Input: ?url= Sample response: ```json { "data": { "platform": "tiktok", "entity_id": "7231338487075638570", "url": "https://www.tiktok.com/@tiktok/video/7231338487075638570", "author": { "handle": "tiktok", "name": "Example Creator" }, "posted_at": "2026-06-30T14:05:00Z", "metrics": { "views": 1204593, "likes": 88410, "comments": 2314, "shares": 10092 } }, "request_id": "req_01JZX4M8Q2TE9W" } ``` ### GET /v1/tiktok/comments Paginated comments on a TikTok video; charged per comment returned. Credits: 2 per comment. Caching: Age-aware cache (15 min - 7 days). Paginated (cursor/limit; per-item billing). Input: ?url= Sample response: ```json { "data": [ { "id": "c_9021", "text": "This fixed it for me, thank you", "author": { "handle": "dev.casey" }, "likes": 412, "posted_at": "2026-07-01T09:12:00Z" }, { "id": "c_9022", "text": "Can you do a follow-up on the advanced settings?", "author": { "handle": "mariana.codes" }, "likes": 96, "posted_at": "2026-07-01T10:44:00Z" } ], "meta": { "count": 2, "limit": 50, "cursor": "eyJvZmZzZXQiOjUwfQ", "has_more": true }, "request_id": "req_01JZX4M8Q2TE9W" } ``` ### GET /v1/tiktok/channel-stats Follower counts and profile metadata for a TikTok account. Credits: 3 per profile. Caching: Cached 12 hours. Input: ?handle= Sample response: ```json { "data": { "platform": "tiktok", "handle": "tiktok", "name": "Example Creator", "bio": "Tutorials and behind-the-scenes. New video every Tuesday.", "verified": true, "metrics": { "followers": 2450318, "following": 132, "posts": 842 } }, "request_id": "req_01JZX4M8Q2TE9W" } ``` ### GET /v1/tiktok/channel-videos Latest videos from a TikTok account with per-video metrics. Credits: 3 per video. Caching: Cached 3 hours (page 1: 1 hour). Paginated (cursor/limit; per-item billing). Input: ?handle= Sample response: ```json { "data": [ { "id": "7231338487075638570", "url": "https://www.tiktok.com/@tiktok/video/7231338487075638570", "caption": "Behind the scenes of our new feature", "posted_at": "2026-07-02T18:00:00Z", "metrics": { "views": 1204593, "likes": 88410, "comments": 2314 } }, { "id": "72313384870756385701", "url": "https://www.tiktok.com/@tiktok/video/7231338487075638570", "caption": "Behind the scenes of our new feature", "posted_at": "2026-07-02T18:00:00Z", "metrics": { "views": 1204593, "likes": 88410, "comments": 2314 } } ], "meta": { "count": 2, "limit": 20, "cursor": "eyJwYWdlIjoyfQ", "has_more": true, "item": "video" }, "request_id": "req_01JZX4M8Q2TE9W" } ``` ### GET /v1/tiktok/search Keyword search over TikTok videos. Credits: 4 per result. Caching: Age-aware cache (15 min - 7 days). Paginated (cursor/limit; per-item billing). Input: ?query= Sample response: ```json { "data": [ { "id": "v_77120", "url": "https://example-result-url", "caption": "How I plan a week of posts in 30 minutes", "author": { "handle": "plannerpro" }, "metrics": { "views": 88210, "likes": 5120 }, "posted_at": "2026-07-08T16:20:00Z" }, { "id": "v_771201", "url": "https://example-result-url", "caption": "How I plan a week of posts in 30 minutes", "author": { "handle": "plannerpro" }, "metrics": { "views": 88210, "likes": 5120 }, "posted_at": "2026-07-08T16:20:00Z" } ], "meta": { "count": 2, "limit": 20, "cursor": "eyJwYWdlIjoyfQ", "has_more": true, "item": "result" }, "request_id": "req_01JZX4M8Q2TE9W" } ``` ### GET /v1/tiktok/hashtag-search Videos posted under a TikTok hashtag. Credits: 3 per result. Caching: Age-aware cache (15 min - 7 days). Paginated (cursor/limit; per-item billing). Input: ?query= Sample response: ```json { "data": [ { "id": "v_77120", "url": "https://example-result-url", "caption": "How I plan a week of posts in 30 minutes", "author": { "handle": "plannerpro" }, "metrics": { "views": 88210, "likes": 5120 }, "posted_at": "2026-07-08T16:20:00Z" }, { "id": "v_771201", "url": "https://example-result-url", "caption": "How I plan a week of posts in 30 minutes", "author": { "handle": "plannerpro" }, "metrics": { "views": 88210, "likes": 5120 }, "posted_at": "2026-07-08T16:20:00Z" } ], "meta": { "count": 2, "limit": 20, "cursor": "eyJwYWdlIjoyfQ", "has_more": true, "item": "result" }, "request_id": "req_01JZX4M8Q2TE9W" } ``` ### GET /v1/tiktok/download Resolves a downloadable media URL for a TikTok video (URL expires within 1h). Credits: 7 per video. Caching: Cached 7 days (media URLs max 1h). Input: ?url= Sample response: ```json { "data": { "platform": "tiktok", "entity_id": "7231338487075638570", "media_url": "https://cdn.example/video/master.mp4?sig=...", "mime_type": "video/mp4", "expires_at": "2026-07-15T13:00:00Z", "warning": "media_url is short-lived (max 1h) — download promptly or re-request." }, "request_id": "req_01JZX4M8Q2TE9W" } ``` Related: [Instagram API](/api/instagram/) · [Pagination](/pagination/) · [Caching & freshness](/caching-and-freshness/) --- # Instagram API > Reference for all nine Instagram endpoints — reel transcripts, summaries, stats, comments, profiles, reels and search. Nine endpoints under `/v1/instagram/*`. All are `GET`, authenticated with `Authorization: Bearer sk_live_...`, and return the standard `{ data, meta?, request_id }` envelope. Reel transcription is audio-based, so it costs more credits than TikTok's subtitle-backed transcripts. ### GET /v1/instagram/transcript Full transcript of an Instagram reel with timestamped segments. Credits: 40 per reel. Caching: Cached forever. Input: ?url= Sample response: ```json { "data": { "platform": "instagram", "entity_id": "C8xQvZ2sVAb", "language": "en", "source": "audio", "text": "Today I want to show you the three settings everyone ignores when they set this up for the first time...", "segments": [ { "start": 0, "end": 3.2, "text": "Today I want to show you the three settings" }, { "start": 3.2, "end": 5.9, "text": "everyone ignores when they set this up" } ] }, "request_id": "req_01JZX4M8Q2TE9W" } ``` ### GET /v1/instagram/summary Short AI summary of an Instagram reel, built on its transcript. Credits: 42 per reel. Caching: Cached forever. Input: ?url= Sample response: ```json { "data": { "platform": "instagram", "entity_id": "C8xQvZ2sVAb", "language": "en", "summary": "A quick tutorial covering three commonly missed configuration steps, with a before/after comparison and a call to check the pinned comment for the full guide." }, "request_id": "req_01JZX4M8Q2TE9W" } ``` ### GET /v1/instagram/stats Likes, comments and view counts for an Instagram post or reel. Credits: 1 per post. Caching: Age-aware cache (15 min - 7 days). Input: ?url= Sample response: ```json { "data": { "platform": "instagram", "entity_id": "C8xQvZ2sVAb", "url": "https://www.instagram.com/reel/C8xQvZ2sVAb/", "author": { "handle": "instagram", "name": "Example Creator" }, "posted_at": "2026-06-30T14:05:00Z", "metrics": { "views": 1204593, "likes": 88410, "comments": 2314, "shares": 10092 } }, "request_id": "req_01JZX4M8Q2TE9W" } ``` ### GET /v1/instagram/comments Paginated comments on an Instagram post; charged per comment returned. Credits: 3 per comment. Caching: Age-aware cache (15 min - 7 days). Paginated (cursor/limit; per-item billing). Input: ?url= Sample response: ```json { "data": [ { "id": "c_9021", "text": "This fixed it for me, thank you", "author": { "handle": "dev.casey" }, "likes": 412, "posted_at": "2026-07-01T09:12:00Z" }, { "id": "c_9022", "text": "Can you do a follow-up on the advanced settings?", "author": { "handle": "mariana.codes" }, "likes": 96, "posted_at": "2026-07-01T10:44:00Z" } ], "meta": { "count": 2, "limit": 50, "cursor": "eyJvZmZzZXQiOjUwfQ", "has_more": true }, "request_id": "req_01JZX4M8Q2TE9W" } ``` ### GET /v1/instagram/channel-stats Follower counts, bio and profile metadata for an Instagram account. Credits: 3 per profile. Caching: Cached 12 hours. Input: ?handle= Sample response: ```json { "data": { "platform": "instagram", "handle": "instagram", "name": "Example Creator", "bio": "Tutorials and behind-the-scenes. New video every Tuesday.", "verified": true, "metrics": { "followers": 2450318, "following": 132, "posts": 842 } }, "request_id": "req_01JZX4M8Q2TE9W" } ``` ### GET /v1/instagram/channel-posts Latest posts from an Instagram account with per-post metrics. Credits: 2 per post. Caching: Cached 3 hours (page 1: 1 hour). Paginated (cursor/limit; per-item billing). Input: ?handle= Sample response: ```json { "data": [ { "id": "C8xQvZ2sVAb", "url": "https://www.instagram.com/reel/C8xQvZ2sVAb/", "caption": "Three settings you should change today", "posted_at": "2026-07-03T12:00:00Z", "metrics": { "likes": 55210, "comments": 981 } }, { "id": "C8xQvZ2sVAb1", "url": "https://www.instagram.com/reel/C8xQvZ2sVAb/", "caption": "Three settings you should change today", "posted_at": "2026-07-03T12:00:00Z", "metrics": { "likes": 55210, "comments": 981 } } ], "meta": { "count": 2, "limit": 20, "cursor": "eyJwYWdlIjoyfQ", "has_more": true, "item": "post" }, "request_id": "req_01JZX4M8Q2TE9W" } ``` ### GET /v1/instagram/channel-reels Latest reels from an Instagram account with per-reel metrics. Credits: 3 per reel. Caching: Cached 3 hours (page 1: 1 hour). Paginated (cursor/limit; per-item billing). Input: ?handle= Sample response: ```json { "data": [ { "id": "C8xQvZ2sVAb", "url": "https://www.instagram.com/reel/C8xQvZ2sVAb/", "caption": "POV: your setup finally works", "posted_at": "2026-07-05T15:30:00Z", "metrics": { "views": 421009, "likes": 38112, "comments": 640 } }, { "id": "C8xQvZ2sVAb1", "url": "https://www.instagram.com/reel/C8xQvZ2sVAb/", "caption": "POV: your setup finally works", "posted_at": "2026-07-05T15:30:00Z", "metrics": { "views": 421009, "likes": 38112, "comments": 640 } } ], "meta": { "count": 2, "limit": 20, "cursor": "eyJwYWdlIjoyfQ", "has_more": true, "item": "reel" }, "request_id": "req_01JZX4M8Q2TE9W" } ``` ### GET /v1/instagram/reels-search Keyword search over Instagram reels. Credits: 3 per result. Caching: Age-aware cache (15 min - 7 days). Paginated (cursor/limit; per-item billing). Input: ?query= Sample response: ```json { "data": [ { "id": "v_77120", "url": "https://example-result-url", "caption": "How I plan a week of posts in 30 minutes", "author": { "handle": "plannerpro" }, "metrics": { "views": 88210, "likes": 5120 }, "posted_at": "2026-07-08T16:20:00Z" }, { "id": "v_771201", "url": "https://example-result-url", "caption": "How I plan a week of posts in 30 minutes", "author": { "handle": "plannerpro" }, "metrics": { "views": 88210, "likes": 5120 }, "posted_at": "2026-07-08T16:20:00Z" } ], "meta": { "count": 2, "limit": 20, "cursor": "eyJwYWdlIjoyfQ", "has_more": true, "item": "result" }, "request_id": "req_01JZX4M8Q2TE9W" } ``` ### GET /v1/instagram/download Resolves a downloadable media URL for an Instagram reel (URL expires within 1h). Credits: 40 per video. Caching: Cached 7 days (media URLs max 1h). Input: ?url= Sample response: ```json { "data": { "platform": "instagram", "entity_id": "C8xQvZ2sVAb", "media_url": "https://cdn.example/video/master.mp4?sig=...", "mime_type": "video/mp4", "expires_at": "2026-07-15T13:00:00Z", "warning": "media_url is short-lived (max 1h) — download promptly or re-request." }, "request_id": "req_01JZX4M8Q2TE9W" } ``` Related: [TikTok API](/api/tiktok/) · [Facebook API](/api/facebook/) · [Output formats](/output-formats/) --- # Facebook API > Reference for all six Facebook endpoints — video transcripts, summaries, post stats, comments, page stats and downloads. Six endpoints under `/v1/facebook/*`. All are `GET`, authenticated with `Authorization: Bearer sk_live_...`, and return the standard `{ data, meta?, request_id }` envelope. Public pages, posts, and Watch videos only — groups and private profiles are out of scope by design. ### GET /v1/facebook/transcript Full transcript of a Facebook video with timestamped segments. Credits: 15 per video. Caching: Cached forever. Input: ?url= Sample response: ```json { "data": { "platform": "facebook", "entity_id": "1093831344793765", "language": "en", "source": "audio", "text": "Today I want to show you the three settings everyone ignores when they set this up for the first time...", "segments": [ { "start": 0, "end": 3.2, "text": "Today I want to show you the three settings" }, { "start": 3.2, "end": 5.9, "text": "everyone ignores when they set this up" } ] }, "request_id": "req_01JZX4M8Q2TE9W" } ``` ### GET /v1/facebook/summary Short AI summary of a Facebook video, built on its transcript. Credits: 17 per video. Caching: Cached forever. Input: ?url= Sample response: ```json { "data": { "platform": "facebook", "entity_id": "1093831344793765", "language": "en", "summary": "A quick tutorial covering three commonly missed configuration steps, with a before/after comparison and a call to check the pinned comment for the full guide." }, "request_id": "req_01JZX4M8Q2TE9W" } ``` ### GET /v1/facebook/stats Reactions, comments and shares for a Facebook post or video. Credits: 5 per post. Caching: Age-aware cache (15 min - 7 days). Input: ?url= Sample response: ```json { "data": { "platform": "facebook", "entity_id": "1093831344793765", "url": "https://www.facebook.com/watch/?v=1093831344793765", "author": { "handle": "facebook", "name": "Example Creator" }, "posted_at": "2026-06-30T14:05:00Z", "metrics": { "views": 1204593, "likes": 88410, "comments": 2314, "shares": 10092 } }, "request_id": "req_01JZX4M8Q2TE9W" } ``` ### GET /v1/facebook/comments Paginated comments on a Facebook post; charged per comment returned. Credits: 3 per comment. Caching: Age-aware cache (15 min - 7 days). Paginated (cursor/limit; per-item billing). Input: ?url= Sample response: ```json { "data": [ { "id": "c_9021", "text": "This fixed it for me, thank you", "author": { "handle": "dev.casey" }, "likes": 412, "posted_at": "2026-07-01T09:12:00Z" }, { "id": "c_9022", "text": "Can you do a follow-up on the advanced settings?", "author": { "handle": "mariana.codes" }, "likes": 96, "posted_at": "2026-07-01T10:44:00Z" } ], "meta": { "count": 2, "limit": 50, "cursor": "eyJvZmZzZXQiOjUwfQ", "has_more": true }, "request_id": "req_01JZX4M8Q2TE9W" } ``` ### GET /v1/facebook/page-stats Followers, likes and metadata for a Facebook page. Credits: 10 per page. Caching: Cached 12 hours. Input: ?url= or ?handle= Sample response: ```json { "data": { "platform": "facebook", "handle": "facebook", "name": "Example Creator", "bio": "Tutorials and behind-the-scenes. New video every Tuesday.", "verified": true, "metrics": { "followers": 2450318, "following": 132, "posts": 842 } }, "request_id": "req_01JZX4M8Q2TE9W" } ``` ### GET /v1/facebook/download Resolves a downloadable media URL for a Facebook video (URL expires within 1h). Credits: 10 per video. Caching: Cached 7 days (media URLs max 1h). Input: ?url= Sample response: ```json { "data": { "platform": "facebook", "entity_id": "1093831344793765", "media_url": "https://cdn.example/video/master.mp4?sig=...", "mime_type": "video/mp4", "expires_at": "2026-07-15T13:00:00Z", "warning": "media_url is short-lived (max 1h) — download promptly or re-request." }, "request_id": "req_01JZX4M8Q2TE9W" } ``` Related: [Instagram API](/api/instagram/) · [X/Twitter API](/api/twitter/) · [Errors](/errors/) --- # X/Twitter API > Reference for all five X/Twitter endpoints — video transcripts, summaries, post stats, profiles and timelines. Five endpoints under `/v1/twitter/*` (the path uses `twitter`; `x.com` and `twitter.com` URLs are both accepted as input). All are `GET`, authenticated with `Authorization: Bearer sk_live_...`, and return the standard `{ data, meta?, request_id }` envelope. ### GET /v1/twitter/transcript Full transcript of a video attached to a post on X. Credits: 7 per video. Caching: Cached forever. Input: ?url= Sample response: ```json { "data": { "platform": "twitter", "entity_id": "1861122334455667788", "language": "en", "source": "audio", "text": "Today I want to show you the three settings everyone ignores when they set this up for the first time...", "segments": [ { "start": 0, "end": 3.2, "text": "Today I want to show you the three settings" }, { "start": 3.2, "end": 5.9, "text": "everyone ignores when they set this up" } ] }, "request_id": "req_01JZX4M8Q2TE9W" } ``` ### GET /v1/twitter/summary Short AI summary of a video posted on X, built on its transcript. Credits: 9 per video. Caching: Cached forever. Input: ?url= Sample response: ```json { "data": { "platform": "twitter", "entity_id": "1861122334455667788", "language": "en", "summary": "A quick tutorial covering three commonly missed configuration steps, with a before/after comparison and a call to check the pinned comment for the full guide." }, "request_id": "req_01JZX4M8Q2TE9W" } ``` ### GET /v1/twitter/stats Views, likes, reposts and replies for a single post on X. Credits: 1 per tweet. Caching: Age-aware cache (15 min - 7 days). Input: ?url= Sample response: ```json { "data": { "platform": "twitter", "entity_id": "1861122334455667788", "url": "https://x.com/XDevelopers/status/1861122334455667788", "author": { "handle": "XDevelopers", "name": "Example Creator" }, "posted_at": "2026-06-30T14:05:00Z", "metrics": { "views": 1204593, "likes": 88410, "comments": 2314, "shares": 10092 } }, "request_id": "req_01JZX4M8Q2TE9W" } ``` ### GET /v1/twitter/profile Follower counts, bio and profile metadata for an account on X. Credits: 5 per lookup. Caching: Cached 12 hours. Input: ?handle= Sample response: ```json { "data": { "platform": "twitter", "handle": "XDevelopers", "name": "Example Creator", "bio": "Tutorials and behind-the-scenes. New video every Tuesday.", "verified": true, "metrics": { "followers": 2450318, "following": 132, "posts": 842 } }, "request_id": "req_01JZX4M8Q2TE9W" } ``` ### GET /v1/twitter/tweets Latest posts from an account on X with per-post metrics. Credits: 1 per tweet. Caching: Cached 3 hours (page 1: 1 hour). Paginated (cursor/limit; per-item billing). Input: ?handle= Sample response: ```json { "data": [ { "id": "1861122334455667788", "url": "https://x.com/XDevelopers/status/1861122334455667788", "text": "Shipping notes: cursor pagination is now live on all list endpoints.", "posted_at": "2026-07-06T08:15:00Z", "metrics": { "views": 90211, "likes": 1211, "reposts": 204, "replies": 88 } }, { "id": "18611223344556677881", "url": "https://x.com/XDevelopers/status/1861122334455667788", "text": "Shipping notes: cursor pagination is now live on all list endpoints.", "posted_at": "2026-07-06T08:15:00Z", "metrics": { "views": 90211, "likes": 1211, "reposts": 204, "replies": 88 } } ], "meta": { "count": 2, "limit": 20, "cursor": "eyJwYWdlIjoyfQ", "has_more": true, "item": "tweet" }, "request_id": "req_01JZX4M8Q2TE9W" } ``` Related: [LinkedIn API](/api/linkedin/) · [Rate limits](/rate-limits/) · [Field projection](/field-projection/) --- # LinkedIn API > Reference for all five LinkedIn endpoints — post stats, comments, member profiles, companies and company posts. Five endpoints under `/v1/linkedin/*`. All are `GET`, authenticated with `Authorization: Bearer sk_live_...`, and return the standard `{ data, meta?, request_id }` envelope. LinkedIn data is premium-priced, covers public pages and posts only, and profile responses are retained in cache for at most 30 days. Transcript and summary endpoints for LinkedIn video are planned for v2. ### GET /v1/linkedin/stats Reactions, comments and reposts for a LinkedIn post. Credits: 5 per post. Caching: Age-aware cache (15 min - 7 days). Input: ?url= Sample response: ```json { "data": { "platform": "linkedin", "entity_id": "7181111111111111111", "url": "https://www.linkedin.com/posts/linkedin_hiring-activity-7181111111111111111", "author": { "handle": "linkedin", "name": "Example Creator" }, "posted_at": "2026-06-30T14:05:00Z", "metrics": { "views": 1204593, "likes": 88410, "comments": 2314, "shares": 10092 } }, "request_id": "req_01JZX4M8Q2TE9W" } ``` ### GET /v1/linkedin/comments Paginated comments on a LinkedIn post; charged per comment returned. Credits: 5 per comment. Caching: Age-aware cache (15 min - 7 days). Paginated (cursor/limit; per-item billing). Input: ?url= Sample response: ```json { "data": [ { "id": "c_9021", "text": "This fixed it for me, thank you", "author": { "handle": "dev.casey" }, "likes": 412, "posted_at": "2026-07-01T09:12:00Z" }, { "id": "c_9022", "text": "Can you do a follow-up on the advanced settings?", "author": { "handle": "mariana.codes" }, "likes": 96, "posted_at": "2026-07-01T10:44:00Z" } ], "meta": { "count": 2, "limit": 50, "cursor": "eyJvZmZzZXQiOjUwfQ", "has_more": true }, "request_id": "req_01JZX4M8Q2TE9W" } ``` ### GET /v1/linkedin/profile Public profile data for a LinkedIn member: headline, positions, education. Credits: 10 per profile (25 with include_email=true). Caching: Cached 12 hours. Input: ?url= or ?handle= Sample response: ```json { "data": { "platform": "linkedin", "handle": "example-person", "name": "Example Person", "headline": "Head of Growth at Example Co", "location": "Austin, Texas, United States", "positions": [ { "title": "Head of Growth", "company": "Example Co", "start": "2024-03", "end": null } ], "metrics": { "followers": 18240, "connections": "500+" } }, "request_id": "req_01JZX4M8Q2TE9W" } ``` ### GET /v1/linkedin/company Company page data: industry, size, followers and description. Credits: 10 per company. Caching: Cached 12 hours. Input: ?url= or ?handle= Sample response: ```json { "data": { "platform": "linkedin", "handle": "example-co", "name": "Example Co", "industry": "Software Development", "employees_range": "201-500", "headquarters": "Austin, Texas", "metrics": { "followers": 96210 } }, "request_id": "req_01JZX4M8Q2TE9W" } ``` ### GET /v1/linkedin/company-posts Latest posts from a LinkedIn company page with per-post metrics. Credits: 5 per post. Caching: Cached 3 hours (page 1: 1 hour). Paginated (cursor/limit; per-item billing). Input: ?url= or ?handle= Sample response: ```json { "data": [ { "id": "7181111111111111111", "url": "https://www.linkedin.com/posts/linkedin_hiring-activity-7181111111111111111", "text": "We are hiring across engineering and support.", "posted_at": "2026-07-04T11:00:00Z", "metrics": { "reactions": 842, "comments": 63, "reposts": 41 } }, { "id": "71811111111111111111", "url": "https://www.linkedin.com/posts/linkedin_hiring-activity-7181111111111111111", "text": "We are hiring across engineering and support.", "posted_at": "2026-07-04T11:00:00Z", "metrics": { "reactions": 842, "comments": 63, "reposts": 41 } } ], "meta": { "count": 2, "limit": 20, "cursor": "eyJwYWdlIjoyfQ", "has_more": true, "item": "post" }, "request_id": "req_01JZX4M8Q2TE9W" } ``` Related: [X/Twitter API](/api/twitter/) · [Trademark & affiliation](/legal/trademark-and-affiliation/) · [Caching & freshness](/caching-and-freshness/) --- # Jobs API > Reference for /v1/jobs — list jobs, fetch status and results for large asynchronous pulls. Async jobs are created automatically when a list call requests `limit > 50` (see [Async jobs](/async-jobs/)). These endpoints manage them. ## `GET /v1/jobs` Lists your recent jobs, newest first. Paginated with `cursor`/`limit`. ```bash curl "https://api.scrapersocial.com/v1/jobs?limit=10" \ -H "Authorization: Bearer sk_live_..." ``` ```json { "data": [ { "job_id": "job_01JZY0P3XQ", "endpoint": "/v1/tiktok/comments", "status": "succeeded", "items": 500, "created_at": "2026-07-14T18:20:11Z", "completed_at": "2026-07-14T18:21:37Z" } ], "meta": { "count": 1, "has_more": false }, "request_id": "req_01JZX4M8Q2TE9W" } ``` ## `GET /v1/jobs/{job_id}` Returns one job. While running, `status` is `queued` or `running`. On success the payload adds `result_url` — a signed URL to the full result set (JSON lines): ```json { "data": { "job_id": "job_01JZY0P3XQ", "status": "succeeded", "items": 500, "credits_charged": 1000, "result_url": "https://api.scrapersocial.com/v1/jobs/job_01JZY0P3XQ/result" }, "request_id": "req_01JZX4M8Q2TE9W" } ``` Failed jobs include `error.code`/`error.message` and charge nothing. Prefer webhooks over polling for long jobs — register for `job.completed` in the [Webhooks API](/api/webhooks/). --- # Webhooks API > Reference for /v1/webhooks — create, list and delete webhook endpoints and their signing secrets. Programmatic management of webhook endpoints. Concepts and signature verification live in the [webhooks guide](/webhooks-guide/). ## `POST /v1/webhooks` ```bash curl -X POST "https://api.scrapersocial.com/v1/webhooks" \ -H "Authorization: Bearer sk_live_..." \ -H "Content-Type: application/json" \ -d '{"url": "https://example.com/hooks/ss", "events": ["job.completed"]}' ``` ```json { "data": { "webhook_id": "wh_01JZ...", "url": "https://example.com/hooks/ss", "events": ["job.completed"], "secret": "whsec_..." }, "request_id": "req_01JZX4M8Q2TE9W" } ``` `secret` is returned **once** — store it for signature verification. | Field | Type | Notes | | --- | --- | --- | | `url` | string, required | HTTPS only | | `events` | string[], required | Any of `job.completed`, `credits.low`, `plan.changed` | ## `GET /v1/webhooks` Lists registered endpoints (secrets are never returned again). ## `DELETE /v1/webhooks/{webhook_id}` Removes the endpoint; in-flight deliveries finish, no new ones are queued. Returns `204 No Content`. Related: [Webhooks guide](/webhooks-guide/) · [Verify-webhook recipe](/recipes/verify-webhook/) --- # Account API > Reference for /v1/me and /v1/usage — plan, credit balance, rate limit, and usage history. Two read-only endpoints for programmatic account introspection. ## `GET /v1/me` The account behind the key — handy for CI checks and dashboards: ```bash curl "https://api.scrapersocial.com/v1/me" \ -H "Authorization: Bearer sk_live_..." ``` ```json { "data": { "account_id": "acc_01J...", "email": "dev@example.com", "plan": "annual", "credits_remaining": 11310, "credits_quota": 12000, "renews_at": "2027-07-01T00:00:00Z", "rate_limit_rpm": 300 }, "request_id": "req_01JZX4M8Q2TE9W" } ``` ## `GET /v1/usage` Usage records, newest first, paginated with `cursor`/`limit`. Filter with `from`/`to` (ISO dates) and `endpoint`: ```bash curl "https://api.scrapersocial.com/v1/usage?from=2026-07-01&endpoint=/v1/tiktok/transcript" \ -H "Authorization: Bearer sk_live_..." ``` ```json { "data": [ { "request_id": "req_01JZX4M8Q2TE9W", "endpoint": "/v1/tiktok/transcript", "credits": 8, "cache": "miss", "status": 200, "created_at": "2026-07-14T18:20:11Z" } ], "meta": { "count": 1, "has_more": false }, "request_id": "req_01JZX51AB3CD4E" } ``` Every metered response also carries live headers: `X-Credits-Charged` and `X-Credits-Remaining`, so most integrations never need to poll `/v1/usage`. Related: [Authentication](/authentication/) · [Rate limits](/rate-limits/) --- # Recipe: transcribe a channel > List every video from a TikTok account and transcribe each one — pagination loop, credit math, and caching notes. Goal: the full transcript set of a creator's recent videos, ready for search or an LLM. Two endpoints: `channel-videos` to enumerate, `transcript` per video. ## The loop ```python import httpx BASE = "https://api.scrapersocial.com/v1" H = {"Authorization": "Bearer sk_live_..."} def channel_videos(handle: str, max_items: int = 100): params = {"handle": handle, "limit": 50} while max_items > 0: r = httpx.get(f"{BASE}/tiktok/channel-videos", params=params, headers=H, timeout=120).json() for v in r["data"][:max_items]: yield v max_items -= len(r["data"]) if not r["meta"]["has_more"]: return params["cursor"] = r["meta"]["cursor"] transcripts = {} for video in channel_videos("tiktok", max_items=50): t = httpx.get(f"{BASE}/tiktok/transcript", params={"url": video["url"]}, headers=H, timeout=120) if t.status_code == 422: # image post / no speech continue t.raise_for_status() transcripts[video["id"]] = t.json()["data"]["text"] ``` ## Credit math 50 videos listed (3 credits each) + 50 transcripts (8 credits each) = **550 credits** — about $2.75 at the monthly rate, $2.48 on annual. ## Notes - Transcripts are cached forever, so re-running the loop is fast for already-seen videos (same credits, millisecond responses). - Prefer `format=text` if you're piping straight into a prompt. - For Instagram, swap `tiktok/channel-videos` for `instagram/channel-reels` and expect higher transcript pricing (audio pipeline). Related: [Pagination](/pagination/) · [Content repurposing use case](https://scrapersocial.com/use-cases/content-repurposing/) --- # Recipe: social listening pipeline > A daily brand-mention sweep — search TikTok and Instagram, pull comments on hits, and score sentiment. Goal: a daily job that finds new posts mentioning your brand, collects their comments, and hands the text to a sentiment model. ## 1. Find mentions ```python import httpx BASE = "https://api.scrapersocial.com/v1" H = {"Authorization": "Bearer sk_live_..."} BRAND = "acme widgets" hits = [] for path in ("tiktok/search", "instagram/reels-search"): r = httpx.get(f"{BASE}/{path}", params={"query": BRAND, "limit": 25}, headers=H, timeout=120).json() hits += [(path.split("/")[0], item) for item in r["data"]] ``` ## 2. Pull comments on anything that spiked ```python def comments(platform: str, url: str, pages: int = 2): params = {"url": url, "limit": 50} for _ in range(pages): r = httpx.get(f"{BASE}/{platform}/comments", params=params, headers=H, timeout=120).json() yield from r["data"] if not r["meta"]["has_more"]: return params["cursor"] = r["meta"]["cursor"] mentions = [] for platform, item in hits: if item["metrics"]["views"] < 10_000: continue for c in comments(platform, item["url"]): mentions.append({"post": item["url"], "text": c["text"], "likes": c["likes"], "at": c["posted_at"]}) ``` ## 3. Score and store Feed `mentions` to your sentiment model of choice and upsert into your warehouse keyed on comment `id` — comment lists are age-aware cached, so re-polls are cheap on latency and idempotent in your store. ## Cost model 25 + 25 search results (4 + 3 credits each) + ~10 posts × 100 comments (2–3 credits each) ≈ **2,400 credits/day** worst case. Run it on X and LinkedIn too by adding their `stats`/`comments` endpoints — same request shape. Related: [Async jobs](/async-jobs/) for bigger pulls · [Social listening use case](https://scrapersocial.com/use-cases/social-listening/) --- # Recipe: verify a webhook > Verify x-scrapersocial-signature in Node and Python before trusting any webhook payload. Never process a webhook without verifying `x-scrapersocial-signature`. The header is `t=,v1=` where `v1` is HMAC-SHA256 of `"{t}.{raw_body}"` using your `whsec_` secret. ## Node (Express) ```js const app = express(); app.post("/hooks/ss", express.raw({ type: "application/json" }), (req, res) => { const header = req.header("x-scrapersocial-signature") ?? ""; const parts = Object.fromEntries(header.split(",").map((p) => p.split("="))); const { t, v1 } = parts; if (!t || !v1 || Math.abs(Date.now() / 1000 - Number(t)) > 300) { return res.status(400).send("stale or malformed signature"); } const expected = crypto .createHmac("sha256", process.env.SS_WEBHOOK_SECRET) .update(`${t}.${req.body}`) .digest("hex"); if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1))) { return res.status(401).send("bad signature"); } const event = JSON.parse(req.body); // handle event.type: job.completed | credits.low | plan.changed res.sendStatus(200); }); ``` ## Python (FastAPI) ```python import hashlib, hmac, json, os, time from fastapi import FastAPI, Header, HTTPException, Request app = FastAPI() SECRET = os.environ["SS_WEBHOOK_SECRET"].encode() @app.post("/hooks/ss") async def hook(request: Request, x_scrapersocial_signature: str = Header("")): raw = await request.body() parts = dict(p.split("=") for p in x_scrapersocial_signature.split(",") if "=" in p) t, v1 = parts.get("t", ""), parts.get("v1", "") if not t or abs(time.time() - int(t)) > 300: raise HTTPException(400, "stale signature") expected = hmac.new(SECRET, f"{t}.".encode() + raw, hashlib.sha256).hexdigest() if not hmac.compare_digest(expected, v1): raise HTTPException(401, "bad signature") event = json.loads(raw) return {"ok": True} ``` ## Checklist - Compute over the **raw** body, before any JSON parsing or re-serialization. - Use a constant-time comparison (`timingSafeEqual` / `compare_digest`). - Reject timestamps older than ~5 minutes to block replays. - Respond 2xx fast; do slow work async — deliveries retry on non-2xx. Related: [Webhooks guide](/webhooks-guide/) · [Webhooks API](/api/webhooks/) --- # Trademark & affiliation > ScraperSocial is not affiliated with TikTok, Instagram, Facebook, X, or LinkedIn, and accesses publicly available data only. ScraperSocial is not affiliated with, endorsed by, or sponsored by TikTok, Instagram, Facebook, X, or LinkedIn. TikTok is a registered trademark of TikTok Pte. Ltd. Instagram and Facebook are registered trademarks of Meta Platforms, Inc. X is a registered trademark of X Corp. LinkedIn is a registered trademark of LinkedIn Corporation. All trademarks are the property of their respective owners and are used here only to identify the platforms whose publicly available content the service can access. ScraperSocial accesses publicly available data through automated tools and exposes it via a stable REST API. Specifically: - We only access content that is visible in a normal browser **without logging in**. - We never access private accounts, direct messages, or any data behind authentication. - We do not claim to be an official API of any platform, and nothing on this site should be read as implying partnership or endorsement. - Cached copies of LinkedIn profile data are retained for a maximum of 30 days. - We honor removal requests for cached content — email [support@scrapersocial.com](mailto:support@scrapersocial.com) with the URL(s). Questions about acceptable use of the data you retrieve are ultimately governed by the laws that apply to you and by each platform's terms as they apply to your own conduct. When in doubt, talk to your counsel. Related: [Contact](https://scrapersocial.com/contact/) · [Caching & freshness](/caching-and-freshness/) ---