# SocialCrawl API: Full Reference Last updated: 2026-11-01 ## Base URL: https://www.socialcrawl.dev/v1 ## Authentication Every request carries your key in the `x-api-key` header (or as `Authorization: Bearer `; `x-api-key` wins when both are sent): ``` curl https://www.socialcrawl.dev/v1/tiktok/profile?handle=charlidamelio \ -H "x-api-key: YOUR_API_KEY" ``` ## Response Format A typical success response follows this envelope: ```json { "success": true, "platform": "tiktok", "endpoint": "/v1/tiktok/profile", "data": { ... }, "credits_used": 1, "credits_remaining": 4999, "request_id": "req-XXXXX", "cached": false } ``` On billed responses, `credits_remaining` and `X-Credits-Remaining` are authoritative. Free cache hits return `credits_remaining: null` and omit the header because they do not establish an authoritative post-request balance. Idempotent replays return the current balance when their lookup succeeds; otherwise they return `credits_remaining: null` and omit the header. Routes that explicitly model an unavailable balance, including endpoint-parameter validation and best-effort metadata, omit both fields when their balance read fails. ## Request Headers Optional headers accepted on every `/v1/*` request: - `Cache-Control: no-cache`: bypass the response cache and force a live fetch. Billed at the normal endpoint rate; the fresh result is written back so the next call is a free hit. Only the `no-cache` directive triggers it (`no-store` alone does not); matching is case-insensitive. - `Idempotency-Key: `: reserve a request atomically per account before billing (5-minute crash lease; completed outcome TTL 24 hours). Concurrent retries return `409 IDEMPOTENCY_IN_PROGRESS` with a `Retry-After` of 1 second while the original is young, growing to a quarter of the time it has been running (at most 30); claim-store failures return an unbilled 503 rather than executing without protection. A replay keeps the cached payload immutable except for billing metadata: body `credits_used` is 0 and `idempotent_replay` is true. A known current balance is refreshed in body `credits_remaining` and the `X-Credits-Remaining` header; no balance row resolves to 0. On a transient balance lookup failure, body `credits_remaining` is null and `X-Credits-Remaining` is omitted. Takes precedence over `Cache-Control: no-cache`. GET Server-Sent Event requests cannot be replayed: use `Accept: application/json` when sending an idempotency key. The unsupported key-plus-SSE combination returns an unbilled `400 INVALID_REQUEST`. POST batch SSE keeps its documented sync-equivalent replay behavior. ## Caching & Freshness Successful responses are cached and shared across your account (the key is the endpoint plus its sorted query parameters, not your API key). A cache hit costs 0 credits and returns `cached: true` / `X-Cache: HIT`; a miss is billed at the endpoint rate and returns `X-Cache: MISS`. Freshness windows by data type: profiles ~15 min, posts ~10 min, comments ~5 min, search ~2 min, analytics ~30 min. After the window expires the next call is a live miss that re-warms the entry. Send `Cache-Control: no-cache` to force a live fetch for a single call. --- ## Account: Meta Endpoints Account metadata (balance, usage). These endpoints cost 0 credits and never touch upstream platforms. ### GET /credits/balance Check your current credit balance and recent deduction count. The body also carries the top-up offer: `runway` (`days_left` is whole days at the last 14 days' pace, null with no recent use), `recommended_pack`, `top_up_url`, `checkout_url` and `auto_recharge`. These five are left out when they cannot be read in time, so treat them as optional. If `runway.days_left` is under 3, show the account owner `checkout_url`. Credit cost: 0 (metadata call) Response: ```json { "success": true, "platform": "meta", "endpoint": "/v1/credits/balance", "data": { "balance": 4999, "recent_deductions": 12, "runway": { "window_days": 14, "credits_per_day": 666, "days_left": 2 }, "recommended_pack": { "id": "growth", "name": "Growth", "credits": 20000, "price_gbp": 49, "price_display": "£49", "days_at_current_burn": 30, "credits_per_day": 666, "burn_window_days": 14 }, "top_up_url": "https://www.socialcrawl.dev/dashboard/billing/payments?pack=growth", "checkout_url": "https://www.socialcrawl.dev/dashboard/billing/checkout?token=...", "auto_recharge": { "enabled": false, "armed": false, "threshold_credits": null, "pack_id": null, "has_card": false, "disabled_reason": null, "blocked_reason": null, "setup_url": "..." } }, "credits_used": 0, "credits_remaining": 4999, "request_id": "req-XXXXX", "cached": false } ``` ``` curl "https://www.socialcrawl.dev/v1/credits/balance" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /credits/transactions List your credit ledger (dispute-grade receipts), newest first. Deductions are negative, refunds positive. Cursor-paginated (limit defaults to 50, capped at 100) and filterable by request_id. A call that walks pages (`max_pages`, `seen`, or a row filter such as `country`) is billed page by page: look up each id in its `data.walk.request_ids`, since the walk's own request_id has no receipt. Credit cost: 0 (metadata call) Query params: limit, cursor, request_id (all optional) Response: ```json { "success": true, "platform": "meta", "endpoint": "/v1/credits/transactions", "data": { "items": [ { "id": "tx_XXXXX", "type": "deduction", "amount": -20, "balance_after": 4980, "description": "search/everywhere", "endpoint": "/v1/search/everywhere", "platform": "search", "credit_tier": "standard", "request_id": "req-XXXXX", "created_at": "2026-07-03T00:00:00.000Z" } ], "next_cursor": null }, "credits_used": 0, "credits_remaining": 4980, "request_id": "req-XXXXX", "cached": false } ``` ``` curl "https://www.socialcrawl.dev/v1/credits/transactions?limit=50" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /credits/usage Credits and requests your account used over a date range, grouped by API key, UTC day and endpoint in any combination (one row per key per day by default). Use it to bill clients per key or to see which endpoints your credits go to. Any of the account's keys can read the whole account; keys are named by name and prefix, never the key itself. `requests` counts the calls you sent; a composite such as `prism/discover` bills the lanes it calls on their own rows, so by endpoint those credits appear under each lane with 0 requests. Counted from the request log, which keeps 90 days, so a range covers at most 92 days. Credit cost: 0 (metadata call) Query params: from, to (YYYY-MM-DD, UTC, inclusive; default the last 30 days), group_by (any of api_key, day and endpoint, comma-separated; default api_key,day), api_key_id (all optional) ``` curl "https://www.socialcrawl.dev/v1/credits/usage?from=2026-10-01&to=2026-10-21&group_by=api_key,day" \ -H "x-api-key: YOUR_API_KEY" ``` Spend per endpoint for one key, e.g. `{ "endpoint": "/v1/tiktok/profile", "requests": 120, "credits_used": 120 }`: ``` curl "https://www.socialcrawl.dev/v1/credits/usage?group_by=endpoint&api_key_id=KEY_ID" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /keys/current The API key you call with: its credit limit, what it has used against the limit and what is left, the account balance, `spendable` (what this key can spend right now: the smaller of its remaining limit and the balance) and its last 30 days of requests and credits. A key with no limit has null `credit_limit`, `credits_used` and `credits_remaining`. Limits are set in Dashboard -> API Keys. Every response made with a limited key also carries `X-Key-Credits-Remaining`, the credits it may still spend after that call, so a script can stop before it is refused. Credit cost: 0 (metadata call) Response: ```json { "success": true, "platform": "meta", "endpoint": "/v1/keys/current", "data": { "id": "key_XXXXX", "name": "client-acme", "key_prefix": "sc_live_ab", "status": "active", "created_at": "2026-09-01T10:00:00Z", "last_used_at": "2026-10-23T18:30:00Z", "expires_at": null, "credit_limit": 500, "credits_used": 120, "credits_remaining": 380, "account_balance": 10000, "spendable": 380, "last_30_days": { "from": "2026-09-25", "to": "2026-10-24", "requests": 40, "credits_used": 300 } }, "credits_used": 0, "credits_remaining": 10000, "request_id": "req-XXXXX", "cached": false } ``` ``` curl "https://www.socialcrawl.dev/v1/keys/current" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /notifications What needs your attention on the account, newest first: a monitor whose webhook was paused, a failed Discover run, a call refused for credits, a low balance, a fixed defect, a new release. Every item has a plain `title` and `message` and one concrete `next_step` (a call to make or a page to open), so you do not need to know the kinds. A repeat folds into one item (`occurrences`) and surfaces as unread again. `kind` is one or more of job.failed, job.paused, dataset.ready, credits.refused, credits.low, defect.fixed, release.published (comma-separated); `unread=true` returns unread items only. Page with `pagination.next_cursor`, sent back as `cursor`. An empty inbox answers 200 with `data.empty_reason_note`. Poll once a minute or less. Credit cost: 0 (metadata call) Query params: unread, kind, limit (1 to 100, default 20), cursor (all optional) ``` curl "https://www.socialcrawl.dev/v1/notifications?unread=true&limit=20" \ -H "x-api-key: YOUR_API_KEY" ``` ### POST /notifications/read Marks notifications read: the ids you name (`{"ids": ["ntf_..."]}`, 1 to 100) or all of them (`{"all": true}`). Send exactly one of the two. Ids of another account are ignored. `data.updated` counts the items that were unread; `data.unread_count` is what is left. Credit cost: 0 (metadata call) ``` curl -X POST "https://www.socialcrawl.dev/v1/notifications/read" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"ids": ["ntf_..."]}' ``` ### GET /status Check which platforms have a problem right now, plus the upstream-version mix. The answer comes from the status page engine (real traffic plus our own probes, refreshed every minute); a platform with a problem is listed as `degraded` or `down`, with its failing endpoints, and the open incidents are under `incidents`. If the status page data is more than 5 minutes old, the answer falls back to the circuit breaker and the last 30 minutes of calls. Public route: no API key, no credit cost, no upstream call. Call it before retrying a platform that keeps returning 502/503: a `degraded` or `down` platform will not recover by retrying sooner. Credit cost: 0 (public metadata call, no API key required) Response: ```json { "status": "degraded", "platforms": { "youtube": { "status": "down", "endpoints": [{ "method": "GET", "path": "/v1/youtube/channel", "status": "down" }] } }, "upstreamVersions": { "v1": 0, "v2": 0, "v3": 0 }, "updated_at": "2026-10-20T12:00:00.000Z", "incidents": [ { "id": "k3v9w2m8q1zt", "title": "Elevated errors on YouTube", "status": "investigating", "impact": "major", "url": "https://www.socialcrawl.dev/status/incidents/k3v9w2m8q1zt", "components": ["platform:youtube", "endpoint:GET youtube/channel"] } ], "status_page": "https://www.socialcrawl.dev/status" } ``` ``` curl "https://www.socialcrawl.dev/v1/status" ``` ### GET /status/{platform} One platform in detail: its state, every active endpoint with its state and 90 daily cells (UTC days: colour, uptime %, request availability, weighted downtime minutes, incident ids), and the open incidents touching it. States are `operational`, `degraded_performance`, `partial_outage`, `major_outage`, `under_maintenance` and `no_data` (no recent traffic). Public: no API key, 0 credits. An unknown platform is a `404`; if status data is unavailable the answer is a `503` with `Retry-After: 60`, never a guessed state. ```json { "platform": { "slug": "youtube", "name": "YouTube", "status": "major_outage", "since": "2026-10-20T11:00:00.000Z", "uptime_90d": 99.871, "days": ["..."] }, "endpoints": [ { "method": "GET", "path": "/v1/youtube/channel", "name": "GET /v1/youtube/channel", "status": "major_outage", "since": "2026-10-20T11:00:00.000Z", "uptime_90d": 99.6, "days": [{ "day": "2026-10-20", "color": "red", "uptime": 95.833, "request_availability": 0.91, "downtime_minutes": 60, "incident_ids": ["k3v9w2m8q1zt"] }] } ], "incidents": [{ "id": "k3v9w2m8q1zt", "title": "Elevated errors on YouTube", "status": "investigating", "impact": "major", "url": "...", "started_at": "2026-10-20T11:00:00.000Z", "components": ["platform:youtube"] }], "updated_at": "2026-10-20T12:00:00.000Z", "stale": false, "status_page": "https://www.socialcrawl.dev/status" } ``` ``` curl "https://www.socialcrawl.dev/v1/status/youtube" ``` #### Status page feeds The status page at https://www.socialcrawl.dev/status publishes the same data in standard formats, all public and CORS-enabled: - `https://www.socialcrawl.dev/status/api/v2/summary.json`: Statuspage v2 format (also `status.json`, `components.json`, `incidents.json`, `incidents/unresolved.json`, `scheduled-maintenances.json`, `scheduled-maintenances/upcoming.json`, `scheduled-maintenances/active.json`, for example `https://www.socialcrawl.dev/status/api/v2/incidents/unresolved.json`), so StatusGator, IsDown and Statuspage clients read it unchanged - `https://www.socialcrawl.dev/status/history.rss` and `https://www.socialcrawl.dev/status/history.atom`: the 25 most recent incidents - `https://www.socialcrawl.dev/status/badge/{component}.svg`: a status badge for `overall`, `api` or a platform slug --- ## Cohorts: Audience-Filtered Mention Search Ask which of YOUR OWN list of public social identities posted about your keywords. Upload up to 10,000 platform-qualified public identities once, then submit bounded keyword queries against their recent authored activity. Asynchronous: submit returns `202` with a poll URL. Supported platforms: bluesky, instagram, kwai, linkedin, threads, tiktok, truth-social, twitch, twitter, youtube. SocialCrawl never receives or stores the data you used to build the panel: only the public identity and your own opaque `external_id`, both encrypted at rest. This is NOT audience discovery: you bring the list. Credits: every lifecycle call costs 0. A query reserves a worst-case ceiling on submission, charges 1 credit per successful upstream page (LinkedIn 5), and refunds the unspent remainder exactly once when it reaches a terminal state. ### POST /cohorts Create a cohort. Requires an `Idempotency-Key` UUID header. Body: `name` (optional, ≤120 chars), `retention_days` (7-90, default 30). Credit cost: 0 ``` curl -X POST "https://www.socialcrawl.dev/v1/cohorts" \ -H "x-api-key: YOUR_API_KEY" \ -H "Idempotency-Key: 11111111-1111-4111-8111-111111111111" \ -H "content-type: application/json" \ -d '{"name":"August purchaser panel","retention_days":30}' ``` ### PUT /cohorts/{cohortId}/members Upsert one chunk of 1-1,000 members; a cohort holds at most 10,000. Re-sending the same `external_id` updates its identity without growing the count. Requires an `Idempotency-Key` UUID header. Credit cost: 0 ``` curl -X PUT "https://www.socialcrawl.dev/v1/cohorts/{cohortId}/members" \ -H "x-api-key: YOUR_API_KEY" \ -H "Idempotency-Key: 22222222-2222-4222-8222-222222222222" \ -H "content-type: application/json" \ -d '{"members":[{"external_id":"buyer_01983","platform":"instagram","handle":"natgeo"}]}' ``` ### GET /cohorts/{cohortId} Read cohort metadata and counts. Never enumerates stored identities. Credit cost: 0 ### DELETE /cohorts/{cohortId} Delete the cohort and cascade its members, queries, and results. Returns `204`. Credit-ledger receipts are never deleted. Credit cost: 0 ### POST /cohorts/{cohortId}/queries Submit an asynchronous keyword query. Returns `202` with `query_id`, `status_url`, `result_url`, and `reserved_credits`. Body: `keywords` (1-20), `date_from` (full RFC3339 timestamp), optional `date_to`, `max_pages_per_identity` (1-20), `max_items_per_identity` (1-1000), `max_credits` (your safety limit), optional `platforms` subset. Requires an `Idempotency-Key` UUID header. Credit cost: metered, 1 credit per successful upstream page (LinkedIn 5), reserved at the ceiling and refunded down to actual. ``` curl -X POST "https://www.socialcrawl.dev/v1/cohorts/{cohortId}/queries" \ -H "x-api-key: YOUR_API_KEY" \ -H "Idempotency-Key: 33333333-3333-4333-8333-333333333333" \ -H "content-type: application/json" \ -d '{"keywords":["acme","acme pro"],"date_from":"2026-08-01T00:00:00.000Z","max_pages_per_identity":3,"max_items_per_identity":100,"max_credits":12000}' ``` ### GET /cohort-queries/{queryId} Poll status, durable progress counters, and billing. `status` is one of `queued`, `running`, `succeeded`, `failed`, `cancelled`, `expired`. A query can be `succeeded` with partial failures: check `progress.pages_failed` and per-member `coverage` before treating results as exhaustive. Credit cost: 0 ### DELETE /cohort-queries/{queryId} Cancel a query. Returns `204`. Pages already fetched stay billable; the unspent reservation is refunded exactly once. Credit cost: 0 ### GET /cohort-queries/{queryId}/results Cursor-paginated matches plus per-member coverage. Query params: `cursor`, `limit` (default 100, max 500). Matching is deterministic. Unicode NFKC, case folding, whitespace collapse, then whole-word matching at Unicode word boundaries (normalized keywords are echoed back; YouTube/Twitch match title plus description), with no stemming, fuzzy matching, or semantic expansion. `coverage[].window_complete` is true only when the route reached the requested time boundary or an end-of-feed marker; a spent page budget leaves it false. Credit cost: 0 ``` curl "https://www.socialcrawl.dev/v1/cohort-queries/{queryId}/results?limit=100" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## Monitors: Scheduled Watches with Signed Webhooks Re-run any GET endpoint or Prism composite on a cadence and POST each result to your webhook, signed. `recipe` is any endpoint path without the `/v1/` prefix. The batch endpoints that read a JSON body (`prism/post-stats`, `prism/comment-lookup`, `prism/profiles`, `youtube/transcripts`, `youtube/videos`, `youtube/channels`) are POST and cannot be monitored: call those directly. One hour is the shortest cadence. A cron firing more often is accepted, flagged, and then throttled to the one-hour floor. This is the only unattended recurring meter in the catalogue: creating a monitor is free, and then every run bills the recipe's own cost plus 1 credit for orchestration until you pause or delete it. There is no platform-push feed behind this: no social platform exposes one to us. A monitor is scheduled polling that we run and you receive. ### POST /monitors Create a monitor. Body: `recipe` (required), `params` (the recipe's own parameters, frozen at create time), `cadence` (`hourly`/`daily`/`weekly`/`{cron}`, required), `webhook_url` (https, public host, required unless `track` is set), optional `name`, `alert_rules`, `suppress_webhook_unless_alert`, `webhook_secret`, `webhook_format` (`full` or `rows`), `webhook_rows` (`all`, the default, or `new`), `track`, `output_schema`. The first run is at `next_run_at`, the next slot of the cadence. The signing secret is returned exactly once, here. `track` makes a numbers-only monitor: `{ metrics: [1 to 20 paths], row_key?, max_rows? (default 100, max 200) }`. Paths read the unified response: `items[].post.engagement.views` is one number per row, `author.followers` one number off the page. Each run stores those numbers per row plus the change against the previous run and nothing else; the full response is delivered to the webhook but not stored. With `track`, `webhook_url` is optional and `webhook_format: "rows"` sends flat `{ t, row_id, metric, value, prev, abs_change, pct_change }` rows for a spreadsheet. Active-monitor limit by highest credit pack purchased: Free 3, Starter 10, Growth 25, Pro 100, Enterprise 500 (403 `MONITOR_LIMIT_REACHED`). `webhook_rows: "new"` sends only the rows the monitor has never seen (a row that comes back after dropping off the page is not sent again), which is how a monitor tells you about the new posts or comments of one account or post you already know; it needs `track` with an `items[].` metric and a `webhook_url`. Monitors re-run one read of one account or post, so for a list of accounts use a discover schedule ([docs](https://www.socialcrawl.dev/docs/prism/discover#schedules)). Details: [Only the new rows](https://www.socialcrawl.dev/docs/webhooks#only-the-new-rows). Credit cost: 0 to create; each run bills the recipe cost plus 1. ``` curl -X POST "https://www.socialcrawl.dev/v1/monitors" \ -H "x-api-key: YOUR_API_KEY" \ -H "content-type: application/json" \ -d '{"recipe":"prism/comments","params":{"url":"https://www.instagram.com/p/DAbc123","max":100},"cadence":"hourly","webhook_url":"https://example.com/hooks/socialcrawl"}' ``` ### GET /monitors List your monitors, newest first. Query params: `status` (`active`/`paused`/`all`), `cursor`, `limit` (default 20, max 100). Credit cost: 0 ### GET /monitors/{id} Read one monitor. A monitor owned by another account returns `404`, never `403`. The signing secret is never returned here. Credit cost: 0 ### PATCH /monitors/{id} Pause or resume: `{"status":"paused"}` or `{"status":"active"}`. A paused monitor is not scheduled and bills nothing. Nothing else is editable: change a recipe by creating a new monitor. Credit cost: 0 ### DELETE /monitors/{id} Unschedule and delete the monitor with its run history and webhook. Returns `204`. Credit-ledger receipts are never deleted. Credit cost: 0 ### GET /monitors/{id}/runs Run history, newest first, each with `credits_used`, the per-leg envelope, `alerts_fired`, and `webhook_delivery`. Query params: `status` (`ok`/`partial`/`failed`/`skipped`), `from`, `to`, `cursor`, `limit`, and `include=result` to inline each stored result (`include=numbers` for a numbers-only run's `numbers` and `deltas`; `include=result,numbers` for both). A `skipped` run carries `skip_reason` and was never charged. Credit cost: 0 ### GET /monitors/{id}/timeseries Every stored run projected into `{t, metrics}` over the recipe stable numeric keys, so a monitor doubles as a metric history you do not have to store. Query params: `metric` (comma-separated keys), `from`, `to`, and `row` (numbers-only monitors: comma-separated row ids). A numbers-only monitor also returns `series`: one `{ row_id, metric, points: [{t, value}] }` per row and metric. Reads snapshots only and never calls an upstream. Credit cost: 0 ``` curl "https://www.socialcrawl.dev/v1/monitors/{id}/timeseries" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /monitors/{id}/export Download the stored numbers as CSV (default) or NDJSON (`format=ndjson`), one line per run, row and metric: `t,row_id,metric,value,abs_change,pct_change`. Query params: `format`, `from`, `to`. A monitor that stores full results exports the numeric fields `/timeseries` finds, under row id `_`. Reads at most the newest 1,000 runs in the window. Credit cost: 0 --- ## Aliexpress ### GET /aliexpress/product Get an AliExpress product by id or URL (oneOf: product_id|url) Credit cost: 5 (advanced) Parameters: - product_id (optional, string): AliExpress product id, the numeric id in a product URL: aliexpress.com/item/3256812847028710.html is product id 3256812847028710. Product ids also come back on every row of GET /v1/aliexpress/search and GET /v1/aliexpress/search/hot. - url (optional, string): Full AliExpress product URL, for example https://www.aliexpress.com/item/3256812847028710.html. The product id is extracted from the path. - country (optional, string): Ship-to country as a two-letter code. Default US. Changes displayed price and availability. A product that exists for US can return empty for another country. - currency (optional, string): ISO 4217 currency for displayed prices. Default USD. Live-validated: USD GBP CAD EUR UAH MXN TRY RUB BRL AUD INR JPY IDR SEK KRW ILS THB CLP VND. - language (optional, string): Copy language as an ISO code. Default EN. Live-validated: EN RU PT ES FR ID IT TH JA AR VI TR DE HE KO NL PL MX CL IN. - Constraint: one of product_id, url (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/aliexpress/product" \ --data-urlencode "product_id=3256812847028710" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /aliexpress/search Search AliExpress products Credit cost: 5 (advanced) Parameters: - query (required): Search keywords, for example xiaomi or wireless earbuds. - page (optional, integer): Page number, starting at 1. Prefer the universal cursor parameter. - page_size (optional, integer): Products per page, 1 to 50. Default 10. - sort_by (optional, enum: price_low | price_high | most_sold | least_sold): Result ordering: price_low, price_high, most_sold, or least_sold. - min_price (optional, string): Lowest price to include, in the smallest unit of the target currency (cents for USD). - max_price (optional, string): Highest price to include, in the smallest unit of the target currency (cents for USD). - category_id (optional, string): AliExpress category id. Category ids come from GET /v1/aliexpress/categories. - delivery_days (optional, integer): Keep products that claim delivery within this many days. - country (optional, string): Ship-to country as a two-letter code. Default US. Changes displayed price and availability. A product that exists for US can return empty for another country. - currency (optional, string): ISO 4217 currency for displayed prices. Default USD. Live-validated: USD GBP CAD EUR UAH MXN TRY RUB BRL AUD INR JPY IDR SEK KRW ILS THB CLP VND. - language (optional, string): Copy language as an ISO code. Default EN. Live-validated: EN RU PT ES FR ID IT TH JA AR VI TR DE HE KO NL PL MX CL IN. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/aliexpress/search" \ --data-urlencode "query=xiaomi" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /aliexpress/product/similar Get similar AliExpress products (oneOf: product_id|url|query) Credit cost: 5 (advanced) Parameters: - product_id (optional, string): AliExpress product id, the numeric id in a product URL: aliexpress.com/item/3256812847028710.html is product id 3256812847028710. Product ids also come back on every row of GET /v1/aliexpress/search and GET /v1/aliexpress/search/hot. - url (optional, string): Full AliExpress product URL, for example https://www.aliexpress.com/item/3256812847028710.html. The product id is extracted from the path. - query (optional, string): Keywords used when no product_id is passed. - page (optional, integer): Page number, starting at 1. Prefer the universal cursor parameter. - page_size (optional, integer): Products per page, 1 to 50. Default 10. - sort_by (optional, enum: price_low | price_high | most_sold | least_sold): Result ordering: price_low, price_high, most_sold, or least_sold. - country (optional, string): Ship-to country as a two-letter code. Default US. Changes displayed price and availability. A product that exists for US can return empty for another country. - currency (optional, string): ISO 4217 currency for displayed prices. Default USD. Live-validated: USD GBP CAD EUR UAH MXN TRY RUB BRL AUD INR JPY IDR SEK KRW ILS THB CLP VND. - language (optional, string): Copy language as an ISO code. Default EN. Live-validated: EN RU PT ES FR ID IT TH JA AR VI TR DE HE KO NL PL MX CL IN. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. - Constraint: one of product_id, url, query (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/aliexpress/product/similar" \ --data-urlencode "product_id=3256812847028710" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /aliexpress/reviews Get AliExpress product reviews (oneOf: product_id|url) Credit cost: 5-9 (metered): 5 credits per page. The default labels (sentiment, issue) are free. label=reports, incentivized or injection holds 4 extra credits and refunds down to 1 credit per started 25 reviews that were newly judged (the first 100 reviews of a page are judged): reviews already labelled are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - product_id (optional, string): AliExpress product id, the numeric id in a product URL: aliexpress.com/item/3256812847028710.html is product id 3256812847028710. Product ids also come back on every row of GET /v1/aliexpress/search and GET /v1/aliexpress/search/hot. - url (optional, string): Full AliExpress product URL, for example https://www.aliexpress.com/item/3256812847028710.html. The product id is extracted from the path. - page (optional, integer): Page number, starting at 1. Prefer the universal cursor parameter. - filter (optional, enum: all | 1 | 2 | 3 | 4 | 5 | image | additional | local): Keep all reviews, only one star value (1-5), only reviews with photos (image), additional comments, or local reviews for country. - sort_by (optional, enum: default | newest): Review ordering: default or newest. - country (optional, string): Reviewer country when filter=local. Default US. - language (optional, string): Copy language as an ISO code. Default EN. Live-validated: EN RU PT ES FR ID IT TH JA AR VI TR DE HE KO NL PL MX CL IN. - label (optional, string): Optional CSV of SocialCrawl labels to add to every review. Without this param every page already carries sentiment and issue, free; label= adds the labels you name to them (the defaults keep running). sentiment and issue are free when asked for too; reports, incentivized and injection add 1 credit per started 25 newly judged reviews. judgments=off (or label=none) turns the default labels off. They read the review's title and text, never its stars. sentiment: how the reviewer feels, on five levels (level 0 to 4, score_0_1, confidence), plus rating_mismatch, true when the words clearly contradict the star rating (a glowing text under 1 star, a furious one under 5), null when the review has no rating. issue: the main problem the review reports (label: product_defect, sizing_or_fit, shipping_or_delivery, customer_service, price_or_value, missing_feature, other, or none, with confidence; label is null when unsure). reports (needs reports=): does this review say that the thing you describe happened (p, 0 to 1). incentivized: does the reviewer say they got the product free, discounted or rewarded for the review (p), and did the text carry a disclosure such as 'in exchange for my honest review', Vine or 체험단 (disclosed). injection flags text that addresses an AI system and tries to direct it (flagged, p); it never drops or rewrites a row. These are signals to read, never a verdict on a review or a reviewer. A review that could not be judged carries labels: null. Reviews already labelled for anyone are free, and so is a cached page. data.labels reports what was judged and billed. - reports (optional, string): Required by label=reports, ignored otherwise. What to look for, in plain words, up to 300 characters, for example reports=the battery drains quickly or reports=배송이 늦음. Every review gains labels.reports.p, the probability that it says so, in any wording or language. Without it label=reports is skipped with the warning label_reports_needs_reports and is not billed. - judgments (optional, enum: on | off): Optional, on (the default) or off. By default every row gains free SocialCrawl judgments (computed.labels, and computed.relevance on search endpoints), reported in data.labels (mode default) and data.relevance (origin default), each with a status (complete, partial or skipped) and pending: the rows still being judged when the page was sent, which carry null now and are filled on your next call or cached read. Default judgments never add credits, never change an existing field, and never drop or reorder a row. off returns the page exactly as before, with none of those keys. label=none does the same. - dry_run (optional, enum: 1): Optional. When 1, return a cost preview for this labelled or relevance-filtered request without fetching the page or judging any row. data.estimate reports rows_expected, rows_cached, label_credits_min, label_credits_max and base_credits. 0 credits charged. - label_evidence (optional, enum: 1): Optional, only with label=. When 1, every labelled row also carries computed.labels_evidence. = { quote, sentence_index }: the sentence in the row that most clearly shows the label, copied verbatim. Absent or null when no single sentence shows it. - fit (optional, enum: goal): Optional. When goal, keep the rows and fields needed for the goal you pass in goal= (plus any that are uncertain, and the first and last), and replace the rest with a stub. data.held_back lists the held ids and a recall id that re-reads the full page from cache at no extra charge. Without this param the page is unchanged. - goal (optional, string): Required by fit=goal. What you are trying to do, in your own words, up to 300 characters. - fit_tokens (optional, integer): Optional, only with fit=goal. Soft cap on how much of the page to keep, in tokens. Uncertain blocks and the first and last block are kept even if they exceed it. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. - Constraint: one of product_id, url (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/aliexpress/reviews" \ --data-urlencode "product_id=1005006079931969" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /aliexpress/product/shipping Get AliExpress shipping for a product SKU (oneOf: product_id|url) Credit cost: 5 (advanced) Parameters: - sku_id (required): SKU id for a colour or size variant. Comes back on every product as sku_id. - product_id (optional, string): AliExpress product id, the numeric id in a product URL: aliexpress.com/item/3256812847028710.html is product id 3256812847028710. Product ids also come back on every row of GET /v1/aliexpress/search and GET /v1/aliexpress/search/hot. - url (optional, string): Full AliExpress product URL, for example https://www.aliexpress.com/item/3256812847028710.html. The product id is extracted from the path. - country (optional, string): Ship-to country as a two-letter code. Default US. Changes displayed price and availability. A product that exists for US can return empty for another country. - currency (optional, string): ISO 4217 currency for displayed prices. Default USD. Live-validated: USD GBP CAD EUR UAH MXN TRY RUB BRL AUD INR JPY IDR SEK KRW ILS THB CLP VND. - language (optional, string): Copy language as an ISO code. Default EN. Live-validated: EN RU PT ES FR ID IT TH JA AR VI TR DE HE KO NL PL MX CL IN. - price (optional, string): Sale price in the target currency, used for free-shipping calculation. - tax_rate (optional, string): Tax rate as a decimal, for example 0.1. Default 0. - Constraint: one of product_id, url (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/aliexpress/product/shipping" \ --data-urlencode "sku_id=12000060082591973" \ --data-urlencode "product_id=3256812847028710" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /aliexpress/search/hot List hot AliExpress products Credit cost: 5 (advanced) Parameters: - query (optional, string): Optional keyword filter, for example xiaomi. - page (optional, integer): Page number, starting at 1. Prefer the universal cursor parameter. - page_size (optional, integer): Products per page, 1 to 50. Default 10. - sort_by (optional, enum: price_low | price_high | most_sold | least_sold): Result ordering: price_low, price_high, most_sold, or least_sold. - min_price (optional, string): Lowest price to include, in the smallest unit of the target currency (cents for USD). - max_price (optional, string): Highest price to include, in the smallest unit of the target currency (cents for USD). - category_id (optional, string): AliExpress category id. Category ids come from GET /v1/aliexpress/categories. - country (optional, string): Ship-to country as a two-letter code. Default US. Changes displayed price and availability. A product that exists for US can return empty for another country. - currency (optional, string): ISO 4217 currency for displayed prices. Default USD. Live-validated: USD GBP CAD EUR UAH MXN TRY RUB BRL AUD INR JPY IDR SEK KRW ILS THB CLP VND. - language (optional, string): Copy language as an ISO code. Default EN. Live-validated: EN RU PT ES FR ID IT TH JA AR VI TR DE HE KO NL PL MX CL IN. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl "https://www.socialcrawl.dev/v1/aliexpress/search/hot" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /aliexpress/search/promo List AliExpress products in a featured promotion Credit cost: 5 (advanced) Parameters: - promotion_name (required): Promotion name exactly as returned by GET /v1/aliexpress/promo, for example New Arrival. - page (optional, integer): Page number, starting at 1. Prefer the universal cursor parameter. - page_size (optional, integer): Products per page, 1 to 50. Default 10. - sort_by (optional, enum: price_low | price_high | most_sold | least_sold): Result ordering: price_low, price_high, most_sold, or least_sold. - category_id (optional, string): Optional category id to narrow the promotion. - country (optional, string): Ship-to country as a two-letter code. Default US. Changes displayed price and availability. A product that exists for US can return empty for another country. - currency (optional, string): ISO 4217 currency for displayed prices. Default USD. Live-validated: USD GBP CAD EUR UAH MXN TRY RUB BRL AUD INR JPY IDR SEK KRW ILS THB CLP VND. - language (optional, string): Copy language as an ISO code. Default EN. Live-validated: EN RU PT ES FR ID IT TH JA AR VI TR DE HE KO NL PL MX CL IN. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/aliexpress/search/promo" \ --data-urlencode "promotion_name=0203-Knasta-202602-PE" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /aliexpress/categories List AliExpress categories Credit cost: 1 (standard) ``` curl "https://www.socialcrawl.dev/v1/aliexpress/categories" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /aliexpress/promo List AliExpress featured promotions Credit cost: 1 (standard) ``` curl "https://www.socialcrawl.dev/v1/aliexpress/promo" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## Amazon ### GET /amazon/shop Get Amazon shop page Credit cost: 1 (standard) Parameters: - url (required): Full URL of the Amazon shop or storefront page ``` curl -G "https://www.socialcrawl.dev/v1/amazon/shop" \ --data-urlencode "url=https://www.amazon.com/shop/alix_earle" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /amazon/product-search Search Amazon products by keyword Credit cost: 1 (standard) Parameters: - query (required): Search keyword or phrase. - country (optional, enum: US | GB | CA | DE | FR | IT | ES | JP | IN | MX | BR | AU | NL): Amazon marketplace as an ISO 3166-1 alpha-2 country code (default US). Supported: US, GB, CA, DE, FR, IT, ES, JP, IN, MX, BR, AU, NL. Non-US marketplaces are best-effort; an occasional timeout is refunded. - depth (optional, integer): Maximum number of products to return in ONE call (default 20, max 700). Use it when you would rather make a single large request than paginate: it selects a different, slower search backend whose latency grows with depth, and a timeout is auto-refunded. Leave it unset for the fast default page size, which is the better choice for keyword→ASIN resolution. A depth of 1 to 3 falls back to the fast default page when the slower backend times out, so that answer has at most `depth` rows and carries `data.details.depth_truncated: true`. When the slower backend answers it may return more rows than `depth` (it returns its whole first page), as before. - page (optional, integer): Page number, starting at 1. Prefer the universal cursor parameter. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/amazon/product-search" \ --data-urlencode "query=wireless earbuds" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /amazon/product Get an Amazon product by ASIN Credit cost: 5 (advanced) Parameters: - asin (required): 10-character Amazon ASIN (the product identifier). - country (optional, enum: US | GB | CA | DE | FR | IT | ES | JP | IN | MX | BR | AU | NL): Amazon marketplace as an ISO 3166-1 alpha-2 country code (default US). Supported: US, GB, CA, DE, FR, IT, ES, JP, IN, MX, BR, AU, NL. The same ASIN can be a different listing, price or stock state on each marketplace, so pass the one the ASIN came from. ``` curl -G "https://www.socialcrawl.dev/v1/amazon/product" \ --data-urlencode "asin=B0FQFB8FMG" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /amazon/reviews Get Amazon product reviews Credit cost: 5-9 (metered): 5 credits per page. The default labels (sentiment, issue) are free. label=reports, incentivized or injection holds 4 extra credits and refunds down to 1 credit per started 25 reviews that were newly judged (the first 100 reviews of a page are judged): reviews already labelled are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. Parameters: - asin (required): 10-character Amazon ASIN (the product identifier). - country (optional, enum: US | GB | CA | DE | FR | IT | ES | JP | IN | MX | BR | AU | NL): Amazon marketplace as an ISO 3166-1 alpha-2 country code (default US). Supported: US, GB, CA, DE, FR, IT, ES, JP, IN, MX, BR, AU, NL. Amazon shows reviews across a product's whole variant family, so a review row can originate from a sibling ASIN; review.entity_id is always the ASIN you requested. - label (optional, string): Optional CSV of SocialCrawl labels to add to every review. Without this param every page already carries sentiment and issue, free; label= adds the labels you name to them (the defaults keep running). sentiment and issue are free when asked for too; reports, incentivized and injection add 1 credit per started 25 newly judged reviews. judgments=off (or label=none) turns the default labels off. They read the review's title and text, never its stars. sentiment: how the reviewer feels, on five levels (level 0 to 4, score_0_1, confidence), plus rating_mismatch, true when the words clearly contradict the star rating (a glowing text under 1 star, a furious one under 5), null when the review has no rating. issue: the main problem the review reports (label: product_defect, sizing_or_fit, shipping_or_delivery, customer_service, price_or_value, missing_feature, other, or none, with confidence; label is null when unsure). reports (needs reports=): does this review say that the thing you describe happened (p, 0 to 1). incentivized: does the reviewer say they got the product free, discounted or rewarded for the review (p), and did the text carry a disclosure such as 'in exchange for my honest review', Vine or 체험단 (disclosed). injection flags text that addresses an AI system and tries to direct it (flagged, p); it never drops or rewrites a row. These are signals to read, never a verdict on a review or a reviewer. A review that could not be judged carries labels: null. Reviews already labelled for anyone are free, and so is a cached page. data.labels reports what was judged and billed. - reports (optional, string): Required by label=reports, ignored otherwise. What to look for, in plain words, up to 300 characters, for example reports=the battery drains quickly or reports=배송이 늦음. Every review gains labels.reports.p, the probability that it says so, in any wording or language. Without it label=reports is skipped with the warning label_reports_needs_reports and is not billed. - judgments (optional, enum: on | off): Optional, on (the default) or off. By default every row gains free SocialCrawl judgments (computed.labels, and computed.relevance on search endpoints), reported in data.labels (mode default) and data.relevance (origin default), each with a status (complete, partial or skipped) and pending: the rows still being judged when the page was sent, which carry null now and are filled on your next call or cached read. Default judgments never add credits, never change an existing field, and never drop or reorder a row. off returns the page exactly as before, with none of those keys. label=none does the same. - dry_run (optional, enum: 1): Optional. When 1, return a cost preview for this labelled or relevance-filtered request without fetching the page or judging any row. data.estimate reports rows_expected, rows_cached, label_credits_min, label_credits_max and base_credits. 0 credits charged. - label_evidence (optional, enum: 1): Optional, only with label=. When 1, every labelled row also carries computed.labels_evidence. = { quote, sentence_index }: the sentence in the row that most clearly shows the label, copied verbatim. Absent or null when no single sentence shows it. - fit (optional, enum: goal): Optional. When goal, keep the rows and fields needed for the goal you pass in goal= (plus any that are uncertain, and the first and last), and replace the rest with a stub. data.held_back lists the held ids and a recall id that re-reads the full page from cache at no extra charge. Without this param the page is unchanged. - goal (optional, string): Required by fit=goal. What you are trying to do, in your own words, up to 300 characters. - fit_tokens (optional, integer): Optional, only with fit=goal. Soft cap on how much of the page to keep, in tokens. Uncertain blocks and the first and last block are kept even if they exceed it. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. ``` curl -G "https://www.socialcrawl.dev/v1/amazon/reviews" \ --data-urlencode "asin=B0BTYCRJSS" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /amazon/sellers Get Amazon sellers and offers for a product Credit cost: 1 (standard) Parameters: - asin (required): 10-character Amazon ASIN (the product identifier). - country (optional, enum: US | GB | CA | DE | FR | IT | ES | JP | IN | MX | BR | AU | NL): Amazon marketplace as an ISO 3166-1 alpha-2 country code (default US). Supported: US, GB, CA, DE, FR, IT, ES, JP, IN, MX, BR, AU, NL. Note: non-US marketplaces (especially EU) are best-effort: the upstream provider is slower and occasionally times out for these; such calls are refunded, and US is the most reliable marketplace. ``` curl -G "https://www.socialcrawl.dev/v1/amazon/sellers" \ --data-urlencode "asin=B09SM24S8C" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /amazon/best-sellers Get Amazon Best Sellers in a category Credit cost: 1 (standard) Parameters: - category (required): Best Sellers category path from the Amazon Best Sellers URL: a department slug such as software or officeproduct, or a department plus node id for a subcategory, such as officeproduct/202900031 (Karten on amazon.de). - country (optional, enum: US | GB | CA | DE | FR | IT | ES | JP | IN | MX | BR | AU | NL): Amazon marketplace as an ISO 3166-1 alpha-2 country code (default US). - type (optional, enum: BEST_SELLERS | NEW_RELEASES | MOVERS_AND_SHAKERS | MOST_WISHED_FOR | GIFT_IDEAS): List type: BEST_SELLERS (default), NEW_RELEASES, MOST_WISHED_FOR, GIFT_IDEAS, or MOVERS_AND_SHAKERS. MOVERS_AND_SHAKERS currently returns an empty list at 0 credits on US, GB and DE, because Amazon itself shows no Movers and Shakers rows. A category with no list of the requested type also returns empty and free. - page (optional, integer): Page number, starting at 1. Prefer the universal cursor parameter. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/amazon/best-sellers" \ --data-urlencode "category=software" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /amazon/deals Get current Amazon deals Credit cost: 1 (standard) Parameters: - country (optional, enum: US | GB | CA | DE | FR | IT | ES | JP | IN | MX | BR | AU | NL): Amazon marketplace as an ISO 3166-1 alpha-2 country code (default US). ``` curl "https://www.socialcrawl.dev/v1/amazon/deals" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /amazon/seller Get an Amazon seller profile Credit cost: 1 (standard) Parameters: - seller_id (required): Amazon seller id, the seller= value on a seller profile URL. - country (optional, enum: US | GB | CA | DE | FR | IT | ES | JP | IN | MX | BR | AU | NL): Amazon marketplace as an ISO 3166-1 alpha-2 country code (default US). ``` curl -G "https://www.socialcrawl.dev/v1/amazon/seller" \ --data-urlencode "seller_id=A294P4X9EWVXLJ" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## App_store ### GET /app_store/app-search Search Apple App Store apps by keyword Credit cost: 5 (advanced) Parameters: - query (required): Search keyword (e.g. 'photo editor'). - country (optional, string): Storefront country as an ISO code ('US'), a full name ('United States'), or a numeric location code ('2840'). Defaults to the US. - language (optional, string): Language code (e.g. 'en'). Defaults to 'en'. - depth (optional, integer): Number of results to retrieve (default 100, rounded up to multiples of 100, max 300). ``` curl -G "https://www.socialcrawl.dev/v1/app_store/app-search" \ --data-urlencode "query=photo editor" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /app_store/search-suggestions Get Apple App Store search suggestions Credit cost: 1 (standard) Parameters: - query (required): Partial search keyword to autocomplete (e.g. 'reverse'). - country (optional, string): ISO 3166-1 alpha-2 storefront country code (e.g. 'us', 'gb', 'kr'; the United Kingdom is 'gb', not 'uk'). Defaults to 'us'. A code that is not an App Store storefront is rejected with a free 400 rather than silently answered with US suggestions. ``` curl -G "https://www.socialcrawl.dev/v1/app_store/search-suggestions" \ --data-urlencode "query=reverse" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /app_store/app-info Get full Apple App Store app details Credit cost: 5 (advanced) Parameters: - app_id (required): Apple App Store numeric app id (e.g. '324684580'), from app-search. - country (optional, string): Storefront country as an ISO code ('US'), a full name, or a numeric code. Defaults to the US. - language (optional, string): Language code (e.g. 'en'). Defaults to 'en'. ``` curl -G "https://www.socialcrawl.dev/v1/app_store/app-info" \ --data-urlencode "app_id=324684580" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /app_store/app-reviews Get Apple App Store reviews for an app Credit cost: 5-9 (metered): 5 credits per page. The default labels (sentiment, issue) are free. label=reports, incentivized or injection holds 4 extra credits and refunds down to 1 credit per started 25 reviews that were newly judged (the first 100 reviews of a page are judged): reviews already labelled are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. Parameters: - app_id (required): Apple App Store numeric app id (e.g. '324684580'), from app-search. - country (optional, string): Storefront country as an ISO code ('US'), a full name, or a numeric code. Defaults to the US. - language (optional, string): Language code (e.g. 'en'). Defaults to 'en'. - depth (optional, integer): Number of reviews to retrieve (default 50, batches of 50, max 500). Above 500 is clamped, not rejected. Apple's feed stops there; Google Play reaches 600. - sort_by (optional, enum: most_recent | most_helpful): Review ordering: `most_recent` (default) or `most_helpful`. - rating (optional, integer): Not supported on this store: Apple's review feed cannot be filtered by star rating upstream, so this is rejected with a free 400 rather than silently returning the unfiltered feed. Filter on `review.rating.value` client-side, or use /v1/google_play/app-reviews. - label (optional, string): Optional CSV of SocialCrawl labels to add to every review. Without this param every page already carries sentiment and issue, free; label= adds the labels you name to them (the defaults keep running). sentiment and issue are free when asked for too; reports, incentivized and injection add 1 credit per started 25 newly judged reviews. judgments=off (or label=none) turns the default labels off. They read the review's title and text, never its stars. sentiment: how the reviewer feels, on five levels (level 0 to 4, score_0_1, confidence), plus rating_mismatch, true when the words clearly contradict the star rating (a glowing text under 1 star, a furious one under 5), null when the review has no rating. issue: the main problem the review reports (label: product_defect, sizing_or_fit, shipping_or_delivery, customer_service, price_or_value, missing_feature, other, or none, with confidence; label is null when unsure). reports (needs reports=): does this review say that the thing you describe happened (p, 0 to 1). incentivized: does the reviewer say they got the product free, discounted or rewarded for the review (p), and did the text carry a disclosure such as 'in exchange for my honest review', Vine or 체험단 (disclosed). injection flags text that addresses an AI system and tries to direct it (flagged, p); it never drops or rewrites a row. These are signals to read, never a verdict on a review or a reviewer. A review that could not be judged carries labels: null. Reviews already labelled for anyone are free, and so is a cached page. data.labels reports what was judged and billed. - reports (optional, string): Required by label=reports, ignored otherwise. What to look for, in plain words, up to 300 characters, for example reports=the battery drains quickly or reports=배송이 늦음. Every review gains labels.reports.p, the probability that it says so, in any wording or language. Without it label=reports is skipped with the warning label_reports_needs_reports and is not billed. - judgments (optional, enum: on | off): Optional, on (the default) or off. By default every row gains free SocialCrawl judgments (computed.labels, and computed.relevance on search endpoints), reported in data.labels (mode default) and data.relevance (origin default), each with a status (complete, partial or skipped) and pending: the rows still being judged when the page was sent, which carry null now and are filled on your next call or cached read. Default judgments never add credits, never change an existing field, and never drop or reorder a row. off returns the page exactly as before, with none of those keys. label=none does the same. - dry_run (optional, enum: 1): Optional. When 1, return a cost preview for this labelled or relevance-filtered request without fetching the page or judging any row. data.estimate reports rows_expected, rows_cached, label_credits_min, label_credits_max and base_credits. 0 credits charged. - label_evidence (optional, enum: 1): Optional, only with label=. When 1, every labelled row also carries computed.labels_evidence. = { quote, sentence_index }: the sentence in the row that most clearly shows the label, copied verbatim. Absent or null when no single sentence shows it. - fit (optional, enum: goal): Optional. When goal, keep the rows and fields needed for the goal you pass in goal= (plus any that are uncertain, and the first and last), and replace the rest with a stub. data.held_back lists the held ids and a recall id that re-reads the full page from cache at no extra charge. Without this param the page is unchanged. - goal (optional, string): Required by fit=goal. What you are trying to do, in your own words, up to 300 characters. - fit_tokens (optional, integer): Optional, only with fit=goal. Soft cap on how much of the page to keep, in tokens. Uncertain blocks and the first and last block are kept even if they exceed it. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off the page, and the page that reaches one ends the walk: `next_cursor` is not returned and `pagination.stopped_at` is `since`. Sending it sets the newest-first sort shown in this endpoint's sort parameter (any other sort value is a free 400). The page still costs what a page costs, so you pay for the pages you walk and no more. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. ``` curl -G "https://www.socialcrawl.dev/v1/app_store/app-reviews" \ --data-urlencode "app_id=324684580" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /app_store/app-list Get an Apple App Store chart Credit cost: 5 (advanced) Parameters: - app_collection (required): Chart to retrieve: top_free_ios, top_paid_ios, top_grossing_ios, new_ios, new_free_ios, new_paid_ios. An unknown value is rejected with a free 400 that lists the accepted set. - app_category (optional, enum: books | business | catalogs | education | entertainment | finance | food_and_drink | games | games_action | games_adventure | games_arcade | games_board | games_card | games_casino | games_dice | games_educational | games_family | games_music | games_puzzle | games_racing | games_role_playing | games_simulation | games_sports | games_strategy | games_trivia | games_word | health_and_fitness | lifestyle | magazines_and_newspapers | magazines_arts | magazines_automotive | magazines_weddings | magazines_business | magazines_children | magazines_computer | magazines_food | magazines_crafts | magazines_electronics | magazines_entertainment | magazines_fashion | magazines_health | magazines_history | magazines_home | magazines_literary | magazines_men | magazines_movies_and_music | magazines_politics | magazines_outdoors | magazines_family | magazines_pets | magazines_professional | magazines_regional | magazines_science | magazines_sports | magazines_teens | magazines_travel | magazines_women | medical | music | navigation | news | photo_and_video | productivity | reference | shopping | social_networking | sports | travel | utilities | weather): Optional Apple App Store category to scope the chart (e.g. 'photo_and_video', 'games_puzzle'). - country (optional, string): Storefront country as an ISO code ('US'), a full name, or a numeric code. Defaults to the US. - language (optional, string): Language code (e.g. 'en'). Defaults to 'en'. - depth (optional, integer): Number of apps to retrieve (default 100, rounded up to multiples of 100, max 500). ``` curl -G "https://www.socialcrawl.dev/v1/app_store/app-list" \ --data-urlencode "app_collection=top_free_ios" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /app_store/app-listings-search Search the Apple App Store listings database (paginated) Credit cost: 10 (premium) Parameters: - title (required): App title to search for (e.g. 'photo editor'). - description (optional, string): Optional app-description text to match. - limit (optional, integer): Apps per page (1-50, default 20). - offset (optional, integer): Pagination offset (up to 10,000). Prefer `cursor`: pass `pagination.next_cursor` straight back and the offset is handled for you. - offset_token (optional, string): Legacy deep-pagination token. Prefer `cursor`, which accepts `pagination.next_cursor` verbatim and works on every paginatable endpoint. - filters (optional, string): Advanced filter expression as a JSON array of clauses, e.g. [["item.rating.value",">",4]]. Must be valid JSON and must be an array: anything else is rejected with a free 400 rather than silently returning an unfiltered page. - country (optional, string): Storefront country as an ISO code ('US'), a full name, or a numeric code. Defaults to the US. - language (optional, string): Language code (e.g. 'en'). Defaults to 'en'. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/app_store/app-listings-search" \ --data-urlencode "title=photo editor" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /app_store/categories List Apple App Store app categories Credit cost: 1 (standard) ``` curl "https://www.socialcrawl.dev/v1/app_store/categories" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /app_store/locations List supported Apple App Store storefront locations Credit cost: 1 (standard) Parameters: - location_type (optional, enum: country | all): `country` (default) returns the storefronts the `country` param accepts. `all` returns the full underlying geo tree, including cities, postal codes and neighbourhoods, which all resolve to their parent country. ``` curl "https://www.socialcrawl.dev/v1/app_store/locations" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /app_store/languages List supported Apple App Store languages Credit cost: 1 (standard) ``` curl "https://www.socialcrawl.dev/v1/app_store/languages" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## Apple_music ### GET /apple_music/search Search Apple Music Credit cost: 1 (standard) Parameters: - query (required): Search query. - type (optional, string): Optional result kind such as song, album, or artist. ``` curl -G "https://www.socialcrawl.dev/v1/apple_music/search" \ --data-urlencode "query=taylor swift" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /apple_music/artist Get an Apple Music artist (oneOf: id|url) Credit cost: 1 (standard) Parameters: - id (optional, string): Apple Music artist ID. - url (optional, string): Apple Music artist URL. - Constraint: one of id, url (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/apple_music/artist" \ --data-urlencode "url=https://music.apple.com/us/artist/taylor-swift/159260351" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /apple_music/album Get an Apple Music album (oneOf: id|url) Credit cost: 1 (standard) Parameters: - id (optional, string): Apple Music album ID. - url (optional, string): Apple Music album URL. - Constraint: one of id, url (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/apple_music/album" \ --data-urlencode "url=https://music.apple.com/us/album/red-taylors-version/1590368448" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /apple_music/track Get an Apple Music track (oneOf: id|url) Credit cost: 1 (standard) Parameters: - id (optional, string): Apple Music track ID. - url (optional, string): Apple Music track URL. - Constraint: one of id, url (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/apple_music/track" \ --data-urlencode "url=https://music.apple.com/us/album/22-taylors-version/1590368448?i=1590368457" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /apple_music/charts Get the Apple Music charts for a country Credit cost: 1 (standard) Parameters: - country (optional, string): Country as an ISO 3166-1 alpha-2 code, in any case (`us`, `DE`, `kr`). Apple Music publishes a chart for most countries where it is sold; one it does not is rejected with a free 400. `uk` is read as `gb`. Defaults to `us`. - type (optional, enum: songs | albums | music-videos | playlists): Which chart: `songs` (the default), `albums`, `music-videos` or `playlists`. - limit (optional, integer): How many chart positions to return, from the top, 1 to 100. Defaults to 50. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. ``` curl "https://www.socialcrawl.dev/v1/apple_music/charts" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## Bluesky ### GET /bluesky/profile Get a Bluesky profile Credit cost: 1 (standard) Parameters: - handle (required): Bluesky handle (e.g. `espn.com`). ``` curl -G "https://www.socialcrawl.dev/v1/bluesky/profile" \ --data-urlencode "handle=espn.com" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /bluesky/user/posts List a Bluesky user's posts (oneOf: handle|user_id) Credit cost: 1 (standard) Parameters: - handle (optional, string): Bluesky handle. - user_id (optional, string): Bluesky `did` (Bluesky's internal user ID format, e.g. `did:plc:x7d6j54pm22ufehkes6jo4jf`). - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - Constraint: one of handle, user_id (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/bluesky/user/posts" \ --data-urlencode "handle=espn.com" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /bluesky/post Get a Bluesky post Credit cost: 1 (standard) Parameters: - url (required): Bluesky post URL. ``` curl -G "https://www.socialcrawl.dev/v1/bluesky/post" \ --data-urlencode "url=https://bsky.app/profile/espn.com/post/3lqdfq7fkvm2g" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /bluesky/search Search Bluesky posts Credit cost: 1 (standard) Parameters: - query (required): Search terms matched against post text. - sort (optional, enum: latest | top): `latest` (newest first, the default) or `top` (Bluesky's own top ranking, which favours popular posts over recent ones). - since (optional, string): Only posts written at or after this time. ISO 8601 date (`2026-09-01`) or datetime with a timezone (`2026-09-01T00:00:00Z`); any other shape is rejected with a free 400. - until (optional, string): Only posts written before this time. Same format as `since`. Set it to the oldest `published_at` of your last call to fetch the next older batch; the boundary is compared to the second, so a post from that same second can repeat, and deduplicating on `post.id` removes it. - lang (optional, string): Only posts tagged with this language, as a code such as `en`, `ja` or `ko`. - author (optional, string): Only posts by this account: a handle such as `espn.com` or a `did` such as `did:plc:...`. An account that does not exist returns an empty list. ``` curl -G "https://www.socialcrawl.dev/v1/bluesky/search" \ --data-urlencode "query=espresso" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## Content_analysis ### GET /content_analysis/search Search web citations of a keyword with per-mention sentiment Credit cost: 20 (override; tier advanced) Parameters: - keyword (required): Brand or term to find mentions of. Wrap in escaped double-quotes for an exact phrase (e.g. "logitech mouse"). - page_type (optional, enum: ecommerce | news | blogs | message-boards | organization): Narrow to one or more page types (comma-separated): ecommerce, news, blogs, message-boards, organization. Translated to a page_types filter upstream. - search_mode (optional, enum: as_is | one_per_domain): as_is (default) returns every matching page; one_per_domain dedupes to the top page per domain. - limit (optional, integer): Number of citations to return per page (1-100, default 10). Paginate via `cursor` for more. - cursor (optional, string): Opaque pagination cursor: pass the `next_cursor` from the previous response to fetch the next page. - order_by (optional, string): Sort rules as "field,direction"; separate multiple rules with ";" (e.g. content_info.sentiment_connotations.anger,desc). - filters (optional, string): Advanced filter expression as a JSON array (≤8 conditions). Combined with page_type via AND when both are present. ``` curl -G "https://www.socialcrawl.dev/v1/content_analysis/search" \ --data-urlencode "keyword=openai" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /content_analysis/summary Aggregate mention summary for a keyword Credit cost: 20 (override; tier advanced) Parameters: - keyword (required): Brand or term to summarize. - page_type (optional, enum: ecommerce | news | blogs | message-boards | organization): Narrow to one or more page types (comma-separated): ecommerce, news, blogs, message-boards, organization. - positive_connotation_threshold (optional, string): Minimum positive-connotation probability (0-1, default 0.4) for a mention to count as positive. - sentiments_connotation_threshold (optional, string): Minimum sentiment-connotation probability (0-1, default 0.4) for the 6-axis emotion buckets. - internal_list_limit (optional, integer): Cap on internal arrays such as top_domains / categories (1-20). - filters (optional, string): Advanced filter expression as a JSON array (≤8 conditions). ``` curl -G "https://www.socialcrawl.dev/v1/content_analysis/summary" \ --data-urlencode "keyword=openai" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /content_analysis/sentiment Sentiment breakdown for a keyword Credit cost: 20 (override; tier advanced) Parameters: - keyword (required): Brand or term to analyze. - page_type (optional, enum: ecommerce | news | blogs | message-boards | organization): Narrow to one or more page types (comma-separated): ecommerce, news, blogs, message-boards, organization. - positive_connotation_threshold (optional, string): Minimum positive-connotation probability (0-1, default 0.4). - filters (optional, string): Advanced filter expression as a JSON array (≤8 conditions). ``` curl -G "https://www.socialcrawl.dev/v1/content_analysis/sentiment" \ --data-urlencode "keyword=openai" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /content_analysis/rating-distribution Rating histogram for a keyword Credit cost: 20 (override; tier advanced) Parameters: - keyword (required): Product or term to build the rating histogram for. - page_type (optional, enum: ecommerce | news | blogs | message-boards | organization): Narrow to one or more page types (comma-separated): ecommerce, news, blogs, message-boards, organization. - filters (optional, string): Advanced filter expression as a JSON array (≤8 conditions). ``` curl -G "https://www.socialcrawl.dev/v1/content_analysis/rating-distribution" \ --data-urlencode "keyword=iphone" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /content_analysis/phrase-trends Keyword mention volume + sentiment over time Credit cost: 20 (override; tier advanced) Parameters: - keyword (required): Brand or term to trend. - date_from (required): Start of the date range (yyyy-mm-dd). - date_to (optional, string): End of the date range (yyyy-mm-dd); defaults to today. - date_group (optional, enum: day | week | month): Bucket size: day, week, or month (default month). - page_type (optional, enum: ecommerce | news | blogs | message-boards | organization): Narrow to one or more page types (comma-separated): ecommerce, news, blogs, message-boards, organization. - internal_list_limit (optional, integer): Cap on internal arrays per bucket (1-20). - filters (optional, string): Advanced filter expression as a JSON array (≤8 conditions). ``` curl -G "https://www.socialcrawl.dev/v1/content_analysis/phrase-trends" \ --data-urlencode "keyword=openai" \ --data-urlencode "date_from=2025-01-01" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /content_analysis/category-trends Category mention volume + sentiment over time Credit cost: 20 (override; tier advanced) Parameters: - category_code (required): Numeric category code from the /content_analysis/categories taxonomy (e.g. 10021 = Apparel). - date_from (required): Start of the date range (yyyy-mm-dd). - date_to (optional, string): End of the date range (yyyy-mm-dd); defaults to today. - date_group (optional, enum: day | week | month): Bucket size: day, week, or month (default month). - internal_list_limit (optional, integer): Cap on internal arrays per bucket (1-20). - filters (optional, string): Advanced filter expression as a JSON array (≤8 conditions) over the citation fields listed by /content_analysis/filters, applied before the buckets are counted. Example: [["country","=","DE"]] for German-hosted citations, or [["language","=","de"]] for German-language ones. ``` curl -G "https://www.socialcrawl.dev/v1/content_analysis/category-trends" \ --data-urlencode "category_code=10021" \ --data-urlencode "date_from=2025-01-01" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /content_analysis/languages List supported Content Analysis languages Credit cost: 1 (standard) ``` curl "https://www.socialcrawl.dev/v1/content_analysis/languages" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /content_analysis/locations List supported Content Analysis locations Credit cost: 1 (standard) ``` curl "https://www.socialcrawl.dev/v1/content_analysis/locations" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /content_analysis/categories List the Content Analysis category taxonomy Credit cost: 1 (standard) ``` curl "https://www.socialcrawl.dev/v1/content_analysis/categories" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /content_analysis/filters List the filterable fields for Content Analysis Credit cost: 1 (standard) ``` curl "https://www.socialcrawl.dev/v1/content_analysis/filters" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## Douyin ### GET /douyin/search Search Douyin videos Credit cost: 5-125 (metered): 5 credits per row RETURNED. The call holds `limit x 5` up front - the default limit of 10 holds 50 and the 25 maximum holds 125 - and settles down to the rows actually delivered, so a narrow query that returns 3 videos costs 15 credits however high `limit` was set Parameters: - query (required): Search keywords, for example 美食 or coffee. - limit (optional, integer): Rows to return, 1 to 25. Default 10. Each row costs 5 credits and you are charged only for rows returned. - sort (optional, enum: relevance | most_liked | latest): Result ordering: relevance, most_liked, or latest. Default relevance. - published (optional, enum: all | day | week | half_year): Recency window: all, day, week, or half_year. Default all. - duration (optional, enum: all | under_1m | one_to_five_m | over_5m): Length filter: all, under_1m, one_to_five_m, or over_5m. Default all. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. ``` curl -G "https://www.socialcrawl.dev/v1/douyin/search" \ --data-urlencode "query=美食" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /douyin/profile Get a Douyin creator profile (oneOf: handle|url|id) Credit cost: 6 (override; tier advanced) Parameters: - handle (optional, string): Douyin sec_uid, the MS4wLjAB... string in a profile URL, or the numeric user ID. - url (optional, string): Full Douyin profile URL, for example https://www.douyin.com/user/MS4wLjABAAAA... - id (optional, string): Numeric Douyin user ID. - Constraint: one of handle, url, id (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/douyin/profile" \ --data-urlencode "url=https://www.douyin.com/user/MS4wLjABAAAAtxsy7VmVkU3RN9oIX0vdkh_6-LlQAb0gwI-tDf-bYNg" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /douyin/profile/posts List a Douyin creator's videos (oneOf: handle|url|id) Credit cost: 5-125 (metered): 5 credits per row RETURNED. The call holds `limit x 5` up front - the default limit of 10 holds 50 and the 25 maximum holds 125 - and settles down to the videos actually delivered, so a creator with 4 recent videos costs 20 credits however high `limit` was set. `recent_days` and `exclude_pinned` narrow the result set BEFORE billing settles, so they lower the charge as well as the noise Parameters: - handle (optional, string): Douyin sec_uid, the MS4wLjAB... string in a profile URL, or the numeric user ID. - url (optional, string): Full Douyin profile URL. - id (optional, string): Numeric Douyin user ID. - limit (optional, integer): Rows to return, 1 to 25. Default 10. Each row costs 5 credits and you are charged only for rows returned. - recent_days (optional, integer): Only return videos published in the last N days. Leave unset for the newest regardless of age. - exclude_pinned (optional, boolean): Set true to skip videos pinned to the top of the profile, which can be old. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - Constraint: one of handle, url, id (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/douyin/profile/posts" \ --data-urlencode "url=https://www.douyin.com/user/MS4wLjABAAAAtxsy7VmVkU3RN9oIX0vdkh_6-LlQAb0gwI-tDf-bYNg" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /douyin/post Get a Douyin video Credit cost: 10 (premium) Parameters: - url (required): Douyin video URL, for example https://www.douyin.com/video/7681266289940544369, or the bare numeric aweme ID. ``` curl -G "https://www.socialcrawl.dev/v1/douyin/post" \ --data-urlencode "url=https://www.douyin.com/video/7681266289940544369" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /douyin/post/comments Get Douyin video comments Credit cost: 2-100 (metered): 1 credit per comment RETURNED, with a floor of 2. The call holds `limit x 1` up front - the default limit of 20 holds 20 and the 100 maximum holds 100 - and settles down to the comments actually delivered, so a video with four comments does not cost a twenty-comment request Parameters: - url (required): Douyin video URL or the bare numeric aweme ID. - limit (optional, integer): Comments to return, 1 to 100. Default 20. Each comment costs 1 credit and you are charged only for comments returned. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. ``` curl -G "https://www.socialcrawl.dev/v1/douyin/post/comments" \ --data-urlencode "url=https://www.douyin.com/video/7681266289940544369" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /douyin/comment/replies Get replies to a Douyin comment Credit cost: 5-250 (metered): 5 credits per reply RETURNED. The call holds `limit x 5` up front - the default limit of 10 holds 50 and the 50 maximum holds 250 - and settles down to the replies actually delivered. Only a comment with a non-zero `engagement.replies` has a thread, so check that on the parent row before spending a call here With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - url (required): Douyin video URL or the bare numeric aweme ID the comment sits under. - comment_id (required): The comment to expand, taken from the id field of a GET /v1/douyin/post/comments row. Only comments with a non-zero engagement.replies have a thread. - limit (optional, integer): Replies to return, 1 to 50. Default 10. Each reply costs 5 credits and you are charged only for replies returned. - cursor (optional, string): Continuation token from a previous response's pagination block. Omit for the first page. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/douyin/comment/replies" \ --data-urlencode "url=https://www.douyin.com/video/7681266289940544369" \ --data-urlencode "comment_id=7682735175344833317" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /douyin/search/users Search Douyin creators Credit cost: 5-125 (metered): 5 credits per creator RETURNED. The call holds `limit x 5` up front - the default limit of 10 holds 50 and the 25 maximum holds 125 - and settles down to the creators actually delivered With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - query (required): Search keywords, for example 美食 or a brand name. - limit (optional, integer): Rows to return, 1 to 25. Default 10. Each row costs 5 credits and you are charged only for rows returned. - cursor (optional, string): Continuation token from a previous response's pagination block. Omit for the first page. - followers (optional, enum: all | under_1k | 1k_to_10k | 10k_to_100k | 100k_to_1m | over_1m): Follower band: all, under_1k, 1k_to_10k, 10k_to_100k, 100k_to_1m, or over_1m. Default all. - user_type (optional, enum: all | regular_user | enterprise_verified | individual_verified): Account type: all, regular_user, enterprise_verified, or individual_verified. Default all. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/douyin/search/users" \ --data-urlencode "query=美食" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /douyin/trending Get the Douyin hot-search board Credit cost: 25 (override; tier advanced) ``` curl "https://www.socialcrawl.dev/v1/douyin/trending" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## Ebay ### GET /ebay/search Search eBay listings Credit cost: 5 (advanced) Parameters: - query (required): Free-text listing search, for example airpods pro or vintage seiko. - country (optional, enum: US | GB | AU | CA | DE | FR | IT | ES | IE | AT | CH | NL | BE | PL | SG | MY | PH | HK | MX): eBay marketplace as a two-letter country code: US (ebay.com, default), GB, AU, CA, DE, FR, IT, ES, IE, AT, CH, NL, BE, PL, SG, MY, PH, HK, or MX. The marketplace sets the listing pool, language, and currency. - page (optional, integer): Page number, starting at 1. Prefer the universal cursor parameter. - sort_by (optional, enum: best_match | price_low | price_high | newly_listed | ending_soonest): Result ordering: best_match (default), price_low, price_high, newly_listed, or ending_soonest. - condition (optional, enum: new | refurbished | used): Restrict to one item condition: new, refurbished, or used. - buying_format (optional, enum: auction | buy_it_now | accepts_offers): Restrict to one sale type: auction, buy_it_now, or accepts_offers. - show_only (optional, string): Restrict results with completed_items or free_shipping; combine supported values with a comma. sold_items is temporarily unavailable and is rejected before billing because the upstream requires a private eBay session cookie. SocialCrawl does not accept or forward session cookies. - min_price (optional, string): Lowest price to include. Applied by eBay and approximate, so filter on product.price.current if you need a hard bound. - max_price (optional, string): Highest price to include. Applied by eBay and approximate, so filter on product.price.current if you need a hard bound. - aspects (optional, string): Category-specific item aspect filter taken from a previous response, for example Brand:Apple. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/ebay/search" \ --data-urlencode "query=airpods pro" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /ebay/product Get an eBay listing by item id Credit cost: 5 (advanced) Parameters: - product_id (required): eBay item id, the numeric id in a listing URL: ebay.com/itm/327256602116 is item id 327256602116. Item ids also come back on every row of GET /v1/ebay/search. - country (optional, enum: US | GB | AU | CA | DE | FR | IT | ES | IE | AT | CH | NL | BE | PL | SG | MY | PH | HK | MX): eBay marketplace as a two-letter country code: US (ebay.com, default), GB, AU, CA, DE, FR, IT, ES, IE, AT, CH, NL, BE, PL, SG, MY, PH, HK, or MX. The marketplace sets the listing pool, language, and currency. ``` curl -G "https://www.socialcrawl.dev/v1/ebay/product" \ --data-urlencode "product_id=327256602116" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## Economy ### GET /economy/consumer-spend Get household spending by country and category Credit cost: 1 (standard) Parameters: - country_code (required): One or more ISO 3166-1 alpha-2 country codes, comma-separated (up to 10), e.g. `DE,FR,GR`. `EU27` returns the European Union total. - category (optional, string): Spending categories, comma-separated. Any of total, food, alcohol_tobacco, clothing, housing, furnishings, health, transport, communication, recreation, education, restaurants_hotels, insurance_financial, miscellaneous. Defaults to all fourteen. - from_year (optional, integer): First year to return. Defaults to five years before the current year. - to_year (optional, integer): Last year to return. Defaults to the latest year Eurostat has. - unit (optional, enum: eur_millions | eur_per_capita | share_of_total): `eur_millions` (current prices, million euro, the default), `eur_per_capita` (current prices, euro per person) or `share_of_total` (percentage of total household spending). ``` curl -G "https://www.socialcrawl.dev/v1/economy/consumer-spend" \ --data-urlencode "country_code=DE,FR" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /economy/tourism-spend Get travel spending by country and category Credit cost: 1 (standard) Parameters: - country_code (required): One or more ISO 3166-1 alpha-2 country codes of the travellers' home country, comma-separated (up to 10), e.g. `DE,FR`. `EU27` returns the European Union total. - category (optional, string): Spending categories, comma-separated. Any of total, transport, restaurants, accommodation, package, other, durables. Defaults to all seven. - purpose (optional, enum: total | personal | business): Trip purpose: `total` (default), `personal` or `business`. - destination (optional, enum: world | domestic | foreign): Where the trip went: `world` (default, all trips), `domestic` or `foreign`. - measure (optional, enum: total | per_trip | per_night): `total` spend (default), the average `per_trip` or the average `per_night`. - from_year (optional, integer): First year to return. Defaults to five years before the current year. - to_year (optional, integer): Last year to return. Defaults to the latest year Eurostat has. ``` curl -G "https://www.socialcrawl.dev/v1/economy/tourism-spend" \ --data-urlencode "country_code=DE,FR" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /economy/government-spend Get US federal spending by state, county or country Credit cost: 1 (standard) Parameters: - naics (optional, string): Industry filter: one or more NAICS codes, comma-separated (up to 20). Each code must be 2, 4 or 6 digits, and a shorter code matches the whole family, so `4811` covers scheduled air transportation and `48` covers all transportation. - psc (optional, string): Product or service code filter: one or more codes, comma-separated (up to 20), 1 to 4 characters each. A shorter code matches the family, so `V` is all transportation, travel and relocation services and `V301` is travel agent services. - keyword (optional, string): Free-text match against award descriptions, 3 to 120 characters. Use it for a topic such as `travel` that no single industry code captures. - from_date (optional, string): Start of the window, `YYYY-MM-DD`, no earlier than 2007-10-01. Defaults to the first day of the most recent US federal fiscal year (1 October to 30 September) that ended at least 90 days ago, because agencies report with a lag and a year that just ended is only partly reported. Send only this and the window runs to today. - to_date (optional, string): End of the window, `YYYY-MM-DD`. Defaults to the last day of the most recent US federal fiscal year that ended at least 90 days ago (30 September). Send only this and the window starts one year earlier. - geo (optional, enum: state | county | country): Geography layer: `state` (default), `county` or `country`. - scope (optional, enum: place_of_performance | recipient_location): `place_of_performance` (default) places spending where the work is done. `recipient_location` places it where the recipient is based. - award_type (optional, enum: contracts | grants | all): `contracts` (default) is purchase orders, delivery orders, definitive contracts and blanket purchase agreement calls. `grants` is grants and cooperative agreements. `all` is every award type. ``` curl "https://www.socialcrawl.dev/v1/economy/government-spend" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /economy/government-contracts Search US federal contract awards (oneOf: recipient|keyword|naics|psc) Credit cost: 1 (standard) Parameters: - recipient (optional, string): Company or organisation name to match, up to 120 characters, for example `Lockheed Martin`. Matched as text against the recipient name, so a short name also finds related entities. - keyword (optional, string): Free-text match against award descriptions, 3 to 120 characters. Use it for a topic such as `travel` that no single industry code captures. - naics (optional, string): Industry filter: one or more NAICS codes, comma-separated (up to 20). Each code must be 2, 4 or 6 digits, and a shorter code matches the whole family, so `4811` covers scheduled air transportation and `48` covers all transportation. - psc (optional, string): Product or service code filter: one or more codes, comma-separated (up to 20), 1 to 4 characters each. A shorter code matches the family, so `V` is all transportation, travel and relocation services and `V301` is travel agent services. - from_date (optional, string): Start of the window, `YYYY-MM-DD`, no earlier than 2007-10-01. Defaults to the first day of the most recent US federal fiscal year (1 October to 30 September) that ended at least 90 days ago, because agencies report with a lag and a year that just ended is only partly reported. Send only this and the window runs to today. - to_date (optional, string): End of the window, `YYYY-MM-DD`. Defaults to the last day of the most recent US federal fiscal year that ended at least 90 days ago (30 September). Send only this and the window starts one year earlier. - type (optional, enum: purchase_order | delivery_order | definitive_contract | bpa_call | all): Limit to one award type: `purchase_order`, `delivery_order`, `definitive_contract` or `bpa_call`. `all` (default) returns all four. - sort (optional, enum: amount | start_date | end_date | recipient): Sort field: `amount` (default), `start_date`, `end_date` or `recipient`. - order (optional, enum: asc | desc): `desc` (default) or `asc`. - limit (optional, integer): Awards per page, 1 to 100. Defaults to 25. - page (optional, integer): Page number, starting at 1. `page` x `limit` cannot exceed 50,000, so with `limit` 100 the last page is 500 and a deeper page is a refunded 400. The universal `cursor` returned in `pagination.next_cursor` is the simpler way to continue. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. - Constraint: one of recipient, keyword, naics, psc (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/economy/government-contracts" \ --data-urlencode "naics=4811" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /economy/imports Get a US importer's suppliers and shipments Credit cost: 20 credits per call, whatever the limit. A company with no US import records returns 404 and costs 0 credits, and a cached repeat within 24 hours costs 0 credits. Parameters: - company (required): The US importer's company name, as a brand ('Nike') or as it appears on shipping documents ('Nike Usa'). Endings such as Inc, LLC and USA are ignored when matching. 1 to 200 characters. - limit (optional, integer): How many recent shipments to return, newest first, from 1 to 50. Defaults to 25. The price is the same for any value. ``` curl -G "https://www.socialcrawl.dev/v1/economy/imports" \ --data-urlencode "company=Nike" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## Etsy ### GET /etsy/product Get an Etsy listing by id or URL (oneOf: product_id|url) Credit cost: 5 (advanced) Parameters: - product_id (optional, string): Etsy listing id, the numeric id in a listing URL: etsy.com/listing/1215970085 is listing id 1215970085. Listing ids also come back on every row of GET /v1/etsy/shop/products and GET /v1/etsy/product/similar. - url (optional, string): Full Etsy listing URL, for example https://www.etsy.com/listing/1215970085/custom-air-freshener. The listing id is extracted from the path. - country (optional, string): Ship-to country as a two-letter code. Default US. Changes currency and copy language when the listing supports it. - currency (optional, string): ISO 4217 currency code for the displayed price. Default USD. - language (optional, string): Listing copy locale, for example en-US, en-GB, fr, de. Default en-US. - Constraint: one of product_id, url (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/etsy/product" \ --data-urlencode "product_id=1215970085" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /etsy/shop/products List products in an Etsy shop (oneOf: id|url) Credit cost: 5 (advanced) Parameters: - id (optional, string): Etsy shop name, the path in etsy.com/shop/AceElegance. Shop names also come back on every product as product.seller. - url (optional, string): Full Etsy shop URL, for example https://www.etsy.com/shop/AceElegance or https://aceelegance.etsy.com. - page (optional, integer): Page number, starting at 1. Prefer the universal cursor parameter. - limit (optional, integer): Listings per page, 1 to 36. Default 36. - sort_by (optional, enum: most_recent | price_low | price_high): Result ordering: most_recent (default), price_low, or price_high. - currency (optional, string): ISO 4217 currency code for displayed prices. Default USD. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. - Constraint: one of id, url (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/etsy/shop/products" \ --data-urlencode "id=AceElegance" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /etsy/product/similar Get listings similar to an Etsy product (oneOf: product_id|url) Credit cost: 5 (advanced) Parameters: - product_id (optional, string): Etsy listing id, the numeric id in a listing URL: etsy.com/listing/1215970085 is listing id 1215970085. Listing ids also come back on every row of GET /v1/etsy/shop/products and GET /v1/etsy/product/similar. - url (optional, string): Full Etsy listing URL, for example https://www.etsy.com/listing/1215970085/custom-air-freshener. The listing id is extracted from the path. - country (optional, string): Ship-to country as a two-letter code. Default US. Changes currency and copy language when the listing supports it. - currency (optional, string): ISO 4217 currency code for displayed prices. Default USD. - language (optional, string): Listing copy locale. Default en-US. - Constraint: one of product_id, url (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/etsy/product/similar" \ --data-urlencode "product_id=1215970085" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /etsy/search/suggestions Get Etsy search suggestions Credit cost: 5 (advanced) Parameters: - query (required): Partial search query to autocomplete, for example leather. ``` curl -G "https://www.socialcrawl.dev/v1/etsy/search/suggestions" \ --data-urlencode "query=leather" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## Facebook ### GET /facebook/profile Get Facebook page profile Credit cost: 1 (standard) Parameters: - url (required): Full URL of the Facebook page or profile. A profile share link (`https://www.facebook.com/share/…`) is accepted. - get_business_hours (optional, string): Get the business's hours ``` curl -G "https://www.socialcrawl.dev/v1/facebook/profile" \ --data-urlencode "url=https://www.facebook.com/Meta" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /facebook/profile/posts List Facebook page posts (oneOf: url|pageId) Credit cost: 1-4 (metered): 1 credit for the page. include=engagement holds 1 credit per row (3 rows a page) and keeps only the rows filled from a fresh lookup, so a page is at most 4 credits; rows served from cache are free, unfilled rows are refunded, and a repeat of the same call within the cache window costs 0. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - since (optional, string): Only posts published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Older posts are left off the page, and the page that reaches one ends the walk: `next_cursor` is not returned and `pagination.stopped_at` is `since`. Pinned posts sit out of date order and never end the walk. The page still costs what a page costs, so a daily poll pays for the pages it walks and no more. - stop_at_id (optional, string): The id (`post.id`) or URL (`post.url`) of the newest post you already hold. The page stops just before it: that post and everything after it are left off, `next_cursor` is not returned, and `pagination.stopped_at` is `known_id`. A pinned post never counts as the stop point. If it is not on this page the page is returned in full with its cursor, so keep walking. `pagination.stopped_at` is `end` when the list ran out first and `null` while there is more to walk. - url (optional, string): Full URL of the Facebook page or profile to fetch posts for. A profile share link (`https://www.facebook.com/share/…`) is accepted. - pageId (optional, string): Facebook profile page id - cursor (optional, string): To paginate through the posts - include (optional, enum: engagement): Set to `engagement` (one token only) to fill `post.engagement.shares` on every row, and `post.engagement.views` and `post.content.duration_seconds` on reels, in this one call. Holds 1 credit per row (3 a page) and keeps only the rows filled from a fresh lookup: rows already in cache and rows the lookup cannot fill are refunded, and a row with no public share count stays null and is free, so a page is never more than 4 credits and the usual bill is lower. A row that links to an event rather than a post is not looked up. The join takes 2 to 7 seconds on a fresh page, never more than 12, and nothing when the rows are already cached; read `data.hydration` for the rows, the credits held and kept, and the time. Without it the call is unchanged. - recent_days (optional, integer): Keep only posts published in the last N days (1 to 3650) and stop pagination once a page reaches older posts. Does not change the price of a page. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. - Constraint: one of url, pageId (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/facebook/profile/posts" \ --data-urlencode "url=https://www.facebook.com/Meta" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /facebook/post Get Facebook post details Credit cost: 1-2 (metered): 1 credit for the post. include=group holds 1 more and keeps it only when the group's name comes back, so a call with include=group is at most 2 credits; it is refunded when the post is not in a group, when the lookup fails, and when the group was looked up recently and is served from cache. Parameters: - url (required): Full URL of the Facebook post, reel or video. A share link from Facebook's Copy link (`https://www.facebook.com/share/v/…`, `/share/p/…`, `/share/r/…` or `/share/…`) is accepted and resolved to the post it names. - get_comments (optional, boolean): Accepted for backwards compatibility and currently a NO-OP: this endpoint returns the same body with or without it. Use `/v1/facebook/post/comments` (1 credit) for comments, passing the `post.ext.feedback_id` this response returns. - get_transcript (optional, boolean): Accepted for backwards compatibility and currently a NO-OP: this endpoint returns the same body with or without it. Use `/v1/facebook/post/transcript` for a video transcript. - include (optional, enum: group): Set to `group` (one token only) to fill `post.ext.group.name` and `post.ext.group.members` (the member count) on a post published in a Facebook group, in this one call. It costs 1 extra credit, kept only when the group's name comes back: a post that is not in a group, a lookup that fails and a group already in cache are refunded, so the call is never more than 2 credits. The lookup adds about 3 to 7 seconds when it runs, never more than 12, and nothing when the group is cached; read `data.hydration` for the credits held and kept and the time. The group's `id` and `url` are on every group post without it. ``` curl -G "https://www.socialcrawl.dev/v1/facebook/post" \ --data-urlencode "url=https://www.facebook.com/OGCCopperfields/posts/pfbid02mAPifrgyzhGeAAhMGyNU4J2dFdFpAtTfb6p22yCQMHpzwqK9zh9XHS8Bm39tk7mwl" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /facebook/post/comments List Facebook post comments (oneOf: url|feedback_id) Credit cost: 1-5 (metered): 1 credit per page, with the default labels (sentiment, question, purchase_intent, complaint) included free. label=spam, toxic or low_quality holds 4 extra credits and refunds down to 1 credit per started 25 comments that were newly judged: comments already labelled are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - url (optional, string): Full URL of the Facebook post to fetch comments for. Share links (`/share/v/…`, `/share/p/…`, `/share/r/…`, `/share/…`) are accepted. - feedback_id (optional, string): Using feedback_id (instead of url) will *really* speed up the request. You can get the feedback_id when you make a request to /v1/facebook/post. - cursor (optional, string): Cursor to get more comments. Get 'cursor' from previous response. - label (optional, string): Optional CSV of SocialCrawl labels to add to every comment: sentiment, spam, question, purchase_intent, complaint, toxic, low_quality, injection. Without this param every page already carries sentiment, question, purchase_intent and complaint, free; label= adds the labels you name to them (the defaults keep running). Each comment gains computed.labels with probabilities (0 to 1), so you choose the cut-off; a comment that could not be judged carries labels: null. sentiment, question, purchase_intent and complaint are free when asked for too; spam, toxic, low_quality and injection add 1 credit per started 25 newly judged comments (comments already labelled for anyone are free, and so is a cached page). injection flags text that addresses an AI system and tries to direct it (flagged, p); it never drops or rewrites a row. judgments=off (or label=none) turns the default labels off. data.labels reports what was judged and billed. - exclude (optional, string): Optional CSV (spam, low_quality), only with the same preset in label. Drops comments whose probability is 0.8 or higher and lists their ids in data.labels.dropped_ids. A comment that could not be judged is never dropped. - judgments (optional, enum: on | off): Optional, on (the default) or off. By default every row gains free SocialCrawl judgments (computed.labels, and computed.relevance on search endpoints), reported in data.labels (mode default) and data.relevance (origin default), each with a status (complete, partial or skipped) and pending: the rows still being judged when the page was sent, which carry null now and are filled on your next call or cached read. Default judgments never add credits, never change an existing field, and never drop or reorder a row. off returns the page exactly as before, with none of those keys. label=none does the same. - dry_run (optional, enum: 1): Optional. When 1, return a cost preview for this labelled or relevance-filtered request without fetching the page or judging any row. data.estimate reports rows_expected, rows_cached, label_credits_min, label_credits_max and base_credits. 0 credits charged. - label_evidence (optional, enum: 1): Optional, only with label=. When 1, every labelled row also carries computed.labels_evidence. = { quote, sentence_index }: the sentence in the row that most clearly shows the label, copied verbatim. Absent or null when no single sentence shows it. - fit (optional, enum: goal): Optional. When goal, keep the rows and fields needed for the goal you pass in goal= (plus any that are uncertain, and the first and last), and replace the rest with a stub. data.held_back lists the held ids and a recall id that re-reads the full page from cache at no extra charge. Without this param the page is unchanged. - goal (optional, string): Required by fit=goal. What you are trying to do, in your own words, up to 300 characters. - fit_tokens (optional, integer): Optional, only with fit=goal. Soft cap on how much of the page to keep, in tokens. Uncertain blocks and the first and last block are kept even if they exceed it. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. - Constraint: one of url, feedback_id (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/facebook/post/comments" \ --data-urlencode "url=https://www.facebook.com/Meta/videos/a-slightly-life-changing-story/1459847961114516/" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /facebook/group/posts List Facebook group posts (oneOf: url|group_id) Credit cost: 1 (standard) Parameters: - url (optional, string): Full URL of the Facebook group - group_id (optional, string): The ID of the group - sort_by (optional, enum: TOP_POSTS | RECENT_ACTIVITY | CHRONOLOGICAL | CHRONOLOGICAL_LISTINGS): How to sort the posts. CHRONOLOGICAL_LISTINGS reads the group's listings feed and can stall on some groups (a group with no listings feed does not answer); use the default sort, or CHRONOLOGICAL for posts in date order. - cursor (optional, string): Cursor to get the next page of posts. Take it from `pagination.next_cursor` on the previous response. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. - Constraint: one of url, group_id (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/facebook/group/posts" \ --data-urlencode "url=https://www.facebook.com/groups/2204685680" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /facebook/group Get a Facebook group (oneOf: url|group_id) Credit cost: 1 (standard) Parameters: - url (optional, string): Full URL of the Facebook group. - group_id (optional, string): Numeric Facebook group id. - Constraint: one of url, group_id (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/facebook/group" \ --data-urlencode "url=https://www.facebook.com/groups/2204685680" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /facebook/post/transcript Get Facebook video transcript Credit cost: 10 (premium) Parameters: - url (required): Full URL of the Facebook video post. Share links (`/share/v/…`, `/share/r/…`) are accepted. ``` curl -G "https://www.socialcrawl.dev/v1/facebook/post/transcript" \ --data-urlencode "url=https://www.facebook.com/Meta/videos/a-slightly-life-changing-story/1459847961114516/" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /facebook/profile/photos List Facebook profile photos Credit cost: 1-9 (metered): 1 credit for the page. include=details holds 1 credit per photo (8 a page) and keeps only the rows filled from a fresh lookup, so a page is at most 9 credits; rows served from cache are free, unfilled rows are refunded, and a repeat of the same call within the cache window costs 0. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - url (required): Full URL of the Facebook page or profile. A profile share link (`https://www.facebook.com/share/…`) is accepted. - next_page_id (optional, string): To paginate through to the next page - cursor (optional, string): To paginate through to the next page - include (optional, enum: details): Set to `details` (one token only) to fill `post.author.display_name`, `post.author.avatar_url`, `post.ext.author_id`, `post.engagement.likes`, `.comments` and `.shares`, and `post.published_at` on every photo in this one call. Holds 1 credit per photo (8 a page) and keeps only the rows filled from a fresh lookup; rows already in cache and rows the lookup cannot fill are refunded, so a page is never more than 9 credits. A photo inside a multi-photo post carries its own counts, not the post's. The join takes 2 to 8 seconds on a fresh page, never more than 12, and nothing when the rows are already cached; read `data.hydration` for the rows, the credits held and kept, and the time. Without it the call is unchanged. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/facebook/profile/photos" \ --data-urlencode "url=https://www.facebook.com/Meta" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /facebook/profile/reels List Facebook profile reels Credit cost: 1 (standard) Parameters: - url (required): Full URL of the Facebook page or profile. A profile share link (`https://www.facebook.com/share/…`) is accepted. - next_page_id (optional, string): To paginate through to the next page - cursor (optional, string): To paginate through to the next page - recent_days (optional, integer): Keep only reels published in the last N days (1 to 3650) and stop pagination once a page reaches older reels. Does not change the price of a page. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/facebook/profile/reels" \ --data-urlencode "url=https://www.facebook.com/Meta" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /facebook/adlibrary/ad Get Facebook Ad Library ad details (oneOf: id|url) Credit cost: 5 (advanced) Parameters: - id (optional, string): Facebook Ad Library ad ID. Ad ids rotate as campaigns end, so take a current one from `/v1/facebook/adlibrary/company/ads` or `/v1/facebook/adlibrary/search/ads` rather than reusing an old one. - url (optional, string): Facebook Ad Library URL for the ad (`https://www.facebook.com/ads/library?id=...`). A page URL is rejected with a 400 at 0 credits. - trim (optional, boolean): Set to true for a trimmed down version of the response - Constraint: one of id, url (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/facebook/adlibrary/ad" \ --data-urlencode "id=1702938977100376" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /facebook/adlibrary/company/ads List Facebook Ad Library company ads (oneOf: pageId|companyName) Credit cost: 5-35 (metered): 5 credits for the page. include=audience holds 1 credit per ad (30 a page) and keeps only the rows filled from a fresh lookup, so a page is at most 35 credits; rows served from cache are free, ads that never ran in the EU and other unfilled rows are refunded, and a repeat of the same call within the cache window costs 0. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - pageId (optional, string): Facebook page ID of the advertiser - companyName (optional, string): The name of the company. Can either use this or pageId - country (optional, string): This can only be one country. It has to be the 2 letter code for the country. It defaults to ALL. - status (optional, enum: ALL | ACTIVE | INACTIVE): Status of the ad. Defaults to ACTIVE. - media_type (optional, enum: ALL | IMAGE | VIDEO | MEME | IMAGE_AND_MEME | NONE): Media type of the ad. Defaults to ALL. Meme refers to ads with image and text. Not sure why they call it meme. - language (optional, string): Language to filter ads on. Needs to be 2 letter language code, ie EN, ES, FR, etc - sort_by (optional, enum: total_impressions | relevancy_monthly_grouped): Sort by impressions (high to low), or Most Recent (relevancy_monthly_grouped). Defaults to impressions. - start_date (optional, string): Start date to search for. Format: YYYY-MM-DD - end_date (optional, string): End date to search for. Format: YYYY-MM-DD - cursor (optional, string): Cursor to paginate through results - trim (optional, boolean): Set to true for a trimmed down version of the response - include (optional, enum: audience): Set to `audience` (one token only) to fill, on every ad that ran in the EU, `post.ext.ad.eu_total_reach`, `post.ext.ad.eu_reach_breakdown` (reach per EU country by age band and gender), `post.ext.ad.targets_eu`, `target_ages`, `target_gender` and `target_locations`. Holds 1 credit per ad (30 a page) and keeps only the rows filled from a fresh lookup; rows already in cache, ads that never ran in the EU and rows the lookup cannot fill are refunded, so a page is never more than 35 credits. The join takes 3 to 6 seconds on a fresh page, never more than 12 seconds a row, and nothing when the rows are already cached; read `data.hydration` for the rows, the credits held and kept, and the time. Without it the call is unchanged. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. - Constraint: one of pageId, companyName (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/facebook/adlibrary/company/ads" \ --data-urlencode "companyName=Lululemon" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /facebook/adlibrary/search/ads Search Facebook Ad Library Credit cost: 5 (advanced) Parameters: - query (required): Search keyword or phrase to find ads in the Facebook Ad Library - sort_by (optional, enum: total_impressions | relevancy_monthly_grouped): Sort by impressions (high to low), or Most Recent (relevancy_monthly_grouped). Defaults to impressions. - search_type (optional, enum: keyword_unordered | keyword_exact_phrase): If you want to search by exact phrase or not - ad_type (optional, enum: all | political_and_issue_ads): Search for all ads or only political and issue ads - country (optional, string): This can only be one country. It has to be the 2 letter code for the country. It defaults to ALL. - status (optional, enum: ALL | ACTIVE | INACTIVE): Status of the ad. Defaults to ACTIVE. - media_type (optional, enum: ALL | IMAGE | VIDEO | MEME | IMAGE_AND_MEME | NONE): Media type of the ad. Defaults to ALL. Meme just means the ad has text and an image. No clue why they call it meme. - start_date (optional, string): Impressions start date. Needs to be in YYYY-MM-DD format. - end_date (optional, string): Impressions end date. Needs to be in YYYY-MM-DD format. - cursor (optional, string): Cursor to paginate through results - trim (optional, boolean): Set to true for a trimmed down version of the response - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/facebook/adlibrary/search/ads" \ --data-urlencode "query=artificial intelligence" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /facebook/adlibrary/search/companies Search Facebook Ad Library companies Credit cost: 5 (advanced) Parameters: - query (required): Search keyword or phrase to find companies in the Facebook Ad Library ``` curl -G "https://www.socialcrawl.dev/v1/facebook/adlibrary/search/companies" \ --data-urlencode "query=Nike" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /facebook/profile/events List a Facebook page's events Credit cost: 1-9 (metered): 1 credit for the page. include=details holds 1 credit per event (8 a page) and keeps only the rows filled from a fresh lookup, so a page is at most 9 credits; rows served from cache are free, unfilled rows are refunded, and a repeat of the same call within the cache window costs 0. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - url (required): Full URL of the public Facebook page. - cursor (optional, string): Cursor returned by the previous response for pagination. - include (optional, enum: details): Set to `details` (one token only) to fill, on every event in this one call, `post.ext.event.description`, `address`, `latitude`, `longitude`, `hosts`, `host_context_text`, `category`, `privacy`, `attendance_count`, `interested_count` and `going_count`, the cover image on `post.content.thumbnail_url`, and the RSVP counts on `post.engagement.views` (interested) and `.likes` (going). Holds 1 credit per event (8 a page) and keeps only the rows filled from a fresh lookup; rows already in cache and rows the lookup cannot fill are refunded, so a page is never more than 9 credits. The join takes 2 to 6 seconds on a fresh page, never more than 12, and nothing when the rows are already cached; read `data.hydration` for the rows, the credits held and kept, and the time. Without it the call is unchanged. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/facebook/profile/events" \ --data-urlencode "url=https://www.facebook.com/brickyardoldtown" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /facebook/post/comment/replies List replies to a Facebook post comment Credit cost: 1-5 (metered): 1 credit per page, with the default labels (sentiment, question, purchase_intent, complaint) included free. label=spam, toxic or low_quality holds 4 extra credits and refunds down to 1 credit per started 25 comments that were newly judged: comments already labelled are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - feedback_id (required): `feedback_id` from `/v1/facebook/post/comments` for the parent comment (this is NOT the comment ID). - expansion_token (required): `expansion_token` from `/v1/facebook/post/comments` for the parent comment. - cursor (optional, string): Cursor returned by the previous response for pagination. - label (optional, string): Optional CSV of SocialCrawl labels to add to every comment: sentiment, spam, question, purchase_intent, complaint, toxic, low_quality, injection. Without this param every page already carries sentiment, question, purchase_intent and complaint, free; label= adds the labels you name to them (the defaults keep running). Each comment gains computed.labels with probabilities (0 to 1), so you choose the cut-off; a comment that could not be judged carries labels: null. sentiment, question, purchase_intent and complaint are free when asked for too; spam, toxic, low_quality and injection add 1 credit per started 25 newly judged comments (comments already labelled for anyone are free, and so is a cached page). injection flags text that addresses an AI system and tries to direct it (flagged, p); it never drops or rewrites a row. judgments=off (or label=none) turns the default labels off. data.labels reports what was judged and billed. - exclude (optional, string): Optional CSV (spam, low_quality), only with the same preset in label. Drops comments whose probability is 0.8 or higher and lists their ids in data.labels.dropped_ids. A comment that could not be judged is never dropped. - judgments (optional, enum: on | off): Optional, on (the default) or off. By default every row gains free SocialCrawl judgments (computed.labels, and computed.relevance on search endpoints), reported in data.labels (mode default) and data.relevance (origin default), each with a status (complete, partial or skipped) and pending: the rows still being judged when the page was sent, which carry null now and are filled on your next call or cached read. Default judgments never add credits, never change an existing field, and never drop or reorder a row. off returns the page exactly as before, with none of those keys. label=none does the same. - dry_run (optional, enum: 1): Optional. When 1, return a cost preview for this labelled or relevance-filtered request without fetching the page or judging any row. data.estimate reports rows_expected, rows_cached, label_credits_min, label_credits_max and base_credits. 0 credits charged. - label_evidence (optional, enum: 1): Optional, only with label=. When 1, every labelled row also carries computed.labels_evidence. = { quote, sentence_index }: the sentence in the row that most clearly shows the label, copied verbatim. Absent or null when no single sentence shows it. - fit (optional, enum: goal): Optional. When goal, keep the rows and fields needed for the goal you pass in goal= (plus any that are uncertain, and the first and last), and replace the rest with a stub. data.held_back lists the held ids and a recall id that re-reads the full page from cache at no extra charge. Without this param the page is unchanged. - goal (optional, string): Required by fit=goal. What you are trying to do, in your own words, up to 300 characters. - fit_tokens (optional, integer): Optional, only with fit=goal. Soft cap on how much of the page to keep, in tokens. Uncertain blocks and the first and last block are kept even if they exceed it. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/facebook/post/comment/replies" \ --data-urlencode "feedback_id=ZmVlZGJhY2s6MzM3MzYxMzI5OTQ4Nzc1NV8xOTI5NTk0OTE3Njg3MTcz" \ --data-urlencode "expansion_token=MjoxNzc4NjM1MDM3OgF_JaMa3kMP9RIWC" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /facebook/marketplace/location/search Search Facebook Marketplace locations Credit cost: 1 (standard) Parameters: - query (required): Location search query (city or area name). ``` curl -G "https://www.socialcrawl.dev/v1/facebook/marketplace/location/search" \ --data-urlencode "query=Los Angeles" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /facebook/marketplace/search Search Facebook Marketplace listings Credit cost: 1 (standard) Parameters: - query (required): Search keyword (e.g., `bike`, `couch`). - lat (required): Latitude of the search location. - lng (required): Longitude of the search location. - radius_km (optional, integer): Search radius in kilometers. - min_price (optional, integer): Minimum listing price. - max_price (optional, integer): Maximum listing price. - count (optional, integer): Number of listings to return per page. - sort_by (optional, enum: suggested | distance_ascend | creation_time_descend | price_ascend | price_descend): Sort order for results. - delivery_method (optional, enum: all | local_pickup | shipping): Delivery filter: local pickup only, shipping only, or all. - condition (optional, enum: new | used_like_new | used_good | used_fair): Filter listings by item condition. - date_listed (optional, enum: all | 1 | 7 | 30 | last_24_hours | last_7_days | last_30_days): Filter by listing recency (relative window or numeric day count). - availability (optional, enum: available | sold | all): Filter by listing status: `available`, `sold`, or `all`. - cursor (optional, string): Opaque pagination cursor returned by the previous response: forward as-is. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/facebook/marketplace/search" \ --data-urlencode "query=bike" \ --data-urlencode "lat=40.7128" \ --data-urlencode "lng=-74.0060" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /facebook/marketplace/item Get a Facebook Marketplace item (oneOf: id|url) Credit cost: 1 (standard) Parameters: - id (optional, string): Facebook Marketplace item ID (numeric). - url (optional, string): Full URL of the Marketplace item. - Constraint: one of id, url (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/facebook/marketplace/item" \ --data-urlencode "url=https://www.facebook.com/marketplace/item/1656586118821988/" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /facebook/events/search Search Facebook events by keyword Credit cost: 1 (standard) Parameters: - query (required): Event name or keyword to search for. - cursor (optional, string): Cursor returned by the previous response for pagination. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/facebook/events/search" \ --data-urlencode "query=dogs" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /facebook/events List Facebook events for a city Credit cost: 1-13 (metered): 1 credit for the page. include=details holds 1 credit per event (at most 12 a page) and keeps only the rows filled from a fresh lookup, so a page is at most 13 credits; rows served from cache are free, unfilled rows are refunded, and a repeat of the same call within the cache window costs 0. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - url (required): Full URL of the city's Facebook Events page. - time (optional, enum: today | this_week | next_week): Relative time window. Defaults to all time when omitted. - cursor (optional, string): Cursor returned by the previous response for pagination. - include (optional, enum: details): Set to `details` (one token only) to fill, on every event in this one call, `post.ext.event.description`, `address`, `city`, `latitude`, `longitude`, `hosts`, `host_context_text`, `category`, `privacy`, `is_canceled` and `attendance_count`, and the host credit on `post.author.display_name`. Holds 1 credit per event (at most 12 a page) and keeps only the rows filled from a fresh lookup; rows already in cache and rows the lookup cannot fill are refunded, so a page is never more than 13 credits. The join takes 2 to 8 seconds on a fresh page, never more than 12, and nothing when the rows are already cached; read `data.hydration` for the rows, the credits held and kept, and the time. Without it the call is unchanged. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/facebook/events" \ --data-urlencode "url=https://www.facebook.com/events/explore/saint-petersburg-florida/111326725552547" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /facebook/event/details Get details for a Facebook event (oneOf: id|url) Credit cost: 1 (standard) Parameters: - id (optional, string): Facebook event ID (numeric). - url (optional, string): Full URL of the event. - Constraint: one of id, url (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/facebook/event/details" \ --data-urlencode "url=https://www.facebook.com/events/4271394179613905/" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /facebook/adlibrary/ad/transcript Get a Facebook Ad Library video ad transcript (oneOf: id|url) Credit cost: 10 (premium) Parameters: - id (optional, string): Facebook Ad Library ad ID. Ad ids rotate as campaigns end, so take a current one from `/v1/facebook/adlibrary/company/ads` rather than reusing an old one. - url (optional, string): Facebook Ad Library URL for the ad. A page URL is rejected with a 400 at 0 credits. - Constraint: one of id, url (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/facebook/adlibrary/ad/transcript" \ --data-urlencode "id=1126618083054464" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /facebook/profile/full Facebook profile, recent posts, and computed analytics in one call. (oneOf: url) Credit cost: 5 (override; tier standard) Parameters: - url (optional, string): Full Facebook profile or page URL. One of the identity params is required. - posts (optional, integer): How many of the fetched recent posts to return and compute the metrics over (1-100, default 25). One call reads one page, which on Facebook is 3 posts, so a larger value returns that page and a `_warnings` note; pass posts_cursor back as cursor for the next page. - cursor (optional, string): Pass a prior response's posts_cursor to read the next page of posts. Each call is a separate 5-credit call whose metrics cover that page only. - include (optional, string): CSV subset of posts,computed (default both). include=computed omits the raw posts[] array from the response to save payload. The metrics are computed from the same fetched posts either way, so they do not change. - Constraint: one of url (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/facebook/profile/full" \ --data-urlencode "url=https://www.facebook.com/mrbeast" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /facebook/profile/reels/full Facebook profile reels with exact views, likes, comments, and shares merged in, in one call. Credit cost: 5-25 (metered): 5 credits per page of 10 reels - 1 for the list and 4 for the per-reel enrichment - which is roughly half what running the list-then-post-stats chain yourself costs. With no `limit` that is one page and a flat 5 credits. With `limit` set the endpoint walks `ceil(limit / 10)` pages, up to 5 for the 50 maximum (25 credits), holds that many up front and refunds every page it did not need. A page whose enrichment yields zero coverage refunds the 4-credit premium automatically, leaving the 1-credit list price Parameters: - url (required): Full URL of the Facebook page or profile. A profile share link (`https://www.facebook.com/share/…`) is accepted. - cursor (optional, string): Cursor from a prior response's next_cursor to page deeper. - limit (optional, integer): Return up to this many reels in one call (1-50). The endpoint pages the underlying list server-side until it has collected this many (or runs out), and bills per upstream page consumed (5 credits/page of 10). If the walk hits the internal time budget first, the response carries `_warnings: ["walk_deadline_reached"]`, unfetched pages are refunded, and `next_cursor` resumes where it stopped. Omit for a single page. ``` curl -G "https://www.socialcrawl.dev/v1/facebook/profile/reels/full" \ --data-urlencode "url=https://www.facebook.com/Meta" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /facebook/search/posts Search Facebook posts by keyword Credit cost: 1-9 (metered): 1 credit per page. Relevance against your query (by default, or with relevance=) is free. Adding relevant_to= holds 4 extra credits and refunds down to 1 credit per started 25 rows that were newly judged: rows already judged for the same topic are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. The default labels (sponsored, intent, niche) are free. label=mention, quality, ai_style, ai_written, injection, or label=intent with offer=, holds 4 extra credits and refunds down to 1 credit per started 25 posts that were newly judged: posts already labelled are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. With `seen`, a page's price falls with its share of rows you already received under that id; credits for joined data (`include=`) are never discounted. Parameters: - query (required): Keyword or phrase to search for. - cursor (optional, string): Cursor from the previous response. - start_date (optional, string): Include posts on or after this date (YYYY-MM-DD). - end_date (optional, string): Include posts on or before this date (YYYY-MM-DD). - recent_posts (optional, boolean): When true, rank by recency instead of relevance. - location_uid (optional, string): Facebook location id to scope the search. - relevance (optional, enum: score | filter): Optional. Without this param every row already carries computed.relevance against your query, free ({ p, sense, depth, spam }: is this row about what your query means, or a different thing that shares its words?), and nothing is dropped or reordered. score asks for it explicitly and waits for every row; filter also drops the rows that are not about your query, and lists their ids in data.relevance.dropped_ids. Your query is the topic; nothing to configure. A row that could not be judged is never dropped and carries relevance: null. Pagination is unchanged, so a filtered page can hold fewer rows. Free with your query as the topic; with relevant_to it adds 1 credit per started 25 newly judged rows (rows already judged for the same topic are free, and so is a cached page). - relevance_threshold (optional, string): Optional, only with relevance. The probability (0 to 1) a row must reach to be kept by relevance=filter. Default 0.5. Lower keeps more rows, higher keeps fewer. - relevant_to (optional, string): Optional, only with relevance. Up to 200 characters describing what you mean, used as the topic instead of the query. Use it when the query is ambiguous, for example query=cleopatra with relevant_to=Cleopatra, the IGT slot game. - judgments (optional, enum: on | off): Optional, on (the default) or off. By default every row gains free SocialCrawl judgments (computed.labels, and computed.relevance on search endpoints), reported in data.labels (mode default) and data.relevance (origin default), each with a status (complete, partial or skipped) and pending: the rows still being judged when the page was sent, which carry null now and are filled on your next call or cached read. Default judgments never add credits, never change an existing field, and never drop or reorder a row. off returns the page exactly as before, with none of those keys. label=none does the same. - dry_run (optional, enum: 1): Optional. When 1, return a cost preview for this labelled or relevance-filtered request without fetching the page or judging any row. data.estimate reports rows_expected, rows_cached, label_credits_min, label_credits_max and base_credits. 0 credits charged. - fit (optional, enum: goal): Optional. When goal, keep the rows and fields needed for the goal you pass in goal= (plus any that are uncertain, and the first and last), and replace the rest with a stub. data.held_back lists the held ids and a recall id that re-reads the full page from cache at no extra charge. Without this param the page is unchanged. - goal (optional, string): Required by fit=goal. What you are trying to do, in your own words, up to 300 characters. - fit_tokens (optional, integer): Optional, only with fit=goal. Soft cap on how much of the page to keep, in tokens. Uncertain blocks and the first and last block are kept even if they exceed it. - label (optional, string): Optional CSV of SocialCrawl labels to add to every post. Without this param every page already carries sponsored, intent and niche, free; label= adds the labels you name to them (the defaults keep running). sponsored, intent and niche are free when asked for too; mention, quality, ai_style, intent with offer=, and injection add 1 credit per started 25 newly judged posts. judgments=off (or label=none) turns the default labels off. sponsored: is the post a paid or gifted promotion (p, 0 to 1), did it carry a disclosure marker such as #ad or 광고 (disclosed), is it likely paid with no marker (undisclosed), and which of the accounts it mentions does it promote (brand, or null). These are signals to review, never a finding. mention (needs brand=): is the post about that brand rather than something that shares its name (about_brand, 0 to 1), how it feels about the brand on five levels (sentiment_level 0 to 4 and sentiment_score 0 to 1, null when the post is not about the brand), is it sarcastic, which aspect it talks about (taste_or_quality, price_or_value, availability_or_delivery, health_or_safety, advertising_or_campaign, customer_service, none), and did the author buy or use it (first_hand). intent: what the author is mainly doing (label: asking_for_recommendation, comparing_options, switching_away, complaining, promoting, news_or_discussion, other, or null when unsure, with confidence), whether they read as a potential buyer rather than a seller (buyer, seller), how pressing the need is (urgency, 0 to 3), and, when you pass offer=, whether your offer would plausibly help them (fits_offer). niche: which of the 33 niches of the published taxonomy sc-niche-v1 the caption belongs to, or personal_no_niche, or other (label, confidence, taxonomy), with label null when the caption is too thin to tell or the pick is unsure. It reads the caption only, not the video. quality: how much checkable detail the caption carries (fact_density 0 to 3), whether it mainly asks for likes, replies, shares, follows or tags (engagement_bait, 0 to 1), whether it is written to provoke anger as a way to get engagement (rage_bait, 0 to 1; about the writing, never the side taken), whether it only repeats someone else's news or view (secondhand, 0 to 1), and what the post is mainly doing (post_aim: inform, opinion, sell, entertain, provoke, other). exclude=engagement_bait drops posts whose engagement_bait is 0.8 or higher. ai_style: writing patterns common in AI-generated text, judged on the text alone. It does not say who wrote the post, and a person who writes in this style is flagged too. Six patterns are judged, each a probability (0 to 1): binary_contrast ("This is not X. It's Y."), faux_insight ("Here's what nobody tells you" and throat-clearing openers), colon_reveal ("The best part: it learns."), profound_kicker (ends on an aphorism or mic-drop line), puffery ("marks a pivotal moment", "highlighting the importance of") and rhetorical_setup ("What if I told you", a question the post answers itself, "Not a X. Not a Y. A Z."). Four are counted in the text: slop_words (words and phrases such as delve, leverage, robust, game changer, at the end of the day), em_dashes, emoji_bullets (lines that start with an emoji or a symbol bullet) and one_line_paragraphs (paragraphs that are one short line). pattern_count is how many patterns are present (a judged pattern at 0.8 or higher, slop_words 1 or more, em_dashes 2 or more, emoji_bullets 2 or more, one_line_paragraphs 5 or more) and markers lists their names, comma-separated. A post with less than about eight words of text is only counted, with every judged pattern 0. The judged patterns read the first 400 and the last 150 characters of a long post, and the counts read the whole post. exclude=ai_style drops posts with 2 or more patterns. ai_written: how likely the post was written by an AI model, from SocialCrawl's own text classifier, reading the opening of the post (about 1,000 characters): p (0 to 1, calibrated), level (likely (p 0.95 or higher), possible (p 0.71 or higher) or unlikely) and model (the model name; a new name means a new scale). A post under 8 words is too short to read: p null, level too_short. It is a probability, not proof: a person can be flagged and an AI-written post can pass, and the shorter the post the less certain it is. exclude=ai_written drops likely posts. injection flags text that addresses an AI system and tries to direct it (flagged, p); it never drops or rewrites a row. A post that could not be judged carries labels: null. Posts already labelled for anyone are free, and so is a cached page. data.labels reports what was judged and billed. - exclude (optional, string): Optional CSV (engagement_bait, ai_style, ai_written). engagement_bait, only with label=quality, drops posts whose engagement_bait probability is 0.8 or higher. ai_style, only with label=ai_style, drops posts that show 2 or more of its writing patterns (pattern_count 2 or higher). ai_written, only with label=ai_written, drops posts whose level is likely (p 0.95 or higher), or whose p reaches your ai_written_threshold; a post too short to read is never dropped. Dropped posts are listed in data.labels.dropped_ids. A post that could not be judged is never dropped. - ai_written_threshold (optional, string): Optional, only with label=ai_written and exclude=ai_written. The p (0 to 1) at which exclude=ai_written drops a post. Default 0.95 (level likely). 0.71 also drops possible posts: more AI posts go, and more posts by people with them. - brand (optional, string): Required by label=mention, ignored otherwise. The brand or product the posts are judged against, up to 300 characters, for example brand=Buldak. Without it label=mention is skipped with the warning label_mention_needs_brand and is not billed. - brand_description (optional, string): Optional, with label=mention. One plain sentence saying what the brand is, for example brand_description=Samyang's spicy instant noodle brand. Use it when the name is also an ordinary word or another company's name. - offer (optional, string): Optional, with label=intent. One or two plain sentences saying what you sell, up to 300 characters, for example offer=A web design agency that builds websites for small businesses. Adds fits_offer to every post; without it fits_offer is null. - label_evidence (optional, enum: 1): Optional, only with label=. When 1, every labelled row also carries computed.labels_evidence. = { quote, sentence_index }: the sentence in the row that most clearly shows the label, copied verbatim. Absent or null when no single sentence shows it. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_age_days (optional, integer): Keep only rows published within this many days (`post.published_at`), 1 to 3650. A row with no date is discarded. Filtering runs on our side after each page is fetched, so every page walked is billed as usual; `data.walk.discarded` counts what was removed and why. Each page is its own entry in `GET /v1/credits/transactions`, under the page's request id listed in `data.walk.request_ids`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them (after any filter). Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows (after any filter), then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. - seen (optional, string): An id you choose (1 to 64 letters, digits, `.`, `_` or `-`). Rows your account already received under the same `seen` id are removed from the page, and the page price falls with the share of repeats: page credits x new rows / rows on the page, rounded up, so a page of nothing but repeats is free. Only the page's own price is discounted: credits for joined data (`include=`) are charged only for rows you have not received, and are never discounted. The memory lasts 24 hours from the last call that used the id, holds row ids only, and is private to your account. Use one id across a set of related searches; `data.walk.repeats` counts what was removed. ``` curl -G "https://www.socialcrawl.dev/v1/facebook/search/posts" \ --data-urlencode "query=NASA" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /facebook/search/pages Search Facebook pages by keyword Credit cost: 1-11 (metered): 1 credit for the page. include=details holds 1 credit per page (at most 10) and keeps only the rows filled from a fresh lookup, so a page is at most 11 credits; rows served from cache are free, unfilled rows are refunded, and a repeat of the same call within the cache window costs 0. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - query (required): Keyword or phrase to search for. - cursor (optional, string): Cursor from the previous response. - location_uid (optional, string): Facebook location id to scope the search. - include (optional, enum: details): Set to `details` (one token only) to fill `author.followers`, `author.bio`, `author.likes_count` and `author.joined_at` on every page in this one call. Holds 1 credit per page (at most 10) and keeps only the rows filled from a fresh lookup: rows already in cache and rows the lookup cannot fill are refunded, so a page is never more than 11 credits. The join takes 3 to 10 seconds on a fresh page, never more than 12, and nothing when the rows are already cached; read `data.hydration` for the rows, the credits held and kept, and the time. Without it the call is unchanged. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/facebook/search/pages" \ --data-urlencode "query=NASA" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /facebook/search/people Search Facebook people by keyword Credit cost: 1 (standard) Parameters: - query (required): Name or phrase to search for. - cursor (optional, string): Cursor from the previous response. - location_uid (optional, string): Facebook location id to scope the search to a city. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/facebook/search/people" \ --data-urlencode "query=NASA" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /facebook/search/videos Search Facebook videos by keyword Credit cost: 1-8 (metered): 1 credit for the page. include=details holds 1 credit per video (7 a page) and keeps only the rows filled from a fresh lookup, so a page is at most 8 credits; rows served from cache are free, unfilled rows are refunded, and a repeat of the same call within the cache window costs 0. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - query (required): Keyword or phrase to search for. - cursor (optional, string): Cursor from the previous response. - start_date (optional, string): Include videos on or after this date (YYYY-MM-DD). - end_date (optional, string): Include videos on or before this date (YYYY-MM-DD). - recent_videos (optional, boolean): When true, rank by recency instead of relevance. - location_uid (optional, string): Facebook location id to scope the search. - include (optional, enum: details): Set to `details` (one token only) to fill `post.engagement.views`, `.likes`, `.comments` and `.shares`, `post.content.duration_seconds`, `post.published_at`, `post.author.avatar_url` and `post.ext.reaction_counts` on every video in this one call. Holds 1 credit per video (7 a page) and keeps only the rows filled from a fresh lookup: rows already in cache and rows the lookup cannot fill are refunded, so a page is never more than 8 credits. The join takes 3 to 5 seconds on a fresh page, never more than 12, and nothing when the rows are already cached; read `data.hydration` for the rows, the credits held and kept, and the time. Without it the call is unchanged. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/facebook/search/videos" \ --data-urlencode "query=NASA" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /facebook/search/groups Search Facebook groups by keyword Credit cost: 1-15 (metered): 1 credit for the page. include=details holds 1 credit per group (at most 10 a page) and keeps only the groups filled from a fresh lookup, so a page is at most 11 credits; groups served from cache are free, unfilled groups are refunded, and a repeat of the same call within the cache window costs 0. Relevance against your query (by default, or with relevance=) is free. Adding relevant_to= holds 4 extra credits and refunds down to 1 credit per started 25 rows that were newly judged: rows already judged for the same topic are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - query (required): Keywords describing the groups to find, for example `vintage cars`. Keywords only: the search is already limited to public Facebook groups, so a `site:` operator is refused before billing. - page (optional, integer): Page number, starting at 1. Forwarding `pagination.next_cursor` as `cursor` does the same. - include (optional, enum: details): Set to `details` (one token only) to fill, on every group in this one call, `author.followers` (members), `author.bio` (the description), `author.joined_at`, `author.ext.group.privacy_label`, `visibility_label`, `activity` and `id` (the numeric group id), and the name where it was null. Holds 1 credit per group (at most 10 a page) and keeps only the groups whose member count, description or activity was filled from a fresh lookup; groups already in cache and groups the lookup cannot fill are refunded, so a page is never more than 11 credits. The join takes 3 to 7 seconds on a fresh page, never more than 12, and nothing when the groups are already cached; read `data.hydration` for the rows, the credits held and kept, and the time. Without it the call is unchanged. - relevance (optional, enum: score | filter): Optional. Without this param every row already carries computed.relevance against your query, free ({ p, sense, depth, spam }: is this row about what your query means, or a different thing that shares its words?), and nothing is dropped or reordered. score asks for it explicitly and waits for every row; filter also drops the rows that are not about your query, and lists their ids in data.relevance.dropped_ids. Your query is the topic; nothing to configure. A row that could not be judged is never dropped and carries relevance: null. Pagination is unchanged, so a filtered page can hold fewer rows. Free with your query as the topic; with relevant_to it adds 1 credit per started 25 newly judged rows (rows already judged for the same topic are free, and so is a cached page). - relevance_threshold (optional, string): Optional, only with relevance. The probability (0 to 1) a row must reach to be kept by relevance=filter. Default 0.5. Lower keeps more rows, higher keeps fewer. - relevant_to (optional, string): Optional, only with relevance. Up to 200 characters describing what you mean, used as the topic instead of the query. Use it when the query is ambiguous, for example query=cleopatra with relevant_to=Cleopatra, the IGT slot game. - judgments (optional, enum: on | off): Optional, on (the default) or off. By default every row gains free SocialCrawl judgments (computed.labels, and computed.relevance on search endpoints), reported in data.labels (mode default) and data.relevance (origin default), each with a status (complete, partial or skipped) and pending: the rows still being judged when the page was sent, which carry null now and are filled on your next call or cached read. Default judgments never add credits, never change an existing field, and never drop or reorder a row. off returns the page exactly as before, with none of those keys. label=none does the same. - dry_run (optional, enum: 1): Optional. When 1, return a cost preview for this labelled or relevance-filtered request without fetching the page or judging any row. data.estimate reports rows_expected, rows_cached, label_credits_min, label_credits_max and base_credits. 0 credits charged. - fit (optional, enum: goal): Optional. When goal, keep the rows and fields needed for the goal you pass in goal= (plus any that are uncertain, and the first and last), and replace the rest with a stub. data.held_back lists the held ids and a recall id that re-reads the full page from cache at no extra charge. Without this param the page is unchanged. - goal (optional, string): Required by fit=goal. What you are trying to do, in your own words, up to 300 characters. - fit_tokens (optional, integer): Optional, only with fit=goal. Soft cap on how much of the page to keep, in tokens. Uncertain blocks and the first and last block are kept even if they exceed it. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/facebook/search/groups" \ --data-urlencode "query=vintage cars" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /facebook/search/hashtag Search Facebook posts and videos carrying a hashtag Credit cost: 0-9 (metered): Metered, 0-9 credits: each call reads one page of /v1/facebook/search/videos (1 credit; with `since` or `until`, up to 8, since each video is looked up for its publish date at 1 credit, and a video still undated is left out) and /v1/facebook/search/posts (1 credit), each billed at that endpoint's own price exactly as a direct call is. A page that found nothing or failed costs nothing, and a page served from cache is free. A page is billed when it returned rows, even if none of them carried the hashtag; data.legs shows each page's charge and data.excluded what was left out. Parameters: - hashtag (required): One hashtag, with or without the #: letters, digits and _, no spaces. - cursor (optional, string): data.next_cursor from the previous response, sent back verbatim for the next page. - since (optional, string): Only posts published on or after this date, YYYY-MM-DD (UTC). - until (optional, string): Only posts published on or before this date, YYYY-MM-DD (UTC); that whole day is included. ``` curl -G "https://www.socialcrawl.dev/v1/facebook/search/hashtag" \ --data-urlencode "hashtag=tractor" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /facebook/search/mentions Search Facebook posts that mention a page Credit cost: 0-1 (metered): Metered, 0-1 credits: each call reads one page of /v1/facebook/search/posts (1 credit), each billed at that endpoint's own price exactly as a direct call is. A page that found nothing or failed costs nothing, and a page served from cache is free. A page is billed when it returned rows, even if none of them carried the mention; data.legs shows each page's charge and data.excluded what was left out. Parameters: - handle (required): The page's username (facebook.com/), with or without the @, or its URL. Used to leave out the page's own posts. - cursor (optional, string): data.next_cursor from the previous response, sent back verbatim for the next page. - since (optional, string): Only posts published on or after this date, YYYY-MM-DD (UTC). - until (optional, string): Only posts published on or before this date, YYYY-MM-DD (UTC); that whole day is included. - name (optional, string): The name people write when they mention the page, for example Nykaa for facebook.com/mynykaa. Defaults to handle. - include_self (optional, boolean): When true, also keep posts by the account itself. Default false. ``` curl -G "https://www.socialcrawl.dev/v1/facebook/search/mentions" \ --data-urlencode "handle=myntra" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## Finance ### GET /finance/quote Get a financial instrument quote Credit cost: 5 (advanced) Parameters: - keyword (required): Instrument identifier: TICKER:EXCHANGE for stocks/ETFs/indices ('GOOGL:NASDAQ', '.INX:INDEXSP') or a forex/crypto pair ('EUR-USD', 'BTC-USD'). Use the `id` returned by ticker-search. - language (optional, string): Language as a full name ('English') or 2-letter code ('en'). Defaults to English. - location (optional, string): Location as a full name ('United States') or numeric code ('2840'). Defaults to the US. ``` curl -G "https://www.socialcrawl.dev/v1/finance/quote" \ --data-urlencode "keyword=GOOGL:NASDAQ" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /finance/ticker-search Search financial instruments by name Credit cost: 1 (standard) Parameters: - keyword (required): Company / instrument name to search for (e.g. 'Apple', 'Euro', 'Bitcoin'). - category (optional, enum: all | stock | index | mutual_fund | currency | futures): Restrict to one instrument class: all (default), stock, index, mutual_fund, currency, or futures. A class with no matches returns an empty list. - language (optional, string): Language as a full name ('English') or 2-letter code ('en'). Defaults to English. - location (optional, string): Location as a full name ('United States') or numeric code ('2840'). Defaults to the US. ``` curl -G "https://www.socialcrawl.dev/v1/finance/ticker-search" \ --data-urlencode "keyword=Apple" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /finance/markets Get a markets overview (indices + movers) Credit cost: 1 (standard) Parameters: - language (optional, string): Language as a full name ('English') or 2-letter code ('en'). Defaults to English. - location (optional, string): Location as a full name ('United States') or numeric code ('2840'). Defaults to the US. ``` curl "https://www.socialcrawl.dev/v1/finance/markets" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /finance/news Get recent news for a financial instrument Credit cost: 1 (standard) Parameters: - keyword (required): Instrument ticker. A bare symbol ('AAPL', '005930.KS') or the 'TICKER:EXCHANGE' form from ticker-search ('AAPL:NASDAQ'); the exchange suffix is ignored. - limit (optional, integer): Maximum articles to return, 1 to 50. Defaults to the upstream batch size. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. ``` curl -G "https://www.socialcrawl.dev/v1/finance/news" \ --data-urlencode "keyword=AAPL" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /finance/history Get daily price history for an instrument Credit cost: 1 (standard) Parameters: - keyword (required): Instrument ticker. A bare symbol ('AAPL', '005930.KS') or the 'TICKER:EXCHANGE' form from ticker-search; the exchange suffix is ignored. - start_date (required): First session to return, as YYYY-MM-DD. - end_date (required): Last session to return, as YYYY-MM-DD. - interval (optional, enum: 1d | 5d | 1wk | 1mo | 3mo): Bar width. Defaults to 1d (daily). Wider intervals return fewer, coarser rows over the same range. ``` curl -G "https://www.socialcrawl.dev/v1/finance/history" \ --data-urlencode "keyword=AAPL" \ --data-urlencode "start_date=2026-06-01" \ --data-urlencode "end_date=2026-09-01" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /finance/statements Get financial statements for a company Credit cost: 5 (advanced) Parameters: - keyword (required): Company ticker. A bare symbol ('AAPL') or the 'TICKER:EXCHANGE' form; the exchange suffix is ignored. - type (optional, enum: annual | quarterly | trailing): Reporting periodicity: annual, quarterly, or trailing. Defaults to annual. ``` curl -G "https://www.socialcrawl.dev/v1/finance/statements" \ --data-urlencode "keyword=AAPL" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /finance/options Get an options chain for an instrument Credit cost: 5 (advanced) Parameters: - keyword (required): Underlying ticker. A bare symbol ('AAPL') or the 'TICKER:EXCHANGE' form; the exchange suffix is ignored. - expiry (optional, string): Expiry to return as YYYY-MM-DD. Defaults to the nearest available expiry. An expiry with no listed contracts returns an empty list, not an error. ``` curl -G "https://www.socialcrawl.dev/v1/finance/options" \ --data-urlencode "keyword=AAPL" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /finance/fundamentals Get quarterly or annual fundamentals for a public company Credit cost: 1 (standard) Parameters: - keyword (required): Company ticker, case-insensitive. A bare symbol ('TGT') or the 'TICKER:EXCHANGE' form; the exchange suffix is ignored. Share classes work as 'BRK.B' or 'BRK-B'. - period (optional, enum: quarterly | annual): Reporting period: quarterly or annual. Defaults to quarterly. - limit (optional, integer): Number of most recent periods to return, a whole number from 1 to 40. Defaults to 8. A value outside that range is rejected with a 400 and the credits are refunded. ``` curl -G "https://www.socialcrawl.dev/v1/finance/fundamentals" \ --data-urlencode "keyword=TGT" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## G2 ### GET /g2/product Get a G2 product by slug or URL (oneOf: id|url) Credit cost: 5 (advanced) Parameters: - id (optional, string): G2 product slug or full G2 product URL. Slug example: postman. URL example: https://www.g2.com/products/postman/reviews. - url (optional, string): G2 product slug or full G2 product URL. Slug example: postman. URL example: https://www.g2.com/products/postman/reviews. - website_contacts_crawl_mode (optional, enum: off | homepage | deep): How far to crawl the product website for emails, phones, and social profiles. Default off. homepage scans the homepage only. deep also crawls contact, about, careers, and blog pages. - Constraint: one of id, url (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/g2/product" \ --data-urlencode "id=postman" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /g2/reviews Get G2 reviews for a product (oneOf: id|url) Credit cost: 5-9 (metered): 5 credits per page. The default labels (sentiment, issue) are free. label=reports, incentivized or injection holds 4 extra credits and refunds down to 1 credit per started 25 reviews that were newly judged (the first 100 reviews of a page are judged): reviews already labelled are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - id (optional, string): G2 product slug or full G2 product URL. Slug example: postman. URL example: https://www.g2.com/products/postman/reviews. - url (optional, string): G2 product slug or full G2 product URL. Slug example: postman. URL example: https://www.g2.com/products/postman/reviews. - page (optional, integer): Page number, starting at 1. Prefer the universal cursor parameter. - industry (optional, string): Comma-separated G2 industry ids. Discover valid ids from a first unfiltered call. - keywords (optional, string): Free-text search inside review bodies. - region (optional, string): Comma-separated reviewer regions: North America, Europe, Asia, Latin America, Middle East, Africa, ANZ. - role (optional, string): Comma-separated reviewer roles, for example Administrator, User, Consultant. - segment (optional, enum: small-business | mid-market | enterprise): Reviewer company size: small-business (50 or fewer), mid-market (51-1000), enterprise (more than 1000). - sort (optional, enum: default | most-recent | most-helpful | highest-rated | lowest-rated): Review order. Default is G2 relevance. - stars (optional, string): Comma-separated star ratings, for example 4,5. - label (optional, string): Optional CSV of SocialCrawl labels to add to every review. Without this param every page already carries sentiment and issue, free; label= adds the labels you name to them (the defaults keep running). sentiment and issue are free when asked for too; reports, incentivized and injection add 1 credit per started 25 newly judged reviews. judgments=off (or label=none) turns the default labels off. They read the review's title and text, never its stars. sentiment: how the reviewer feels, on five levels (level 0 to 4, score_0_1, confidence), plus rating_mismatch, true when the words clearly contradict the star rating (a glowing text under 1 star, a furious one under 5), null when the review has no rating. issue: the main problem the review reports (label: product_defect, sizing_or_fit, shipping_or_delivery, customer_service, price_or_value, missing_feature, other, or none, with confidence; label is null when unsure). reports (needs reports=): does this review say that the thing you describe happened (p, 0 to 1). incentivized: does the reviewer say they got the product free, discounted or rewarded for the review (p), and did the text carry a disclosure such as 'in exchange for my honest review', Vine or 체험단 (disclosed). injection flags text that addresses an AI system and tries to direct it (flagged, p); it never drops or rewrites a row. These are signals to read, never a verdict on a review or a reviewer. A review that could not be judged carries labels: null. Reviews already labelled for anyone are free, and so is a cached page. data.labels reports what was judged and billed. - reports (optional, string): Required by label=reports, ignored otherwise. What to look for, in plain words, up to 300 characters, for example reports=the battery drains quickly or reports=배송이 늦음. Every review gains labels.reports.p, the probability that it says so, in any wording or language. Without it label=reports is skipped with the warning label_reports_needs_reports and is not billed. - judgments (optional, enum: on | off): Optional, on (the default) or off. By default every row gains free SocialCrawl judgments (computed.labels, and computed.relevance on search endpoints), reported in data.labels (mode default) and data.relevance (origin default), each with a status (complete, partial or skipped) and pending: the rows still being judged when the page was sent, which carry null now and are filled on your next call or cached read. Default judgments never add credits, never change an existing field, and never drop or reorder a row. off returns the page exactly as before, with none of those keys. label=none does the same. - dry_run (optional, enum: 1): Optional. When 1, return a cost preview for this labelled or relevance-filtered request without fetching the page or judging any row. data.estimate reports rows_expected, rows_cached, label_credits_min, label_credits_max and base_credits. 0 credits charged. - label_evidence (optional, enum: 1): Optional, only with label=. When 1, every labelled row also carries computed.labels_evidence. = { quote, sentence_index }: the sentence in the row that most clearly shows the label, copied verbatim. Absent or null when no single sentence shows it. - fit (optional, enum: goal): Optional. When goal, keep the rows and fields needed for the goal you pass in goal= (plus any that are uncertain, and the first and last), and replace the rest with a stub. data.held_back lists the held ids and a recall id that re-reads the full page from cache at no extra charge. Without this param the page is unchanged. - goal (optional, string): Required by fit=goal. What you are trying to do, in your own words, up to 300 characters. - fit_tokens (optional, integer): Optional, only with fit=goal. Soft cap on how much of the page to keep, in tokens. Uncertain blocks and the first and last block are kept even if they exceed it. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. - Constraint: one of id, url (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/g2/reviews" \ --data-urlencode "id=postman" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /g2/category List products in a G2 category Credit cost: 5 (advanced) Parameters: - category (required): G2 category slug or full category URL, for example marketing-automation or https://www.g2.com/categories/marketing-automation. - page (optional, integer): Page number, starting at 1. Prefer the universal cursor parameter. - sort (optional, enum: g2-score | popularity | satisfaction): Product order in the category. Default g2-score. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/g2/category" \ --data-urlencode "category=marketing-automation" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /g2/categories List every G2 category URL Credit cost: 1 (standard) ``` curl "https://www.socialcrawl.dev/v1/g2/categories" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /g2/seller Get a G2 seller (vendor) profile (oneOf: id|url|handle) Credit cost: 5 (advanced) Parameters: - id (optional, string): G2 seller slug or full G2 seller URL. Slug example: hubspot. URL example: https://www.g2.com/sellers/hubspot. - url (optional, string): G2 seller slug or full G2 seller URL. Slug example: hubspot. URL example: https://www.g2.com/sellers/hubspot. - handle (optional, string): G2 seller slug or full G2 seller URL. Slug example: hubspot. URL example: https://www.g2.com/sellers/hubspot. - products_page (optional, integer): Page of the seller product portfolio, 9 products per page. - reviews_page (optional, integer): Page of cross-product review teasers, 3 per page, capped at 10. - Constraint: one of id, url, handle (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/g2/seller" \ --data-urlencode "id=hubspot" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /g2/seller/products List products for a G2 seller (oneOf: id|url) Credit cost: 5 (advanced) Parameters: - id (optional, string): G2 seller slug or full G2 seller URL. Slug example: hubspot. URL example: https://www.g2.com/sellers/hubspot. - url (optional, string): G2 seller slug or full G2 seller URL. Slug example: hubspot. URL example: https://www.g2.com/sellers/hubspot. - page (optional, integer): Page number, starting at 1. Prefer the universal cursor parameter. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. - Constraint: one of id, url (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/g2/seller/products" \ --data-urlencode "id=google" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /g2/product-index List G2 product URLs Credit cost: 5 (advanced) Parameters: - page (optional, integer): Page number, starting at 1. Prefer the universal cursor parameter. ``` curl "https://www.socialcrawl.dev/v1/g2/product-index" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## Gab ### GET /gab/profile Get a Gab profile Credit cost: 1 (standard) Parameters: - handle (required): Gab username without the @ (letters, digits and underscores, up to 30 characters), as in gab.com/. ``` curl -G "https://www.socialcrawl.dev/v1/gab/profile" \ --data-urlencode "handle=a" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /gab/user/posts List a Gab user's posts Credit cost: 1 (standard) Parameters: - handle (required): Gab username without the @ (letters, digits and underscores, up to 30 characters), as in gab.com/. - cursor (optional, string): The `next_cursor` value from the previous page, passed back unchanged to fetch the next older page. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/gab/user/posts" \ --data-urlencode "handle=a" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /gab/post Get a Gab post Credit cost: 1 (standard) Parameters: - url (required): Gab post URL (`https://gab.com//posts/`). A bare numeric post id is accepted too. ``` curl -G "https://www.socialcrawl.dev/v1/gab/post" \ --data-urlencode "url=https://gab.com/a/posts/117445750063805239" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /gab/post/comments List the comments on a Gab post Credit cost: 1 (standard) Parameters: - url (required): Gab post URL (`https://gab.com//posts/`). A bare numeric post id is accepted too. - cursor (optional, string): The `next_cursor` value from the previous page (a page number), passed back unchanged. - sort (optional, enum: newest | oldest | top): `newest` (the default), `oldest`, or `top` (Gab's own ranking, which favours the most liked comments). - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/gab/post/comments" \ --data-urlencode "url=https://gab.com/a/posts/117445750063805239" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /gab/search/posts Search Gab posts Credit cost: 1 (standard) Parameters: - query (required): Search terms matched against post text. - cursor (optional, string): The `next_cursor` value from the previous page (a page number), passed back unchanged. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/gab/search/posts" \ --data-urlencode "query=RNLI" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## Github ### GET /github/profile Get a GitHub user profile Credit cost: 1 (standard) Parameters: - handle (required): GitHub username: 1-39 chars, alphanumeric + non-consecutive hyphens, no leading/trailing hyphen. ``` curl -G "https://www.socialcrawl.dev/v1/github/profile" \ --data-urlencode "handle=octocat" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /github/repo Get a GitHub repository Credit cost: 1 (standard) Parameters: - url (required): GitHub repo URL: `https://github.com/{owner}/{repo}`. ``` curl -G "https://www.socialcrawl.dev/v1/github/repo" \ --data-urlencode "url=https://github.com/octocat/Hello-World" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /github/profile/repos List a GitHub user's repositories Credit cost: 1 (standard) Parameters: - handle (required): GitHub username. - type (optional, enum: all | owner | member): Filter: `all`, `owner`, or `member`. Defaults to `owner`. - sort (optional, enum: created | updated | pushed | full_name): Sort field: `created`, `updated`, `pushed`, or `full_name`. Defaults to `full_name`. - direction (optional, enum: asc | desc): `asc` or `desc`. Defaults to `asc` for full_name, `desc` otherwise. - per_page (optional, integer): Repos per page (1-100). Defaults to 30. - page (optional, integer): 1-indexed page number for pagination. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/github/profile/repos" \ --data-urlencode "handle=octocat" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /github/repo/readme Get a repository's README Credit cost: 1 (standard) Parameters: - url (required): GitHub repo URL: `https://github.com/{owner}/{repo}`. ``` curl -G "https://www.socialcrawl.dev/v1/github/repo/readme" \ --data-urlencode "url=https://github.com/octocat/Hello-World" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /github/repo/releases List a repository's releases Credit cost: 1 (standard) Parameters: - url (required): GitHub repo URL: `https://github.com/{owner}/{repo}`. - per_page (optional, integer): Releases per page (1-100). Defaults to 30. - page (optional, integer): 1-indexed page number. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/github/repo/releases" \ --data-urlencode "url=https://github.com/facebook/react" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /github/repo/issues List a repository's issues (and PRs) Credit cost: 1 (standard) Parameters: - url (required): GitHub repo URL: `https://github.com/{owner}/{repo}`. - type (optional, enum: issue | pr | all): Filter the unified issues+PRs list: `issue` returns only issues, `pr` returns only pull requests, `all` (default) returns both. Each item also carries `post.ext.type` (`issue`/`pull_request`). - state (optional, enum: open | closed | all): `open`, `closed`, or `all`. Defaults to `open`. - labels (optional, string): Comma-separated label names (e.g. `bug,help wanted`). - sort (optional, enum: created | updated | comments): `created`, `updated`, or `comments`. Defaults to `created`. - direction (optional, enum: asc | desc): `asc` or `desc`. Defaults to `desc`. - since (optional, string): Only issues updated at or after this ISO 8601 timestamp. - per_page (optional, integer): Issues per page (1-100). Defaults to 30. - page (optional, integer): 1-indexed page number. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/github/repo/issues" \ --data-urlencode "url=https://github.com/facebook/react" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /github/issue Get a single issue or pull request Credit cost: 1 (standard) Parameters: - url (required): GitHub HTML URL: `https://github.com/{owner}/{repo}/issues/{n}` or `/pull/{n}`. ``` curl -G "https://www.socialcrawl.dev/v1/github/issue" \ --data-urlencode "url=https://github.com/facebook/react/issues/27522" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /github/issue/comments Get comments on an issue or pull request Credit cost: 1 (standard) Parameters: - url (required): GitHub HTML URL of the issue or PR. - since (optional, string): Only comments updated at or after this ISO 8601 timestamp. - per_page (optional, integer): Comments per page (1-100). Defaults to 30. - page (optional, integer): 1-indexed page number. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/github/issue/comments" \ --data-urlencode "url=https://github.com/facebook/react/issues/27522" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /github/search Search GitHub issues and pull requests Credit cost: 1 (standard) Parameters: - query (required): GitHub search query (uses GitHub's qualifier syntax: see https://docs.github.com/en/search-github/searching-on-github/searching-issues-and-pull-requests). - sort (optional, enum: reactions | comments | created | updated): Sort field: `reactions`, `comments`, `created`, `updated`. Defaults to best-match relevance. - order (optional, enum: asc | desc): `asc` or `desc`. Defaults to `desc`. - per_page (optional, integer): Results per page (1-100). Defaults to 30. - page (optional, integer): 1-indexed page number. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/github/search" \ --data-urlencode "query=repo:vercel/next.js is:issue is:open" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /github/repo/top-issues Top feature request and top complaint for a repository Credit cost: 5 (advanced) Parameters: - url (required): GitHub repo URL: `https://github.com/{owner}/{repo}`. ``` curl -G "https://www.socialcrawl.dev/v1/github/repo/top-issues" \ --data-urlencode "url=https://github.com/facebook/react" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /github/repo/dossier Full project dossier for a repository Credit cost: 5 (advanced) Parameters: - url (required): GitHub repo URL: `https://github.com/{owner}/{repo}`. ``` curl -G "https://www.socialcrawl.dev/v1/github/repo/dossier" \ --data-urlencode "url=https://github.com/facebook/react" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /github/user/profile-velocity User contribution velocity dossier Credit cost: 10 (premium) Parameters: - handle (required): GitHub username. - depth (optional, enum: quick | default | deep): `quick`, `default`, or `deep`. Defaults to `default`. Trades off upstream calls vs. dossier richness. ``` curl -G "https://www.socialcrawl.dev/v1/github/user/profile-velocity" \ --data-urlencode "handle=octocat" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## Google ### GET /google/search Google web search Credit cost: 1 (standard) Parameters: - query (required): Search keyword or phrase. Google operators such as site:example.com work. - region (optional, string): 2 letter country code, ie US, UK, CA, KR, etc This will show results from that country - date_posted (optional, enum: last-hour | last-day | last-week | last-month | last-year): Only pages Google dates within this window: last-hour, last-day, last-week, last-month or last-year. The strongest recency filter for forum and community posts. - page (optional, integer): Page number to retrieve - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/google/search" \ --data-urlencode "query=best restaurants in London" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /google/ad Get Google ad details Credit cost: 5 (advanced) Parameters: - url (required): Ads Transparency Center CREATIVE URL: must include both the advertiser and creative segments (`…/advertiser/{AR…}/creative/{CR…}`). Get one from `/v1/google/company/ads`. ``` curl -G "https://www.socialcrawl.dev/v1/google/ad" \ --data-urlencode "url=https://adstransparency.google.com/advertiser/AR01614014350098432001/creative/CR04647239345386815489" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /google/adlibrary/advertisers/search Search Google Ad Library advertisers Credit cost: 5 (advanced) Parameters: - query (required): Search keyword or phrase to find advertisers in the Google Ads Transparency Center. - region (optional, string): 2-letter country code to search in. Defaults to US. ``` curl -G "https://www.socialcrawl.dev/v1/google/adlibrary/advertisers/search" \ --data-urlencode "query=lululemon" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /google/company/ads List Google ads by company (oneOf: domain|advertiser_id) Credit cost: 5 (advanced) Parameters: - domain (optional, string): Company domain name to look up ads for - advertiser_id (optional, string): The advertiser id of the company - topic (optional, enum: all | political): The topic to search for. If you search for 'political', you will also need to pass a 'region', like 'US' or 'AU' - region (optional, string): The region to search for. Defaults to anywhere - start_date (optional, string): Start date to search for. Format: YYYY-MM-DD - end_date (optional, string): End date to search for. Format: YYYY-MM-DD - platform (optional, enum: google_maps | google_play | google_search | google_shopping | youtube): Google surface to filter ads by (e.g. youtube, google_search) - format (optional, enum: text | image | video): Ad format to filter by: text, image, or video - get_ad_details (optional, string): Set to true to get the ad details. Will cost 25 credits. - cursor (optional, string): Cursor to paginate through results - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. - Constraint: one of domain, advertiser_id (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/google/company/ads" \ --data-urlencode "domain=lululemon.com" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /google/business/info Get a Google Business Profile (oneOf: keyword|cid|place_id) Credit cost: 1 (standard) Parameters: - keyword (optional, string): Business name + address (e.g. 'Irving Farm New York 645 5th Ave'). Use cid/place_id when known for an exact match. - cid (optional, string): Google customer id (cid) of the place: the most reliable identifier. - place_id (optional, string): Google place_id of the place. - location_name (optional, string): Geographic context as 'City,Region,Country'. Without it, a keyword that looks Polish (a Polish postcode or Polish-only letters) is searched in Poland and every other keyword in 'New York,New York,United States'. - language_name (optional, string): Result language (default 'English'). - Constraint: one of keyword, cid, place_id (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/google/business/info" \ --data-urlencode "keyword=Irving Farm New York 645 5th Ave" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /google/business/extended-reviews Get Google extended (multi-source) reviews (oneOf: keyword|cid|place_id) Credit cost: 5-9 (metered): 5 credits per page. The default labels (sentiment, issue) are free. label=reports, incentivized or injection holds 4 extra credits and refunds down to 1 credit per started 25 reviews that were newly judged (the first 100 reviews of a page are judged): reviews already labelled are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. Parameters: - keyword (optional, string): Business name + address. Use cid/place_id for an exact match. - cid (optional, string): Google customer id (cid) of the place. - place_id (optional, string): Google place_id of the place. - location_name (optional, string): Geographic context as 'City,Region,Country'. - language_name (optional, string): Result language (default 'English'). - depth (optional, integer): Number of reviews to return (default 20, step 20, max 1000). - label (optional, string): Optional CSV of SocialCrawl labels to add to every review. Without this param every page already carries sentiment and issue, free; label= adds the labels you name to them (the defaults keep running). sentiment and issue are free when asked for too; reports, incentivized and injection add 1 credit per started 25 newly judged reviews. judgments=off (or label=none) turns the default labels off. They read the review's title and text, never its stars. sentiment: how the reviewer feels, on five levels (level 0 to 4, score_0_1, confidence), plus rating_mismatch, true when the words clearly contradict the star rating (a glowing text under 1 star, a furious one under 5), null when the review has no rating. issue: the main problem the review reports (label: product_defect, sizing_or_fit, shipping_or_delivery, customer_service, price_or_value, missing_feature, other, or none, with confidence; label is null when unsure). reports (needs reports=): does this review say that the thing you describe happened (p, 0 to 1). incentivized: does the reviewer say they got the product free, discounted or rewarded for the review (p), and did the text carry a disclosure such as 'in exchange for my honest review', Vine or 체험단 (disclosed). injection flags text that addresses an AI system and tries to direct it (flagged, p); it never drops or rewrites a row. These are signals to read, never a verdict on a review or a reviewer. A review that could not be judged carries labels: null. Reviews already labelled for anyone are free, and so is a cached page. data.labels reports what was judged and billed. - reports (optional, string): Required by label=reports, ignored otherwise. What to look for, in plain words, up to 300 characters, for example reports=the battery drains quickly or reports=배송이 늦음. Every review gains labels.reports.p, the probability that it says so, in any wording or language. Without it label=reports is skipped with the warning label_reports_needs_reports and is not billed. - judgments (optional, enum: on | off): Optional, on (the default) or off. By default every row gains free SocialCrawl judgments (computed.labels, and computed.relevance on search endpoints), reported in data.labels (mode default) and data.relevance (origin default), each with a status (complete, partial or skipped) and pending: the rows still being judged when the page was sent, which carry null now and are filled on your next call or cached read. Default judgments never add credits, never change an existing field, and never drop or reorder a row. off returns the page exactly as before, with none of those keys. label=none does the same. - dry_run (optional, enum: 1): Optional. When 1, return a cost preview for this labelled or relevance-filtered request without fetching the page or judging any row. data.estimate reports rows_expected, rows_cached, label_credits_min, label_credits_max and base_credits. 0 credits charged. - label_evidence (optional, enum: 1): Optional, only with label=. When 1, every labelled row also carries computed.labels_evidence. = { quote, sentence_index }: the sentence in the row that most clearly shows the label, copied verbatim. Absent or null when no single sentence shows it. - fit (optional, enum: goal): Optional. When goal, keep the rows and fields needed for the goal you pass in goal= (plus any that are uncertain, and the first and last), and replace the rest with a stub. data.held_back lists the held ids and a recall id that re-reads the full page from cache at no extra charge. Without this param the page is unchanged. - goal (optional, string): Required by fit=goal. What you are trying to do, in your own words, up to 300 characters. - fit_tokens (optional, integer): Optional, only with fit=goal. Soft cap on how much of the page to keep, in tokens. Uncertain blocks and the first and last block are kept even if they exceed it. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - Constraint: one of keyword, cid, place_id (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/google/business/extended-reviews" \ --data-urlencode "keyword=Irving Farm New York 645 5th Ave" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /google/business/updates Get Google Business Profile posts (updates) (oneOf: keyword|cid) Credit cost: 1 (standard) Parameters: - keyword (optional, string): Business name + location (e.g. 'Toyota of Manhattan New York'). - cid (optional, string): Google customer id (cid) of the business. - location_name (optional, string): Geographic context as 'City,Region,Country'. - language_name (optional, string): Result language (default 'English'). - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - Constraint: one of keyword, cid (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/google/business/updates" \ --data-urlencode "keyword=Toyota of Manhattan New York" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /google/business/questions Get Google Business Profile questions & answers (oneOf: keyword|cid|place_id) Credit cost: 5 (advanced) Parameters: - keyword (optional, string): Business name + location (e.g. 'Starbucks Reserve Roastery New York'). - cid (optional, string): Google customer id (cid) of the business. - place_id (optional, string): Google place_id of the business. - location_name (optional, string): Geographic context as 'City,Region,Country'. - language_name (optional, string): Result language (default 'English'). - depth (optional, integer): Number of questions to return (default 20). - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - Constraint: one of keyword, cid, place_id (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/google/business/questions" \ --data-urlencode "keyword=Starbucks Reserve Roastery New York" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /google/business/search Search Google Maps businesses (oneOf: location|lat) Credit cost: 5 (advanced) Parameters: - query (required): What to look for, as you would type it into Google Maps: a category or trade ('dentista', 'ristorante', 'commercialista') or a business name. Use the local language for local results. - location (optional, string): Where to search: 'City, Country' (e.g. 'Milano, Italy'), a country name or ISO code ('Italy', 'IT'), or a full City,Region,Country name ('Milan,Lombardy,Italy'). Required unless lat and lng are sent. - lat (optional, string): Latitude of the search centre (-90 to 90). Send with lng instead of location. - lng (optional, string): Longitude of the search centre (-180 to 180). Send with lat instead of location. - zoom (optional, integer): Google Maps zoom level around lat/lng, 3 (continent) to 21 (street). Defaults to 14, about a city district. Only with lat/lng. - language (optional, string): Language code for the results ('it', 'en', 'de'). Defaults to the main language of the country in location ('it' for Italy), or 'en' for coordinates. - limit (optional, integer): How many places to return, 1-100 (default 20). The price is the same at any limit. - Constraint: one of location, lat (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/google/business/search" \ --data-urlencode "query=dentista" \ --data-urlencode "location=Milano, Italy" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /google/hotels/search Search Google hotels Credit cost: 1 (standard) Parameters: - keyword (required): Hotel search query (e.g. 'hotels in New York'). - location_name (optional, string): Geographic context as 'City,Region,Country'. - language_name (optional, string): Result language (default 'English'). - check_in (optional, string): Check-in date (YYYY-MM-DD). Defaults to the next day. - check_out (optional, string): Check-out date (YYYY-MM-DD). Defaults to one night. ``` curl -G "https://www.socialcrawl.dev/v1/google/hotels/search" \ --data-urlencode "keyword=hotels in New York" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /google/hotels/info Get Google hotel detail Credit cost: 5 (advanced) Parameters: - hotel_identifier (required): Opaque hotel id returned by GET /v1/google/hotels/search. - location_name (optional, string): Geographic context as 'City,Region,Country'. - language_name (optional, string): Result language (default 'English'). ``` curl -G "https://www.socialcrawl.dev/v1/google/hotels/info" \ --data-urlencode "hotel_identifier=ChkIoYjXwK-S_okHGg0vZy8xMW1fd3MzY243EAE" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /google/keyword-volume Get Google keyword search volume by country Credit cost: 16 (override; tier premium) Parameters: - keywords (required): 1-20 comma-separated keywords (e.g. 'lip gloss,lip oil'). Each keyword may be up to 80 characters. Duplicates are ignored. More than 20 keywords, or a keyword over 80 characters, is rejected with a free 400 before the request is billed. - location (optional, string): Country as an ISO code ('US', 'GB', 'KR'), a full country name ('United Kingdom', 'South Korea') or a numeric location code ('2826'). Defaults to the United States. Volume is published for 94 countries; any other location is rejected with a free 400 naming the accepted forms. - language (optional, string): Language code ('en', 'de', 'ko') the keywords are searched in. Defaults to the first language listed for the country: Germany is 'de' and Belgium is 'fr', but Vietnam, Indonesia, India, Malaysia, Pakistan, the Philippines, Hong Kong and Singapore default to 'en', so pass their local language (e.g. 'vi', 'id', 'hi') explicitly. A language the country does not hold (Germany in English) is rejected with a free 400 listing the ones it does. ``` curl -G "https://www.socialcrawl.dev/v1/google/keyword-volume" \ --data-urlencode "keywords=lip gloss,lip oil" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## Google_news ### GET /google_news/search Search Google News Credit cost: 1 (standard) Parameters: - keyword (required): Search query (e.g. 'openai'). Supports any language; pass the matching language_code. Advanced operators (site:, intitle:, cache:, …) are not supported. - depth (optional, integer): Number of articles to retrieve (default 10, multiples of 10, max 100). The response is cut to exactly this many. No pagination beyond depth. - location_code (optional, integer): Google location code (e.g. 2840 = United States: the codes are 2000 + the ISO 3166 numeric code, so small integers are not valid and are rejected). Use one of location_code / location_name / location_coordinate. - location_name (optional, string): Google location name (e.g. 'South Korea'). - location_coordinate (optional, string): GPS target as 'latitude,longitude,radius_mm' (e.g. '40.7128,-74.0060,200') for local-news radius queries. - language_code (optional, string): Google language code (e.g. 'en', 'ko'). Default 'en'. Without a location, en and es get the US edition and most other languages their home edition (bn = Bangladesh, ko = South Korea). Region codes such as pt-BR, zh-TW or zh-Hans are accepted and pick that edition. - time_range (optional, enum: hour | day | week | month | year): Only return articles published within this window: hour, day, week, month, or year. Ignored when from/to is present. Resolution is a calendar DAY on the primary source, so `day` means 'since yesterday', not the last 24 hours; only `hour` is a true rolling window, and it is served by the fallback. - publisher (optional, string): Limit results to a single outlet's domain (e.g. 'bbc.com'). Bare domain only, no scheme, no path. Not combinable with location_coordinate or time_range=hour. - from (optional, string): Start of an exact publish-date window, as YYYY-MM-DD or a Unix timestamp. Overrides time_range. Not combinable with location_coordinate or time_range=hour. - to (optional, string): End of the publish-date window (inclusive), as YYYY-MM-DD or a Unix timestamp. Defaults to now when only from is set. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. ``` curl -G "https://www.socialcrawl.dev/v1/google_news/search" \ --data-urlencode "keyword=openai" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## Google_play ### GET /google_play/app-search Search Google Play apps by keyword Credit cost: 5 (advanced) Parameters: - query (required): Search keyword (e.g. 'photo editor'). - country (optional, string): Storefront country as an ISO code ('US'), a full name ('United States'), or a numeric location code ('2840'). Defaults to the US. - language (optional, string): Language code (e.g. 'en'). Defaults to 'en'. - depth (optional, integer): Number of results to retrieve (default 30, rounded up to multiples of 30, max 300). Note: Google Play's organic keyword search typically exposes only ~30 ranked results, so higher depth values rarely return more. To page deeper for app discovery, use /v1/google_play/app-listings-search (a filterable title/description database with real limit/offset/total). ``` curl -G "https://www.socialcrawl.dev/v1/google_play/app-search" \ --data-urlencode "query=photo editor" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /google_play/search-suggestions Get Google Play search suggestions Credit cost: 1 (standard) Parameters: - query (required): Partial search keyword to autocomplete (e.g. 'reverse'). - country (optional, string): ISO 3166-1 alpha-2 storefront country code (e.g. 'us', 'gb', 'kr'). Defaults to 'us'. Anything that is not alpha-2 is rejected with a free 400; an unrecognised alpha-2 code is forwarded, and Google Play answers it with its default storefront rather than an error, so check the code if results look wrong for the market. - language (optional, string): ISO 639-1 language code. Defaults to 'en'. ``` curl -G "https://www.socialcrawl.dev/v1/google_play/search-suggestions" \ --data-urlencode "query=reverse" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /google_play/app-info Get full Google Play app details Credit cost: 5 (advanced) Parameters: - app_id (required): Google Play package name (e.g. 'com.spotify.music'), from app-search. - country (optional, string): Storefront country as an ISO code ('US'), a full name, or a numeric code. Defaults to the US. - language (optional, string): Language code (e.g. 'en'). Defaults to 'en'. ``` curl -G "https://www.socialcrawl.dev/v1/google_play/app-info" \ --data-urlencode "app_id=com.spotify.music" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /google_play/app-reviews Get Google Play reviews for an app Credit cost: 5-9 (metered): 5 credits per page. The default labels (sentiment, issue) are free. label=reports, incentivized or injection holds 4 extra credits and refunds down to 1 credit per started 25 reviews that were newly judged (the first 100 reviews of a page are judged): reviews already labelled are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. Parameters: - app_id (required): Google Play package name (e.g. 'com.spotify.music'), from app-search. - country (optional, string): Storefront country as an ISO code ('US'), a full name, or a numeric code. Defaults to the US. - language (optional, string): Language code (e.g. 'en'). Defaults to 'en'. - depth (optional, integer): Number of reviews to retrieve (default 150, batches of 150, max 600). - sort_by (optional, enum: newest | most_relevant): Review ordering: `newest` (default) or `most_relevant`. - rating (optional, integer): Filter to a single star rating (1-5). Verified working on this store (measured 06/09/2026: `rating=5` returns 150 five-star reviews, `rating=1` returns 150 one-star). The Apple App Store has no equivalent filter upstream. - label (optional, string): Optional CSV of SocialCrawl labels to add to every review. Without this param every page already carries sentiment and issue, free; label= adds the labels you name to them (the defaults keep running). sentiment and issue are free when asked for too; reports, incentivized and injection add 1 credit per started 25 newly judged reviews. judgments=off (or label=none) turns the default labels off. They read the review's title and text, never its stars. sentiment: how the reviewer feels, on five levels (level 0 to 4, score_0_1, confidence), plus rating_mismatch, true when the words clearly contradict the star rating (a glowing text under 1 star, a furious one under 5), null when the review has no rating. issue: the main problem the review reports (label: product_defect, sizing_or_fit, shipping_or_delivery, customer_service, price_or_value, missing_feature, other, or none, with confidence; label is null when unsure). reports (needs reports=): does this review say that the thing you describe happened (p, 0 to 1). incentivized: does the reviewer say they got the product free, discounted or rewarded for the review (p), and did the text carry a disclosure such as 'in exchange for my honest review', Vine or 체험단 (disclosed). injection flags text that addresses an AI system and tries to direct it (flagged, p); it never drops or rewrites a row. These are signals to read, never a verdict on a review or a reviewer. A review that could not be judged carries labels: null. Reviews already labelled for anyone are free, and so is a cached page. data.labels reports what was judged and billed. - reports (optional, string): Required by label=reports, ignored otherwise. What to look for, in plain words, up to 300 characters, for example reports=the battery drains quickly or reports=배송이 늦음. Every review gains labels.reports.p, the probability that it says so, in any wording or language. Without it label=reports is skipped with the warning label_reports_needs_reports and is not billed. - judgments (optional, enum: on | off): Optional, on (the default) or off. By default every row gains free SocialCrawl judgments (computed.labels, and computed.relevance on search endpoints), reported in data.labels (mode default) and data.relevance (origin default), each with a status (complete, partial or skipped) and pending: the rows still being judged when the page was sent, which carry null now and are filled on your next call or cached read. Default judgments never add credits, never change an existing field, and never drop or reorder a row. off returns the page exactly as before, with none of those keys. label=none does the same. - dry_run (optional, enum: 1): Optional. When 1, return a cost preview for this labelled or relevance-filtered request without fetching the page or judging any row. data.estimate reports rows_expected, rows_cached, label_credits_min, label_credits_max and base_credits. 0 credits charged. - label_evidence (optional, enum: 1): Optional, only with label=. When 1, every labelled row also carries computed.labels_evidence. = { quote, sentence_index }: the sentence in the row that most clearly shows the label, copied verbatim. Absent or null when no single sentence shows it. - fit (optional, enum: goal): Optional. When goal, keep the rows and fields needed for the goal you pass in goal= (plus any that are uncertain, and the first and last), and replace the rest with a stub. data.held_back lists the held ids and a recall id that re-reads the full page from cache at no extra charge. Without this param the page is unchanged. - goal (optional, string): Required by fit=goal. What you are trying to do, in your own words, up to 300 characters. - fit_tokens (optional, integer): Optional, only with fit=goal. Soft cap on how much of the page to keep, in tokens. Uncertain blocks and the first and last block are kept even if they exceed it. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off the page, and the page that reaches one ends the walk: `next_cursor` is not returned and `pagination.stopped_at` is `since`. Sending it sets the newest-first sort shown in this endpoint's sort parameter (any other sort value is a free 400). The page still costs what a page costs, so you pay for the pages you walk and no more. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. ``` curl -G "https://www.socialcrawl.dev/v1/google_play/app-reviews" \ --data-urlencode "app_id=com.spotify.music" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /google_play/app-list Get a Google Play store chart Credit cost: 5 (advanced) Parameters: - app_collection (required): Chart to retrieve: topselling_free, topselling_paid, topgrossing, movers_shakers, topselling_new_free, topselling_new_paid. An unknown value is rejected with a free 400 that lists the accepted set. - app_category (optional, enum: art_and_design | auto_and_vehicles | beauty | books_and_reference | business | comics | communication | dating | education | entertainment | events | finance | food_and_drink | health_and_fitness | house_and_home | libraries_and_demo | lifestyle | maps_and_navigation | medical | music_and_audio | news_and_magazines | parenting | personalization | photography | productivity | shopping | social | sports | tools | travel_and_local | video_players | android_wear | watch_face | weather | game | game_action | game_adventure | game_arcade | game_board | game_card | game_casino | game_casual | game_educational | game_music | game_puzzle | game_racing | game_role_playing | game_simulation | game_sports | game_strategy | game_trivia | game_word | family): Optional Google Play category to scope the chart (e.g. 'photography', 'game_puzzle'). - country (optional, string): Storefront country as an ISO code ('US'), a full name, or a numeric code. Defaults to the US. - language (optional, string): Language code (e.g. 'en'). Defaults to 'en'. - depth (optional, integer): Number of apps to retrieve (default 100, rounded up to multiples of 100, max 500). The store decides how deep a chart actually goes: measured 06/09/2026, `topselling_free` returns 100 at the default and 200 at depth=500, and `topselling_new_paid` has only 5 apps in it. ``` curl -G "https://www.socialcrawl.dev/v1/google_play/app-list" \ --data-urlencode "app_collection=topselling_free" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /google_play/app-listings-search Search the Google Play listings database (paginated) Credit cost: 10 (premium) Parameters: - title (required): App title to search for (e.g. 'photo editor'). - description (optional, string): Optional app-description text to match. - limit (optional, integer): Apps per page (1-50, default 20). - offset (optional, integer): Pagination offset (up to 10,000). Prefer `cursor`: pass `pagination.next_cursor` straight back and the offset is handled for you. - offset_token (optional, string): Legacy deep-pagination token. Prefer `cursor`, which accepts `pagination.next_cursor` verbatim and works on every paginatable endpoint. - filters (optional, string): Advanced filter expression as a JSON array of clauses, e.g. [["item.rating.value",">",4]]. Must be valid JSON and must be an array: anything else is rejected with a free 400 rather than silently returning an unfiltered page. - country (optional, string): Storefront country as an ISO code ('US'), a full name, or a numeric code. Defaults to the US. - language (optional, string): Language code (e.g. 'en'). Defaults to 'en'. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/google_play/app-listings-search" \ --data-urlencode "title=photo editor" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /google_play/categories List Google Play app categories Credit cost: 1 (standard) ``` curl "https://www.socialcrawl.dev/v1/google_play/categories" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /google_play/locations List supported Google Play storefront locations Credit cost: 1 (standard) Parameters: - location_type (optional, enum: country | all): `country` (default) returns the storefronts the `country` param accepts. `all` returns the full underlying geo tree, including cities, postal codes and neighbourhoods, which all resolve to their parent country. ``` curl "https://www.socialcrawl.dev/v1/google_play/locations" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /google_play/languages List supported Google Play languages Credit cost: 1 (standard) ``` curl "https://www.socialcrawl.dev/v1/google_play/languages" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## Google_shopping ### GET /google_shopping/product-search Search Google Shopping products Credit cost: 5 (advanced) Parameters: - query (required): Product search keyword (e.g. 'wireless earbuds'). - country (optional, string): Country as an ISO code ('US', 'GB') or full name ('United States'). Defaults to United States. - language (optional, string): Language code (e.g. 'en'). Defaults to en. - depth (optional, integer): Number of product results to retrieve (default 40, max 120). More results = longer task time. - price_min (optional, string): Minimum product price filter. - price_max (optional, string): Maximum product price filter. - sort_by (optional, enum: review_score | price_low_to_high | price_high_to_low): Result ordering: review_score, price_low_to_high, or price_high_to_low. ``` curl -G "https://www.socialcrawl.dev/v1/google_shopping/product-search" \ --data-urlencode "query=wireless earbuds" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /google_shopping/product Get Google Shopping product detail (oneOf: product_id|gid|data_docid) Credit cost: 1 (standard) Parameters: - product_id (optional, string): Google Shopping product_id (from product-search). - gid (optional, string): Google Shopping gid (from product-search). - data_docid (optional, string): Google Shopping data_docid (from product-search). - country (optional, string): Country as an ISO code ('US', 'GB') or full name ('United States'). Defaults to United States. - language (optional, string): Language code. Defaults to en. - Constraint: one of product_id, gid, data_docid (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/google_shopping/product" \ --data-urlencode "product_id=1749519698216963169" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /google_shopping/reviews Get Google Shopping product reviews Credit cost: 1-5 (metered): 1 credit per page. The default labels (sentiment, issue) are free. label=reports, incentivized or injection holds 4 extra credits and refunds down to 1 credit per started 25 reviews that were newly judged (the first 100 reviews of a page are judged): reviews already labelled are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. Parameters: - gid (required): Google Shopping gid of the product (from product-search). Required. - product_id (optional, string): Google Shopping product_id (recommended for accuracy). - data_docid (optional, string): Google Shopping data_docid (recommended for accuracy). - depth (optional, integer): Number of reviews to retrieve (default 10, multiples of 10, max 8000). - country (optional, string): Country as an ISO code ('US', 'GB') or full name ('United States'). Defaults to United States. - language (optional, string): Language code. Defaults to en. - label (optional, string): Optional CSV of SocialCrawl labels to add to every review. Without this param every page already carries sentiment and issue, free; label= adds the labels you name to them (the defaults keep running). sentiment and issue are free when asked for too; reports, incentivized and injection add 1 credit per started 25 newly judged reviews. judgments=off (or label=none) turns the default labels off. They read the review's title and text, never its stars. sentiment: how the reviewer feels, on five levels (level 0 to 4, score_0_1, confidence), plus rating_mismatch, true when the words clearly contradict the star rating (a glowing text under 1 star, a furious one under 5), null when the review has no rating. issue: the main problem the review reports (label: product_defect, sizing_or_fit, shipping_or_delivery, customer_service, price_or_value, missing_feature, other, or none, with confidence; label is null when unsure). reports (needs reports=): does this review say that the thing you describe happened (p, 0 to 1). incentivized: does the reviewer say they got the product free, discounted or rewarded for the review (p), and did the text carry a disclosure such as 'in exchange for my honest review', Vine or 체험단 (disclosed). injection flags text that addresses an AI system and tries to direct it (flagged, p); it never drops or rewrites a row. These are signals to read, never a verdict on a review or a reviewer. A review that could not be judged carries labels: null. Reviews already labelled for anyone are free, and so is a cached page. data.labels reports what was judged and billed. - reports (optional, string): Required by label=reports, ignored otherwise. What to look for, in plain words, up to 300 characters, for example reports=the battery drains quickly or reports=배송이 늦음. Every review gains labels.reports.p, the probability that it says so, in any wording or language. Without it label=reports is skipped with the warning label_reports_needs_reports and is not billed. - judgments (optional, enum: on | off): Optional, on (the default) or off. By default every row gains free SocialCrawl judgments (computed.labels, and computed.relevance on search endpoints), reported in data.labels (mode default) and data.relevance (origin default), each with a status (complete, partial or skipped) and pending: the rows still being judged when the page was sent, which carry null now and are filled on your next call or cached read. Default judgments never add credits, never change an existing field, and never drop or reorder a row. off returns the page exactly as before, with none of those keys. label=none does the same. - dry_run (optional, enum: 1): Optional. When 1, return a cost preview for this labelled or relevance-filtered request without fetching the page or judging any row. data.estimate reports rows_expected, rows_cached, label_credits_min, label_credits_max and base_credits. 0 credits charged. - label_evidence (optional, enum: 1): Optional, only with label=. When 1, every labelled row also carries computed.labels_evidence. = { quote, sentence_index }: the sentence in the row that most clearly shows the label, copied verbatim. Absent or null when no single sentence shows it. - fit (optional, enum: goal): Optional. When goal, keep the rows and fields needed for the goal you pass in goal= (plus any that are uncertain, and the first and last), and replace the rest with a stub. data.held_back lists the held ids and a recall id that re-reads the full page from cache at no extra charge. Without this param the page is unchanged. - goal (optional, string): Required by fit=goal. What you are trying to do, in your own words, up to 300 characters. - fit_tokens (optional, integer): Optional, only with fit=goal. Soft cap on how much of the page to keep, in tokens. Uncertain blocks and the first and last block are kept even if they exceed it. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. ``` curl -G "https://www.socialcrawl.dev/v1/google_shopping/reviews" \ --data-urlencode "gid=3591805395819257241" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /google_shopping/sellers Get Google Shopping sellers for a product (oneOf: product_id|gid|data_docid) Credit cost: 1 (standard) Parameters: - product_id (optional, string): Google Shopping product_id (from product-search). - gid (optional, string): Google Shopping gid (from product-search). - data_docid (optional, string): Google Shopping data_docid (from product-search). - country (optional, string): Country as an ISO code ('US', 'GB') or full name ('United States'). Defaults to United States. - language (optional, string): Language code. Defaults to en. - Constraint: one of product_id, gid, data_docid (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/google_shopping/sellers" \ --data-urlencode "product_id=1749519698216963169" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /google_shopping/price-history Get Google Shopping price history for a product Credit cost: 1 (standard) Parameters: - product_id (required): Google Shopping product_id from GET /v1/google_shopping/product-search. - country (optional, string): Country as an ISO code such as US (default US). ``` curl -G "https://www.socialcrawl.dev/v1/google_shopping/price-history" \ --data-urlencode "product_id=catalogid:7310953537322941042,productid:2695666608789071335" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## Google_trends ### GET /google_trends/explore Get Google Trends interest over time Credit cost: 5 (advanced) Parameters: - keywords (required): 1-5 comma-separated keywords (e.g. 'reverse audio,voice changer'). With multiple keywords the 0-100 values are normalised across the set for direct comparison. Each keyword must be 2 to 100 characters. More than 5 keywords, a 1-character keyword, or a keyword made only of punctuation is rejected with a free 400 before the request is billed. - location (optional, string): Location as an ISO country code ('US', 'KR'), a full country name as Google Trends spells it ('United States', 'South Korea'), or a numeric location code ('2840'). Common spellings and ISO codes are normalised, so 'KR', 'Korea' and 'South Korea' all reach the same place. Google Trends does not publish every country, and a location it has no data for is rejected with a free 400 naming the accepted forms; a 404 means the keyword had no data there, not that the location is unsupported. Defaults to worldwide-leaning US. - timeframe (optional, enum: past_hour | past_4_hours | past_day | past_7_days | past_30_days | past_90_days | past_12_months | past_5_years): Preset time window: past_hour, past_4_hours, past_day, past_7_days, past_30_days, past_90_days, past_12_months, or past_5_years. Defaults to past_12_months. - category (optional, integer): Numeric Google Trends category code to scope the query (default 0 = all categories). A non-numeric value is rejected with a free 400 rather than silently returning an all-categories series. ``` curl -G "https://www.socialcrawl.dev/v1/google_trends/explore" \ --data-urlencode "keywords=uv index" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /google_trends/rising Get related + rising Google Trends queries Credit cost: 5 (advanced) Parameters: - keyword (required): Single keyword to expand (e.g. 'uv index'). Google Trends returns the related-queries list for one keyword only, so a comma-separated value is rejected with a free 400 before the request is billed. The keyword must be 2 to 100 characters; use /v1/google_trends/explore for multi-keyword comparisons. - location (optional, string): Location as an ISO country code ('US', 'KR'), a full country name as Google Trends spells it ('United States', 'South Korea'), or a numeric location code ('2840'). Common spellings and ISO codes are normalised, so 'KR', 'Korea' and 'South Korea' all reach the same place. Google Trends does not publish every country, and a location it has no data for is rejected with a free 400 naming the accepted forms; a 404 means the keyword had no data there, not that the location is unsupported. Defaults to worldwide-leaning US. - timeframe (optional, enum: past_hour | past_4_hours | past_day | past_7_days | past_30_days | past_90_days | past_12_months | past_5_years): Preset time window: past_hour, past_4_hours, past_day, past_7_days, past_30_days, past_90_days, past_12_months, or past_5_years. Defaults to past_12_months. - category (optional, integer): Numeric Google Trends category code to scope the related-query lists (default 0 = all categories). The same codes /v1/google_trends/explore accepts; 0 is every category. A non-numeric value is rejected with a free 400. ``` curl -G "https://www.socialcrawl.dev/v1/google_trends/rising" \ --data-urlencode "keyword=uv index" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /google_trends/trending Get Google Trends Trending Now for a location Credit cost: 5 (advanced) Parameters: - location (required): Where to read trends. An ISO country code ('DE', 'US', 'KR'), its English name ('Germany'), or an ISO 3166-2 region inside it ('DE-BY', 'US-CA', 'GB-SCT'). Google covers 125 countries and has no worldwide list; a country it does not cover is rejected with a free 400. - hours (optional, enum: 4 | 24 | 48 | 168): Only trends that started in the past 4, 24, 48 or 168 hours (7 days). Defaults to 24. - category (optional, enum: autos_and_vehicles | beauty_and_fashion | business_and_finance | entertainment | food_and_drink | games | health | hobbies_and_leisure | jobs_and_education | law_and_government | other | pets_and_animals | politics | science | shopping | sports | technology | travel_and_transportation | climate): Keep trends Google tagged with this category: autos_and_vehicles, beauty_and_fashion, business_and_finance, climate, entertainment, food_and_drink, games, health, hobbies_and_leisure, jobs_and_education, law_and_government, other, pets_and_animals, politics, science, shopping, sports, technology, or travel_and_transportation. These are Trending Now's categories, not the numeric codes /v1/google_trends/explore takes. Defaults to every category. - status (optional, enum: all | active): `active` keeps only trends that are still trending. Defaults to `all`, which also returns trends that have already ended inside the window. - sort (optional, enum: relevance | search_volume | recency | title): relevance (Google's own order, the default), search_volume (largest first), recency (most recently started first), or title (A to Z). - limit (optional, integer): How many trends to return after filtering and sorting, 1 to 500. Defaults to 100. ``` curl -G "https://www.socialcrawl.dev/v1/google_trends/trending" \ --data-urlencode "location=DE" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## Gumtree ### GET /gumtree/search Search Gumtree UK listings (oneOf: query|url) Credit cost: 5 (advanced) Parameters: - query (optional, string): Free-text listing search, for example iphone or golf mk7 diesel. - url (optional, string): A Gumtree web search URL, for example https://www.gumtree.com/search?q=iphone&search_location=london. Keyword, category, location, and price filters on the URL are parsed server-side. - page (optional, integer): Page number, starting at 1. Prefer the universal cursor parameter. - sort_by (optional, enum: relevance | newest | price_low | price_high): Result ordering: relevance (default), newest, price_low, or price_high. - category_id (optional, string): Gumtree category id or SEO slug from GET /v1/gumtree/categories or search/suggestions. Required for price, seller_type, and attributes filters to take effect. - location_id (optional, string): Gumtree location id from GET /v1/gumtree/locations or locations/nearest. - distance (optional, enum: 0 | 1 | 3 | 5 | 10 | 15 | 30 | 50 | 75 | 100 | 150 | 250 | 500 | nationwide): Search radius in miles around location_id. Ignored without a location_id. nationwide searches the whole UK. - min_price (optional, string): Lowest asking price in GBP. Requires category_id. Filter on product.price.current if you need a hard bound. - max_price (optional, string): Highest asking price in GBP. Requires category_id. - seller_type (optional, enum: private | trade): Restrict to private (individual) or trade (dealer) sellers. Requires category_id. - attributes (optional, string): Category-specific filters as comma-separated name:value pairs from GET /v1/gumtree/filters, for example common_for_sale_condition:new,vehicle_make:audi. Requires category_id. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. - Constraint: one of query, url (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/gumtree/search" \ --data-urlencode "query=iphone" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /gumtree/product Get a Gumtree listing by ad id or URL (oneOf: product_id|url) Credit cost: 5 (advanced) Parameters: - product_id (optional, string): Gumtree advertisement id, the numeric id at the end of a listing URL: gumtree.com/p/apple-iphone/iphone-11/1515115590 is ad id 1515115590. Ad ids also come back on every row of GET /v1/gumtree/search. - url (optional, string): Full Gumtree listing URL, for example https://www.gumtree.com/p/apple-iphone/iphone-11-11-pro-11-pro-max-unlocked-with-warranty/1515115590. The ad id is extracted from the path. - Constraint: one of product_id, url (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/gumtree/product" \ --data-urlencode "product_id=1515115590" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /gumtree/product/similar Get listings similar to a Gumtree ad (oneOf: product_id|url) Credit cost: 5 (advanced) Parameters: - product_id (optional, string): Gumtree advertisement id, the numeric id at the end of a listing URL: gumtree.com/p/apple-iphone/iphone-11/1515115590 is ad id 1515115590. Ad ids also come back on every row of GET /v1/gumtree/search. - url (optional, string): Full Gumtree listing URL, for example https://www.gumtree.com/p/apple-iphone/iphone-11-11-pro-11-pro-max-unlocked-with-warranty/1515115590. The ad id is extracted from the path. - Constraint: one of product_id, url (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/gumtree/product/similar" \ --data-urlencode "product_id=1515115590" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /gumtree/seller Get a Gumtree seller profile (oneOf: seller_id) Credit cost: 5 (advanced) Parameters: - seller_id (optional, string): Gumtree seller userId from a listing detail (product.ext.gumtree.user_id). Must be paired with public_id. - public_id (optional, string): Gumtree seller publicUserId from a listing detail (product.ext.gumtree.public_user_id). Must be paired with seller_id. - account_id (optional, string): Optional Gumtree accountId from a listing detail (product.ext.gumtree.account_id). - Constraint: one of seller_id (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/gumtree/seller" \ --data-urlencode "seller_id=17804454" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /gumtree/seller/listings List a Gumtree seller's active ads (oneOf: seller_id) Credit cost: 5 (advanced) Parameters: - seller_id (optional, string): Gumtree seller userId from a listing detail (product.ext.gumtree.user_id). Must be paired with public_id. - public_id (optional, string): Gumtree seller publicUserId from a listing detail (product.ext.gumtree.public_user_id). Must be paired with seller_id. - account_id (optional, string): Optional Gumtree accountId from a listing detail (product.ext.gumtree.account_id). - page (optional, integer): Page number, starting at 1. Prefer the universal cursor parameter. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. - Constraint: one of seller_id (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/gumtree/seller/listings" \ --data-urlencode "seller_id=17804454" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /gumtree/search/suggestions Get Gumtree search suggestions Credit cost: 5 (advanced) Parameters: - query (required): Partial search query to autocomplete, for example iph. - category_id (optional, string): Optional category id to scope suggestions to. ``` curl -G "https://www.socialcrawl.dev/v1/gumtree/search/suggestions" \ --data-urlencode "query=iphone" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /gumtree/trending Get trending Gumtree searches Credit cost: 1 (standard) ``` curl "https://www.socialcrawl.dev/v1/gumtree/trending" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /gumtree/categories Get the Gumtree category tree Credit cost: 1 (standard) Parameters: - category_id (optional, string): Optional category id or SEO slug. Returns that node's subtree; omit for the full tree. ``` curl "https://www.socialcrawl.dev/v1/gumtree/categories" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /gumtree/filters Get Gumtree filters for a category Credit cost: 1 (standard) Parameters: - category_id (required): Gumtree category id or SEO slug from GET /v1/gumtree/categories. ``` curl -G "https://www.socialcrawl.dev/v1/gumtree/filters" \ --data-urlencode "category_id=10205" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /gumtree/locations Autocomplete Gumtree locations Credit cost: 5 (advanced) Parameters: - query (required): Place name, postcode, or outcode, for example london or B92. - location_type (optional, enum: LOCATION | OUTCODE | POSTCODE): Optional filter: LOCATION, OUTCODE, or POSTCODE. ``` curl -G "https://www.socialcrawl.dev/v1/gumtree/locations" \ --data-urlencode "query=london" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /gumtree/locations/nearest Find the nearest Gumtree location (oneOf: location_coordinate|latitude) Credit cost: 5 (advanced) Parameters: - latitude (optional, string): Latitude. Use with longitude when you do not pass location_coordinate. - longitude (optional, string): Longitude. Requires latitude. - location_coordinate (optional, string): Latitude,longitude pair, for example 51.5074,-0.1278. - Constraint: one of location_coordinate, latitude (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/gumtree/locations/nearest" \ --data-urlencode "location_coordinate=51.5074,-0.1278" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## Hackernews ### GET /hackernews/search Search Hacker News Credit cost: 1-9 (metered): 1 credit per page. Relevance against your query (by default, or with relevance=) is free. Adding relevant_to= holds 4 extra credits and refunds down to 1 credit per started 25 rows that were newly judged: rows already judged for the same topic are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. The default labels (sponsored, intent, niche) are free. label=mention, quality, ai_style, ai_written, injection, or label=intent with offer=, holds 4 extra credits and refunds down to 1 credit per started 25 posts that were newly judged: posts already labelled are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - query (required): Free-text search term. - tags (optional, string): Algolia tag filter: comma-separated. Common values: "story", "comment", "poll", "show_hn", "ask_hn", "front_page", "author_". Defaults to "story". - numericFilters (optional, string): Algolia numeric filter expression on `created_at_i` (the only filterable numeric attribute): e.g. "created_at_i>1700000000". Combine with commas for AND. No filter is applied by default. - hitsPerPage (optional, integer): Hits per page (1-1000). Defaults to 30. - page (optional, integer): 0-indexed page number for pagination. - relevance (optional, enum: score | filter): Optional. Without this param every row already carries computed.relevance against your query, free ({ p, sense, depth, spam }: is this row about what your query means, or a different thing that shares its words?), and nothing is dropped or reordered. score asks for it explicitly and waits for every row; filter also drops the rows that are not about your query, and lists their ids in data.relevance.dropped_ids. Your query is the topic; nothing to configure. A row that could not be judged is never dropped and carries relevance: null. Pagination is unchanged, so a filtered page can hold fewer rows. Free with your query as the topic; with relevant_to it adds 1 credit per started 25 newly judged rows (rows already judged for the same topic are free, and so is a cached page). - relevance_threshold (optional, string): Optional, only with relevance. The probability (0 to 1) a row must reach to be kept by relevance=filter. Default 0.5. Lower keeps more rows, higher keeps fewer. - relevant_to (optional, string): Optional, only with relevance. Up to 200 characters describing what you mean, used as the topic instead of the query. Use it when the query is ambiguous, for example query=cleopatra with relevant_to=Cleopatra, the IGT slot game. - judgments (optional, enum: on | off): Optional, on (the default) or off. By default every row gains free SocialCrawl judgments (computed.labels, and computed.relevance on search endpoints), reported in data.labels (mode default) and data.relevance (origin default), each with a status (complete, partial or skipped) and pending: the rows still being judged when the page was sent, which carry null now and are filled on your next call or cached read. Default judgments never add credits, never change an existing field, and never drop or reorder a row. off returns the page exactly as before, with none of those keys. label=none does the same. - dry_run (optional, enum: 1): Optional. When 1, return a cost preview for this labelled or relevance-filtered request without fetching the page or judging any row. data.estimate reports rows_expected, rows_cached, label_credits_min, label_credits_max and base_credits. 0 credits charged. - fit (optional, enum: goal): Optional. When goal, keep the rows and fields needed for the goal you pass in goal= (plus any that are uncertain, and the first and last), and replace the rest with a stub. data.held_back lists the held ids and a recall id that re-reads the full page from cache at no extra charge. Without this param the page is unchanged. - goal (optional, string): Required by fit=goal. What you are trying to do, in your own words, up to 300 characters. - fit_tokens (optional, integer): Optional, only with fit=goal. Soft cap on how much of the page to keep, in tokens. Uncertain blocks and the first and last block are kept even if they exceed it. - label (optional, string): Optional CSV of SocialCrawl labels to add to every post. Without this param every page already carries sponsored, intent and niche, free; label= adds the labels you name to them (the defaults keep running). sponsored, intent and niche are free when asked for too; mention, quality, ai_style, intent with offer=, and injection add 1 credit per started 25 newly judged posts. judgments=off (or label=none) turns the default labels off. sponsored: is the post a paid or gifted promotion (p, 0 to 1), did it carry a disclosure marker such as #ad or 광고 (disclosed), is it likely paid with no marker (undisclosed), and which of the accounts it mentions does it promote (brand, or null). These are signals to review, never a finding. mention (needs brand=): is the post about that brand rather than something that shares its name (about_brand, 0 to 1), how it feels about the brand on five levels (sentiment_level 0 to 4 and sentiment_score 0 to 1, null when the post is not about the brand), is it sarcastic, which aspect it talks about (taste_or_quality, price_or_value, availability_or_delivery, health_or_safety, advertising_or_campaign, customer_service, none), and did the author buy or use it (first_hand). intent: what the author is mainly doing (label: asking_for_recommendation, comparing_options, switching_away, complaining, promoting, news_or_discussion, other, or null when unsure, with confidence), whether they read as a potential buyer rather than a seller (buyer, seller), how pressing the need is (urgency, 0 to 3), and, when you pass offer=, whether your offer would plausibly help them (fits_offer). niche: which of the 33 niches of the published taxonomy sc-niche-v1 the caption belongs to, or personal_no_niche, or other (label, confidence, taxonomy), with label null when the caption is too thin to tell or the pick is unsure. It reads the caption only, not the video. quality: how much checkable detail the caption carries (fact_density 0 to 3), whether it mainly asks for likes, replies, shares, follows or tags (engagement_bait, 0 to 1), whether it is written to provoke anger as a way to get engagement (rage_bait, 0 to 1; about the writing, never the side taken), whether it only repeats someone else's news or view (secondhand, 0 to 1), and what the post is mainly doing (post_aim: inform, opinion, sell, entertain, provoke, other). exclude=engagement_bait drops posts whose engagement_bait is 0.8 or higher. ai_style: writing patterns common in AI-generated text, judged on the text alone. It does not say who wrote the post, and a person who writes in this style is flagged too. Six patterns are judged, each a probability (0 to 1): binary_contrast ("This is not X. It's Y."), faux_insight ("Here's what nobody tells you" and throat-clearing openers), colon_reveal ("The best part: it learns."), profound_kicker (ends on an aphorism or mic-drop line), puffery ("marks a pivotal moment", "highlighting the importance of") and rhetorical_setup ("What if I told you", a question the post answers itself, "Not a X. Not a Y. A Z."). Four are counted in the text: slop_words (words and phrases such as delve, leverage, robust, game changer, at the end of the day), em_dashes, emoji_bullets (lines that start with an emoji or a symbol bullet) and one_line_paragraphs (paragraphs that are one short line). pattern_count is how many patterns are present (a judged pattern at 0.8 or higher, slop_words 1 or more, em_dashes 2 or more, emoji_bullets 2 or more, one_line_paragraphs 5 or more) and markers lists their names, comma-separated. A post with less than about eight words of text is only counted, with every judged pattern 0. The judged patterns read the first 400 and the last 150 characters of a long post, and the counts read the whole post. exclude=ai_style drops posts with 2 or more patterns. ai_written: how likely the post was written by an AI model, from SocialCrawl's own text classifier, reading the opening of the post (about 1,000 characters): p (0 to 1, calibrated), level (likely (p 0.95 or higher), possible (p 0.71 or higher) or unlikely) and model (the model name; a new name means a new scale). A post under 8 words is too short to read: p null, level too_short. It is a probability, not proof: a person can be flagged and an AI-written post can pass, and the shorter the post the less certain it is. exclude=ai_written drops likely posts. injection flags text that addresses an AI system and tries to direct it (flagged, p); it never drops or rewrites a row. A post that could not be judged carries labels: null. Posts already labelled for anyone are free, and so is a cached page. data.labels reports what was judged and billed. - exclude (optional, string): Optional CSV (engagement_bait, ai_style, ai_written). engagement_bait, only with label=quality, drops posts whose engagement_bait probability is 0.8 or higher. ai_style, only with label=ai_style, drops posts that show 2 or more of its writing patterns (pattern_count 2 or higher). ai_written, only with label=ai_written, drops posts whose level is likely (p 0.95 or higher), or whose p reaches your ai_written_threshold; a post too short to read is never dropped. Dropped posts are listed in data.labels.dropped_ids. A post that could not be judged is never dropped. - ai_written_threshold (optional, string): Optional, only with label=ai_written and exclude=ai_written. The p (0 to 1) at which exclude=ai_written drops a post. Default 0.95 (level likely). 0.71 also drops possible posts: more AI posts go, and more posts by people with them. - brand (optional, string): Required by label=mention, ignored otherwise. The brand or product the posts are judged against, up to 300 characters, for example brand=Buldak. Without it label=mention is skipped with the warning label_mention_needs_brand and is not billed. - brand_description (optional, string): Optional, with label=mention. One plain sentence saying what the brand is, for example brand_description=Samyang's spicy instant noodle brand. Use it when the name is also an ordinary word or another company's name. - offer (optional, string): Optional, with label=intent. One or two plain sentences saying what you sell, up to 300 characters, for example offer=A web design agency that builds websites for small businesses. Adds fits_offer to every post; without it fits_offer is null. - label_evidence (optional, enum: 1): Optional, only with label=. When 1, every labelled row also carries computed.labels_evidence. = { quote, sentence_index }: the sentence in the row that most clearly shows the label, copied verbatim. Absent or null when no single sentence shows it. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/hackernews/search" \ --data-urlencode "query=claude code" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /hackernews/story Get a Hacker News story Credit cost: 1 (standard) Parameters: - id (required): HN story id (the numeric `objectID`). ``` curl -G "https://www.socialcrawl.dev/v1/hackernews/story" \ --data-urlencode "id=8863" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /hackernews/story/comments Get comments on a Hacker News story Credit cost: 1 (standard) Parameters: - id (required): HN story id (the numeric `objectID`). - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. ``` curl -G "https://www.socialcrawl.dev/v1/hackernews/story/comments" \ --data-urlencode "id=8863" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /hackernews/profile Get a Hacker News user profile Credit cost: 1 (standard) Parameters: - handle (required): Hacker News username (case-sensitive, e.g. `pg` or `kogir`). ``` curl -G "https://www.socialcrawl.dev/v1/hackernews/profile" \ --data-urlencode "handle=pg" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## Hm ### GET /hm/search Search H&M products by keyword Credit cost: 5 (advanced) Parameters: - query (required): Search keyword, for example t-shirts, jeans, dress. - page (optional, integer): Page number, starting at 1. Prefer the universal cursor parameter. - limit (optional, integer): Products per page, 1 to 36. Default 36. Values above 36 are clamped; 100 crashes the origin. - sort_by (optional, enum: relevance | newest | price_low | price_high): Result ordering: relevance (default), newest, price_low, or price_high. - language (optional, string): Catalogue locale as language_region, for example en_us, en_gb, de_de, fr_fr. Omit for the US catalogue. This is not the two-letter locale from GET /v1/hm/countries. - filters (optional, string): H&M facet filter string. Example: colorWithNames:black_000000. Combine groups with || and values inside a group with |. Facet ids are listed on every search payload under searchHits.facets. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/hm/search" \ --data-urlencode "query=t-shirts" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /hm/search/suggestions Get H&M search suggestions Credit cost: 5 (advanced) Parameters: - query (required): Partial search query to autocomplete, for example tshirt. - language (optional, string): Catalogue locale as language_region, for example en_us, en_gb, de_de, fr_fr. Omit for the US catalogue. This is not the two-letter locale from GET /v1/hm/countries. ``` curl -G "https://www.socialcrawl.dev/v1/hm/search/suggestions" \ --data-urlencode "query=tshirt" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /hm/stores List H&M stores in a country Credit cost: 5 (advanced) Parameters: - query (required): Two-letter H&M market code, for example us, gb, de. Use GET /v1/hm/countries to list markets. This is a country dump, not a near-me search. - language (optional, string): Catalogue locale as language_region, for example en_us, en_gb, de_de, fr_fr. Omit for the US catalogue. This is not the two-letter locale from GET /v1/hm/countries. ``` curl -G "https://www.socialcrawl.dev/v1/hm/stores" \ --data-urlencode "query=us" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /hm/countries List H&M countries and languages Credit cost: 1 (standard) ``` curl "https://www.socialcrawl.dev/v1/hm/countries" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /hm/categories Get the H&M category tree Credit cost: 1 (standard) ``` curl "https://www.socialcrawl.dev/v1/hm/categories" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /hm/product/suppliers Get H&M suppliers and factories for a product Credit cost: 5 (advanced) Parameters: - product_id (required): H&M product id, the digits in a product page URL: www2.hm.com/en_us/productpage.0963662139.html is product id 0963662139. Product ids also come back on every row of GET /v1/hm/search. - language (optional, string): Catalogue locale as language_region, for example en_us, en_gb, de_de, fr_fr. Omit for the US catalogue. This is not the two-letter locale from GET /v1/hm/countries. ``` curl -G "https://www.socialcrawl.dev/v1/hm/product/suppliers" \ --data-urlencode "product_id=0963662139" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## Home_depot ### GET /home_depot/product Get a Home Depot product by item id or URL (oneOf: item_id|url) Credit cost: 5 (advanced) Parameters: - item_id (optional, string): Home Depot internet number (item id), the numeric id at the end of a homedepot.com product URL: homedepot.com/p//326680222 is item id 326680222. Pass url instead if you would rather hand over the whole product URL. - url (optional, string): Full Home Depot product page URL, used instead of item_id. The item id is extracted from it. - store_id (optional, string): Home Depot store number used to localise stock and pickup availability, for example 0159. Prices are Home Depot's online price and do not change with the store. It is a string: pass it exactly as GET /v1/home_depot/stores returns it (four digits, zero-padded). The store_inventory row with is_selected_store true is this store, and an unknown store number is rejected. - Constraint: one of item_id, url (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/home_depot/product" \ --data-urlencode "item_id=326680222" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /home_depot/reviews Get Home Depot product reviews Credit cost: 5-9 (metered): 5 credits per page. The default labels (sentiment, issue) are free. label=reports, incentivized or injection holds 4 extra credits and refunds down to 1 credit per started 25 reviews that were newly judged (the first 100 reviews of a page are judged): reviews already labelled are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. Parameters: - item_id (required): Home Depot internet number (item id), the numeric id at the end of a homedepot.com product URL: homedepot.com/p//326680222 is item id 326680222. Pass url instead if you would rather hand over the whole product URL. - label (optional, string): Optional CSV of SocialCrawl labels to add to every review. Without this param every page already carries sentiment and issue, free; label= adds the labels you name to them (the defaults keep running). sentiment and issue are free when asked for too; reports, incentivized and injection add 1 credit per started 25 newly judged reviews. judgments=off (or label=none) turns the default labels off. They read the review's title and text, never its stars. sentiment: how the reviewer feels, on five levels (level 0 to 4, score_0_1, confidence), plus rating_mismatch, true when the words clearly contradict the star rating (a glowing text under 1 star, a furious one under 5), null when the review has no rating. issue: the main problem the review reports (label: product_defect, sizing_or_fit, shipping_or_delivery, customer_service, price_or_value, missing_feature, other, or none, with confidence; label is null when unsure). reports (needs reports=): does this review say that the thing you describe happened (p, 0 to 1). incentivized: does the reviewer say they got the product free, discounted or rewarded for the review (p), and did the text carry a disclosure such as 'in exchange for my honest review', Vine or 체험단 (disclosed). injection flags text that addresses an AI system and tries to direct it (flagged, p); it never drops or rewrites a row. These are signals to read, never a verdict on a review or a reviewer. A review that could not be judged carries labels: null. Reviews already labelled for anyone are free, and so is a cached page. data.labels reports what was judged and billed. - reports (optional, string): Required by label=reports, ignored otherwise. What to look for, in plain words, up to 300 characters, for example reports=the battery drains quickly or reports=배송이 늦음. Every review gains labels.reports.p, the probability that it says so, in any wording or language. Without it label=reports is skipped with the warning label_reports_needs_reports and is not billed. - judgments (optional, enum: on | off): Optional, on (the default) or off. By default every row gains free SocialCrawl judgments (computed.labels, and computed.relevance on search endpoints), reported in data.labels (mode default) and data.relevance (origin default), each with a status (complete, partial or skipped) and pending: the rows still being judged when the page was sent, which carry null now and are filled on your next call or cached read. Default judgments never add credits, never change an existing field, and never drop or reorder a row. off returns the page exactly as before, with none of those keys. label=none does the same. - dry_run (optional, enum: 1): Optional. When 1, return a cost preview for this labelled or relevance-filtered request without fetching the page or judging any row. data.estimate reports rows_expected, rows_cached, label_credits_min, label_credits_max and base_credits. 0 credits charged. - label_evidence (optional, enum: 1): Optional, only with label=. When 1, every labelled row also carries computed.labels_evidence. = { quote, sentence_index }: the sentence in the row that most clearly shows the label, copied verbatim. Absent or null when no single sentence shows it. - fit (optional, enum: goal): Optional. When goal, keep the rows and fields needed for the goal you pass in goal= (plus any that are uncertain, and the first and last), and replace the rest with a stub. data.held_back lists the held ids and a recall id that re-reads the full page from cache at no extra charge. Without this param the page is unchanged. - goal (optional, string): Required by fit=goal. What you are trying to do, in your own words, up to 300 characters. - fit_tokens (optional, integer): Optional, only with fit=goal. Soft cap on how much of the page to keep, in tokens. Uncertain blocks and the first and last block are kept even if they exceed it. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. ``` curl -G "https://www.socialcrawl.dev/v1/home_depot/reviews" \ --data-urlencode "item_id=326680222" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /home_depot/search Search Home Depot products by keyword Credit cost: 5 (advanced) Parameters: - query (required): Free-text product search, for example drill or cordless impact driver. - page (optional, integer): Page number, 1 to 30. Prefer the universal cursor parameter. - store_id (optional, string): Home Depot store number used to localise stock and pickup availability, for example 0723. Prices are Home Depot's online price and do not change with the store. Pass the id exactly as GET /v1/home_depot/stores returns it; rows echo it without the leading zero on search (723). - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/home_depot/search" \ --data-urlencode "query=drill" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /home_depot/stores Find Home Depot stores near a ZIP code Credit cost: 1 (standard) Parameters: - query (required): Five-digit US ZIP code to search near, for example 95032. The nearest stores are returned in distance order. ``` curl -G "https://www.socialcrawl.dev/v1/home_depot/stores" \ --data-urlencode "query=95032" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## Instagram ### GET /instagram/profile Get Instagram user profile Credit cost: 1 (standard) Parameters: - handle (required): Instagram username without the @ symbol - trim (optional, boolean): Set to true to skip the recent-posts read. The profile comes back faster, at the same price, with `author.last_post_at` and `computed.engagement_rate` null. - contact_email (optional, enum: 1): Optional. When 1, also returns author.ext.contact_email: when the bio lists two or more email addresses, the one the creator gives for contacting them personally, copied verbatim from the bio. Null when there is one address or none, when every address belongs to a manager, agency or brand, or when the choice was not confident. author.ext.public_email is unchanged. No extra credits. ``` curl -G "https://www.socialcrawl.dev/v1/instagram/profile" \ --data-urlencode "handle=instagram" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /instagram/profile/about Get Instagram account transparency details Credit cost: 1 credit per call, with or without `include=verified_at`. A call that fails, a handle that does not exist and a refused parameter cost nothing. Parameters: - handle (required): Instagram username without the @ symbol - include (optional, string): Send `verified_at` to add `author.ext.verified_at`, the month Instagram verified the account ("April 2024"). Same price. Any other value is a free 400. ``` curl -G "https://www.socialcrawl.dev/v1/instagram/profile/about" \ --data-urlencode "handle=instagram" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /instagram/profile/country Get the country an Instagram account declares, by user id (oneOf: user_id) Credit cost: 1 credit per call. A refused username or a missing `user_id` costs nothing. Parameters: - user_id (optional, string): The numeric Instagram account id, for example `post.ext.author_id` from a search row or `author.id` from /v1/instagram/profile/about. A username is refused at no charge. - Constraint: one of user_id (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/instagram/profile/country" \ --data-urlencode "user_id=25025320" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /instagram/profile/posts List Instagram user posts Credit cost: 1-29 (metered): 1 credit per page. include=audio holds 1 more per row, up to 12, and keeps it only for each video whose sound came back, refunding the rest, so a page costs at most 13 credits and a page with no videos costs 1. include=tags holds 1 more per row, up to 12, and keeps it only for each post whose tagged accounts came back, refunding the rest; with both tokens a page costs at most 25 credits. exclude_pinned and recent_days do not change the price of a page. The default labels (sponsored, intent, niche) are free. label=mention, quality, ai_style, ai_written, injection, or label=intent with offer=, holds 4 extra credits and refunds down to 1 credit per started 25 posts that were newly judged: posts already labelled are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - handle (required): Instagram username without the @ symbol - since (optional, string): Only posts published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Older posts are left off the page, and the page that reaches one ends the walk: `next_cursor` is not returned and `pagination.stopped_at` is `since`. Pinned posts sit out of date order and never end the walk. The page still costs what a page costs, so a daily poll pays for the pages it walks and no more. - stop_at_id (optional, string): The id (`post.id`) or URL (`post.url`) of the newest post you already hold. The page stops just before it: that post and everything after it are left off, `next_cursor` is not returned, and `pagination.stopped_at` is `known_id`. A pinned post never counts as the stop point. If it is not on this page the page is returned in full with its cursor, so keep walking. `pagination.stopped_at` is `end` when the list ran out first and `null` while there is more to walk. A post counts as pinned when `post.flags.pinned` is true or when it is older than a post below it on the page, so a pinned post that also sits in date order is never the stop point on a page that marks pins, and can be on a page whose `post.flags.pinned` is null. - next_max_id (optional, string): Cursor to get next page of results. - trim (optional, boolean): Set to true to request the lighter upstream record. That record is the narrow width described above, so the page is normally served from the second source with every field. A trimmed page served as it is omits `post.ext.coauthors`, and its `post.flags.pinned` is null, as it is on any page called without `exclude_pinned`. The price is the same either way. - exclude_pinned (optional, boolean): Set to true to leave out the posts the owner pinned to the top of the profile. The pins are read from a second source alongside the page, at the same price, and the rows kept carry `post.flags.pinned: false`. A row whose pin state is unknown (`post.flags.pinned: null`), such as a row that pin read did not reach, a trimmed row, or a page from a source that cannot mark pins, is kept, and `data._warnings` says so. Without this parameter `post.flags.pinned` is not read and is null on rows from the first source, except on a page reached with a cursor, whose rows are `false` either way because a pinned post is served on the first page only. - recent_days (optional, integer): Keep only posts published in the last N days (1 to 3650). Older posts are removed from the page, pinned ones included, and once a page reaches posts older than the window `next_cursor` is not returned, so a walk back to a date window stops on its own. The page still costs what a page costs. - include (optional, string): `audio` adds `post.ext.music_id` and `post.ext.music` to video rows from a second lookup: 1 extra credit per video filled, refunded when the sound could not be read. Photos and carousels are never looked up. `tags` adds `post.ext.usertags`, the usernames tagged in the post, to every row from a second lookup: 1 extra credit per post that names someone, refunded for a post that tags nobody or whose lookup failed. Both tokens can be combined (`include=audio,tags`). Adds a few seconds; read `data.hydration` for the credits held and kept and the time. - label (optional, string): Optional CSV of SocialCrawl labels to add to every post. Without this param every page already carries sponsored, intent and niche, free; label= adds the labels you name to them (the defaults keep running). sponsored, intent and niche are free when asked for too; mention, quality, ai_style, intent with offer=, and injection add 1 credit per started 25 newly judged posts. judgments=off (or label=none) turns the default labels off. sponsored: is the post a paid or gifted promotion (p, 0 to 1), did it carry a disclosure marker such as #ad or 광고 (disclosed), is it likely paid with no marker (undisclosed), and which of the accounts it mentions does it promote (brand, or null). These are signals to review, never a finding. mention (needs brand=): is the post about that brand rather than something that shares its name (about_brand, 0 to 1), how it feels about the brand on five levels (sentiment_level 0 to 4 and sentiment_score 0 to 1, null when the post is not about the brand), is it sarcastic, which aspect it talks about (taste_or_quality, price_or_value, availability_or_delivery, health_or_safety, advertising_or_campaign, customer_service, none), and did the author buy or use it (first_hand). intent: what the author is mainly doing (label: asking_for_recommendation, comparing_options, switching_away, complaining, promoting, news_or_discussion, other, or null when unsure, with confidence), whether they read as a potential buyer rather than a seller (buyer, seller), how pressing the need is (urgency, 0 to 3), and, when you pass offer=, whether your offer would plausibly help them (fits_offer). niche: which of the 33 niches of the published taxonomy sc-niche-v1 the caption belongs to, or personal_no_niche, or other (label, confidence, taxonomy), with label null when the caption is too thin to tell or the pick is unsure. It reads the caption only, not the video. quality: how much checkable detail the caption carries (fact_density 0 to 3), whether it mainly asks for likes, replies, shares, follows or tags (engagement_bait, 0 to 1), whether it is written to provoke anger as a way to get engagement (rage_bait, 0 to 1; about the writing, never the side taken), whether it only repeats someone else's news or view (secondhand, 0 to 1), and what the post is mainly doing (post_aim: inform, opinion, sell, entertain, provoke, other). exclude=engagement_bait drops posts whose engagement_bait is 0.8 or higher. ai_style: writing patterns common in AI-generated text, judged on the text alone. It does not say who wrote the post, and a person who writes in this style is flagged too. Six patterns are judged, each a probability (0 to 1): binary_contrast ("This is not X. It's Y."), faux_insight ("Here's what nobody tells you" and throat-clearing openers), colon_reveal ("The best part: it learns."), profound_kicker (ends on an aphorism or mic-drop line), puffery ("marks a pivotal moment", "highlighting the importance of") and rhetorical_setup ("What if I told you", a question the post answers itself, "Not a X. Not a Y. A Z."). Four are counted in the text: slop_words (words and phrases such as delve, leverage, robust, game changer, at the end of the day), em_dashes, emoji_bullets (lines that start with an emoji or a symbol bullet) and one_line_paragraphs (paragraphs that are one short line). pattern_count is how many patterns are present (a judged pattern at 0.8 or higher, slop_words 1 or more, em_dashes 2 or more, emoji_bullets 2 or more, one_line_paragraphs 5 or more) and markers lists their names, comma-separated. A post with less than about eight words of text is only counted, with every judged pattern 0. The judged patterns read the first 400 and the last 150 characters of a long post, and the counts read the whole post. exclude=ai_style drops posts with 2 or more patterns. ai_written: how likely the post was written by an AI model, from SocialCrawl's own text classifier, reading the opening of the post (about 1,000 characters): p (0 to 1, calibrated), level (likely (p 0.95 or higher), possible (p 0.71 or higher) or unlikely) and model (the model name; a new name means a new scale). A post under 8 words is too short to read: p null, level too_short. It is a probability, not proof: a person can be flagged and an AI-written post can pass, and the shorter the post the less certain it is. exclude=ai_written drops likely posts. injection flags text that addresses an AI system and tries to direct it (flagged, p); it never drops or rewrites a row. A post that could not be judged carries labels: null. Posts already labelled for anyone are free, and so is a cached page. data.labels reports what was judged and billed. - exclude (optional, string): Optional CSV (engagement_bait, ai_style, ai_written). engagement_bait, only with label=quality, drops posts whose engagement_bait probability is 0.8 or higher. ai_style, only with label=ai_style, drops posts that show 2 or more of its writing patterns (pattern_count 2 or higher). ai_written, only with label=ai_written, drops posts whose level is likely (p 0.95 or higher), or whose p reaches your ai_written_threshold; a post too short to read is never dropped. Dropped posts are listed in data.labels.dropped_ids. A post that could not be judged is never dropped. - ai_written_threshold (optional, string): Optional, only with label=ai_written and exclude=ai_written. The p (0 to 1) at which exclude=ai_written drops a post. Default 0.95 (level likely). 0.71 also drops possible posts: more AI posts go, and more posts by people with them. - brand (optional, string): Required by label=mention, ignored otherwise. The brand or product the posts are judged against, up to 300 characters, for example brand=Buldak. Without it label=mention is skipped with the warning label_mention_needs_brand and is not billed. - brand_description (optional, string): Optional, with label=mention. One plain sentence saying what the brand is, for example brand_description=Samyang's spicy instant noodle brand. Use it when the name is also an ordinary word or another company's name. - offer (optional, string): Optional, with label=intent. One or two plain sentences saying what you sell, up to 300 characters, for example offer=A web design agency that builds websites for small businesses. Adds fits_offer to every post; without it fits_offer is null. - judgments (optional, enum: on | off): Optional, on (the default) or off. By default every row gains free SocialCrawl judgments (computed.labels, and computed.relevance on search endpoints), reported in data.labels (mode default) and data.relevance (origin default), each with a status (complete, partial or skipped) and pending: the rows still being judged when the page was sent, which carry null now and are filled on your next call or cached read. Default judgments never add credits, never change an existing field, and never drop or reorder a row. off returns the page exactly as before, with none of those keys. label=none does the same. - dry_run (optional, enum: 1): Optional. When 1, return a cost preview for this labelled or relevance-filtered request without fetching the page or judging any row. data.estimate reports rows_expected, rows_cached, label_credits_min, label_credits_max and base_credits. 0 credits charged. - label_evidence (optional, enum: 1): Optional, only with label=. When 1, every labelled row also carries computed.labels_evidence. = { quote, sentence_index }: the sentence in the row that most clearly shows the label, copied verbatim. Absent or null when no single sentence shows it. - fit (optional, enum: goal): Optional. When goal, keep the rows and fields needed for the goal you pass in goal= (plus any that are uncertain, and the first and last), and replace the rest with a stub. data.held_back lists the held ids and a recall id that re-reads the full page from cache at no extra charge. Without this param the page is unchanged. - goal (optional, string): Required by fit=goal. What you are trying to do, in your own words, up to 300 characters. - fit_tokens (optional, integer): Optional, only with fit=goal. Soft cap on how much of the page to keep, in tokens. Uncertain blocks and the first and last block are kept even if they exceed it. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/instagram/profile/posts" \ --data-urlencode "handle=instagram" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /instagram/post Get Instagram post details Credit cost: 1 (standard) Parameters: - url (required): Full URL of the Instagram post. A share link copied from the app (`instagram.com/share/...`) works too; a share link to a profile is refused at no charge with the handle to send, and a dead one is a free 404. - region (optional, string): 2 letter country code to set the proxy in - trim (optional, boolean): Set to true to get a trimmed response - download_media (optional, boolean): Set to true to also download the video/images and get back durable hosted media URLs under `data.post.ext.download_media_urls` (`[{ post_id, cdn_url, type, cached }]`). Use these for archiving; the raw `media_urls` are short-lived signed CDN links that expire. Adds a few seconds of latency while the media is fetched. Not available on login-gated posts (age-restricted 18+ and similar), which are served by a source that cannot host durable copies: the post and its media still come back in full, and the response carries a `data._warnings` note telling you to read `data.post.content.media_urls` directly. ``` curl -G "https://www.socialcrawl.dev/v1/instagram/post" \ --data-urlencode "url=https://www.instagram.com/p/DcCH2ZygIiP/" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /instagram/post/comments List Instagram post comments Credit cost: 5-19 (metered): 5 credits per page, with the default labels (sentiment, question, purchase_intent, complaint) included free. label=spam, toxic or low_quality holds 4 extra credits and refunds down to 1 credit per started 25 comments that were newly judged: comments already labelled are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - url (required): URL of the Instagram post (a /p/, /reel/, /reels/, or /tv/ link). A share link copied from the app (`instagram.com/share/...`) works too; a share link to a profile is refused at no charge with the handle to send, and a dead one is a free 404. - sort (optional, enum: top | recent): Ordering: `top` (default) ranks by Instagram's popularity order (most-liked first); `recent` returns newest-first. Use `recent` when you are paging through a whole thread: Instagram ranks only the most-liked head, so a `top` walk starts repeating comments once you page past it. - cursor (optional, string): Pagination cursor. Use the `next_cursor` from the previous response to fetch the next page. - safe_url (optional, boolean): When true, returns URL-safe profile picture links suitable for embedding. - scan_pages (optional, integer): Optional, 1 to 3, default 1. With 2 or 3, the call reads that many pages, drops comments repeated across pages, and returns them all sorted by likes (sort=top) or newest first (sort=recent). Use it when the first page of a top-sorted thread is low-like comments. Costs 5 credits for each page that added comments; a page of repeats is not billed. data.scan reports the pages read and billed and whether the thread ended. - label (optional, string): Optional CSV of SocialCrawl labels to add to every comment: sentiment, spam, question, purchase_intent, complaint, toxic, low_quality, injection. Without this param every page already carries sentiment, question, purchase_intent and complaint, free; label= adds the labels you name to them (the defaults keep running). Each comment gains computed.labels with probabilities (0 to 1), so you choose the cut-off; a comment that could not be judged carries labels: null. sentiment, question, purchase_intent and complaint are free when asked for too; spam, toxic, low_quality and injection add 1 credit per started 25 newly judged comments (comments already labelled for anyone are free, and so is a cached page). injection flags text that addresses an AI system and tries to direct it (flagged, p); it never drops or rewrites a row. judgments=off (or label=none) turns the default labels off. data.labels reports what was judged and billed. - exclude (optional, string): Optional CSV (spam, low_quality), only with the same preset in label. Drops comments whose probability is 0.8 or higher and lists their ids in data.labels.dropped_ids. A comment that could not be judged is never dropped. - judgments (optional, enum: on | off): Optional, on (the default) or off. By default every row gains free SocialCrawl judgments (computed.labels, and computed.relevance on search endpoints), reported in data.labels (mode default) and data.relevance (origin default), each with a status (complete, partial or skipped) and pending: the rows still being judged when the page was sent, which carry null now and are filled on your next call or cached read. Default judgments never add credits, never change an existing field, and never drop or reorder a row. off returns the page exactly as before, with none of those keys. label=none does the same. - dry_run (optional, enum: 1): Optional. When 1, return a cost preview for this labelled or relevance-filtered request without fetching the page or judging any row. data.estimate reports rows_expected, rows_cached, label_credits_min, label_credits_max and base_credits. 0 credits charged. - label_evidence (optional, enum: 1): Optional, only with label=. When 1, every labelled row also carries computed.labels_evidence. = { quote, sentence_index }: the sentence in the row that most clearly shows the label, copied verbatim. Absent or null when no single sentence shows it. - fit (optional, enum: goal): Optional. When goal, keep the rows and fields needed for the goal you pass in goal= (plus any that are uncertain, and the first and last), and replace the rest with a stub. data.held_back lists the held ids and a recall id that re-reads the full page from cache at no extra charge. Without this param the page is unchanged. - goal (optional, string): Required by fit=goal. What you are trying to do, in your own words, up to 300 characters. - fit_tokens (optional, integer): Optional, only with fit=goal. Soft cap on how much of the page to keep, in tokens. Uncertain blocks and the first and last block are kept even if they exceed it. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off the page, and the page that reaches one ends the walk: `next_cursor` is not returned and `pagination.stopped_at` is `since`. Sending it sets the newest-first sort shown in this endpoint's sort parameter (any other sort value is a free 400). The page still costs what a page costs, so you pay for the pages you walk and no more. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/instagram/post/comments" \ --data-urlencode "url=https://www.instagram.com/p/DXidPIVDU6M/" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /instagram/comment Look up one Instagram comment by URL or id Credit cost: 5-15 (metered): 5 credits for a standard lookup, 15 with `deep_scan=true`, which keeps paging the thread when the comment is not in the first pages. Reach for deep_scan only after a standard lookup comes back not-found Parameters: - comment_url (optional, string): An Instagram comment permalink: `https://www.instagram.com/p/{shortcode}/c/{commentId}/` (or a reply permalink `.../c/{parent}/r/{reply}/`, best-effort). Mutually exclusive with `post_url`+`comment_id`. - post_url (optional, string): The post URL (a `/p/`, `/reel/`, `/reels/`, or `/tv/` link; an app share link `instagram.com/share/...` works too). Combine with `comment_id`, or with `author_username`/`text_contains` for a search. - comment_id (optional, string): The target comment's numeric id (`pk`). Requires `post_url`. - author_username (optional, string): Return up to `max` comments authored by this username (no comment id needed). Mutually exclusive with `text_contains` and any comment id. - text_contains (optional, string): Return up to `max` comments whose text contains this snippet (case-insensitive). Mutually exclusive with `author_username` and any comment id. - deep_scan (optional, boolean): Widen the scan budget for deeply-buried comments (raises the per-chain page ceiling and deadline). Bills 15 credits instead of 5. - position_hint (optional, string): Opaque token from a prior lookup's `lookup.position_hint`. Passing it back replays just the last-known sort chain first, making a re-check of an already-found comment cheap. - max (optional, integer): For `author_username`/`text_contains` search: max matches to return (1-20, default 5). ``` curl "https://www.socialcrawl.dev/v1/instagram/comment" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /instagram/basic-profile Get Instagram basic profile Credit cost: 1 (standard) Parameters: - userId (optional, string): Instagram numeric user ID ``` curl "https://www.socialcrawl.dev/v1/instagram/basic-profile" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /instagram/profile/reels List Instagram user reels (oneOf: user_id|handle) Credit cost: 1-18 (metered): 1 credit per page. include=stats adds 1 credit a page. include=audio holds 1 more per row, up to 12, and keeps it only for each video whose sound came back, refunding the rest; with include=stats every reel already carries its sound, so the audio join costs nothing more. A page costs at most 14 credits. exclude_pinned and recent_days do not change the price of a page. sort=most_viewed costs the same 1 credit a page. The default labels (sponsored, intent, niche) are free. label=mention, quality, ai_style, ai_written, injection, or label=intent with offer=, holds 4 extra credits and refunds down to 1 credit per started 25 posts that were newly judged: posts already labelled are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - since (optional, string): Only reels published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Older reels are left off the page, and the page that reaches one ends the walk: `next_cursor` is not returned and `pagination.stopped_at` is `since`. Pinned reels sit out of date order and never end the walk. The page still costs what a page costs, so a daily poll pays for the pages it walks and no more. - stop_at_id (optional, string): The id (`post.id`) or URL (`post.url`) of the newest reel you already hold. The page stops just before it: that reel and everything after it are left off, `next_cursor` is not returned, and `pagination.stopped_at` is `known_id`. A pinned reel never counts as the stop point. If it is not on this page the page is returned in full with its cursor, so keep walking. `pagination.stopped_at` is `end` when the list ran out first and `null` while there is more to walk. - user_id (optional, string): Instagram user id. Use this for faster response times. - handle (optional, string): Instagram username without the @ symbol - max_id (optional, string): Max id to get more reels. Get 'max_id' from previous response. - trim (optional, boolean): Set to true to request the lighter upstream record: a smaller, faster page that keeps the engagement, the author, the caption, the timestamp and the reel URL. What it drops is `post.content.media_urls` (the video file), `post.content.duration_seconds`, `post.ext.coauthors` and `post.flags.pinned`. The cover image is returned as `post.content.thumbnail_url` either way. Leave it unset when you need the video URL or the duration. - exclude_pinned (optional, boolean): Set to true to leave out the posts the owner pinned to the top of the profile. A row whose pin state is unknown, which is every row with trim=true, is kept. - recent_days (optional, integer): Keep only posts published in the last N days (1 to 3650). Older posts are removed from the page, pinned ones included, and once a page reaches posts older than the window `next_cursor` is not returned, so a walk back to a date window stops on its own. The page still costs what a page costs. - include (optional, string): `stats` returns the full stat line on every reel for 1 extra credit a page: `engagement.shares`, `engagement.saves` and `post.ext.repost_count` beside views, likes, comments, the remix counter, `post.ext.music_id` for original sounds and licensed music alike, and `post.ext.audio_cluster_id`, Instagram's audio cluster for the reel. It is served by a second source, so a cursor from a request without it cannot continue a walk with it; start from page 1. `audio` adds `post.ext.music_id` and `post.ext.music` to reels still missing them from a second lookup: 1 extra credit per reel filled, refunded when the sound could not be read. Both tokens can be combined (`include=audio,stats`). - sort (optional, enum: most_viewed): Send `most_viewed` for the creator's reels in Instagram's most viewed order (highest plays first) instead of newest first. Same price. Its cursors only continue a `sort=most_viewed` walk. Cannot be combined with `include`, `trim`, `recent_days`, `since` or `stop_at_id`. - label (optional, string): Optional CSV of SocialCrawl labels to add to every post. Without this param every page already carries sponsored, intent and niche, free; label= adds the labels you name to them (the defaults keep running). sponsored, intent and niche are free when asked for too; mention, quality, ai_style, intent with offer=, and injection add 1 credit per started 25 newly judged posts. judgments=off (or label=none) turns the default labels off. sponsored: is the post a paid or gifted promotion (p, 0 to 1), did it carry a disclosure marker such as #ad or 광고 (disclosed), is it likely paid with no marker (undisclosed), and which of the accounts it mentions does it promote (brand, or null). These are signals to review, never a finding. mention (needs brand=): is the post about that brand rather than something that shares its name (about_brand, 0 to 1), how it feels about the brand on five levels (sentiment_level 0 to 4 and sentiment_score 0 to 1, null when the post is not about the brand), is it sarcastic, which aspect it talks about (taste_or_quality, price_or_value, availability_or_delivery, health_or_safety, advertising_or_campaign, customer_service, none), and did the author buy or use it (first_hand). intent: what the author is mainly doing (label: asking_for_recommendation, comparing_options, switching_away, complaining, promoting, news_or_discussion, other, or null when unsure, with confidence), whether they read as a potential buyer rather than a seller (buyer, seller), how pressing the need is (urgency, 0 to 3), and, when you pass offer=, whether your offer would plausibly help them (fits_offer). niche: which of the 33 niches of the published taxonomy sc-niche-v1 the caption belongs to, or personal_no_niche, or other (label, confidence, taxonomy), with label null when the caption is too thin to tell or the pick is unsure. It reads the caption only, not the video. quality: how much checkable detail the caption carries (fact_density 0 to 3), whether it mainly asks for likes, replies, shares, follows or tags (engagement_bait, 0 to 1), whether it is written to provoke anger as a way to get engagement (rage_bait, 0 to 1; about the writing, never the side taken), whether it only repeats someone else's news or view (secondhand, 0 to 1), and what the post is mainly doing (post_aim: inform, opinion, sell, entertain, provoke, other). exclude=engagement_bait drops posts whose engagement_bait is 0.8 or higher. ai_style: writing patterns common in AI-generated text, judged on the text alone. It does not say who wrote the post, and a person who writes in this style is flagged too. Six patterns are judged, each a probability (0 to 1): binary_contrast ("This is not X. It's Y."), faux_insight ("Here's what nobody tells you" and throat-clearing openers), colon_reveal ("The best part: it learns."), profound_kicker (ends on an aphorism or mic-drop line), puffery ("marks a pivotal moment", "highlighting the importance of") and rhetorical_setup ("What if I told you", a question the post answers itself, "Not a X. Not a Y. A Z."). Four are counted in the text: slop_words (words and phrases such as delve, leverage, robust, game changer, at the end of the day), em_dashes, emoji_bullets (lines that start with an emoji or a symbol bullet) and one_line_paragraphs (paragraphs that are one short line). pattern_count is how many patterns are present (a judged pattern at 0.8 or higher, slop_words 1 or more, em_dashes 2 or more, emoji_bullets 2 or more, one_line_paragraphs 5 or more) and markers lists their names, comma-separated. A post with less than about eight words of text is only counted, with every judged pattern 0. The judged patterns read the first 400 and the last 150 characters of a long post, and the counts read the whole post. exclude=ai_style drops posts with 2 or more patterns. ai_written: how likely the post was written by an AI model, from SocialCrawl's own text classifier, reading the opening of the post (about 1,000 characters): p (0 to 1, calibrated), level (likely (p 0.95 or higher), possible (p 0.71 or higher) or unlikely) and model (the model name; a new name means a new scale). A post under 8 words is too short to read: p null, level too_short. It is a probability, not proof: a person can be flagged and an AI-written post can pass, and the shorter the post the less certain it is. exclude=ai_written drops likely posts. injection flags text that addresses an AI system and tries to direct it (flagged, p); it never drops or rewrites a row. A post that could not be judged carries labels: null. Posts already labelled for anyone are free, and so is a cached page. data.labels reports what was judged and billed. - exclude (optional, string): Optional CSV (engagement_bait, ai_style, ai_written). engagement_bait, only with label=quality, drops posts whose engagement_bait probability is 0.8 or higher. ai_style, only with label=ai_style, drops posts that show 2 or more of its writing patterns (pattern_count 2 or higher). ai_written, only with label=ai_written, drops posts whose level is likely (p 0.95 or higher), or whose p reaches your ai_written_threshold; a post too short to read is never dropped. Dropped posts are listed in data.labels.dropped_ids. A post that could not be judged is never dropped. - ai_written_threshold (optional, string): Optional, only with label=ai_written and exclude=ai_written. The p (0 to 1) at which exclude=ai_written drops a post. Default 0.95 (level likely). 0.71 also drops possible posts: more AI posts go, and more posts by people with them. - brand (optional, string): Required by label=mention, ignored otherwise. The brand or product the posts are judged against, up to 300 characters, for example brand=Buldak. Without it label=mention is skipped with the warning label_mention_needs_brand and is not billed. - brand_description (optional, string): Optional, with label=mention. One plain sentence saying what the brand is, for example brand_description=Samyang's spicy instant noodle brand. Use it when the name is also an ordinary word or another company's name. - offer (optional, string): Optional, with label=intent. One or two plain sentences saying what you sell, up to 300 characters, for example offer=A web design agency that builds websites for small businesses. Adds fits_offer to every post; without it fits_offer is null. - judgments (optional, enum: on | off): Optional, on (the default) or off. By default every row gains free SocialCrawl judgments (computed.labels, and computed.relevance on search endpoints), reported in data.labels (mode default) and data.relevance (origin default), each with a status (complete, partial or skipped) and pending: the rows still being judged when the page was sent, which carry null now and are filled on your next call or cached read. Default judgments never add credits, never change an existing field, and never drop or reorder a row. off returns the page exactly as before, with none of those keys. label=none does the same. - dry_run (optional, enum: 1): Optional. When 1, return a cost preview for this labelled or relevance-filtered request without fetching the page or judging any row. data.estimate reports rows_expected, rows_cached, label_credits_min, label_credits_max and base_credits. 0 credits charged. - label_evidence (optional, enum: 1): Optional, only with label=. When 1, every labelled row also carries computed.labels_evidence. = { quote, sentence_index }: the sentence in the row that most clearly shows the label, copied verbatim. Absent or null when no single sentence shows it. - fit (optional, enum: goal): Optional. When goal, keep the rows and fields needed for the goal you pass in goal= (plus any that are uncertain, and the first and last), and replace the rest with a stub. data.held_back lists the held ids and a recall id that re-reads the full page from cache at no extra charge. Without this param the page is unchanged. - goal (optional, string): Required by fit=goal. What you are trying to do, in your own words, up to 300 characters. - fit_tokens (optional, integer): Optional, only with fit=goal. Soft cap on how much of the page to keep, in tokens. Uncertain blocks and the first and last block are kept even if they exceed it. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. - Constraint: one of user_id, handle (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/instagram/profile/reels" \ --data-urlencode "handle=mrbeast" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /instagram/highlights List Instagram story highlights (oneOf: user_id|handle) Credit cost: 1 (standard) Parameters: - user_id (optional, string): Instagram user id. Use for faster response times. - handle (optional, string): Instagram username without the @ symbol - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - Constraint: one of user_id, handle (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/instagram/highlights" \ --data-urlencode "handle=instagram" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /instagram/highlight/detail Get Instagram highlight detail Credit cost: 1 (standard) Parameters: - id (optional, string): Instagram highlight ID: the numeric id, with or without the `highlight:` prefix. Get it from `/v1/instagram/highlights`. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. ``` curl "https://www.socialcrawl.dev/v1/instagram/highlight/detail" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /instagram/search/reels Search Instagram posts by keyword or topic: reels about a brand, phrase or subject, with view counts (1 credit a page) Credit cost: 1-69 (metered): 1 credit per page on a plain call or with `region`; `include=creator` or `country` makes it up to 61 (1 plus 2 per creator looked up). `region` costs nothing extra. `include=country` makes it up to 31: 1 extra credit per creator whose declared country came back, refunded for a creator who publishes none or cannot be found. `include=creator` adds 2 credits per creator looked up: it holds 60 extra credits for a full page of 30 and refunds every creator it did not look up or could not find. Two reels by one creator are one lookup, a creator looked up in the last 15 minutes is free, and on a `max_pages` walk each creator is looked up and billed once. `country` runs the same creator lookup, because it needs each creator's declared country, at the same price, charged for every creator looked up, kept or not; `data.hydration.implied_by` says so on the response. `include=creator,country` is priced as `include=creator` alone, because the creator card already carries the country. Relevance against your query (by default, or with relevance=) is free. Adding relevant_to= holds 4 extra credits and refunds down to 1 credit per started 25 rows that were newly judged: rows already judged for the same topic are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. The default labels (sponsored, intent, niche) are free. label=mention, quality, ai_style, ai_written, injection, or label=intent with offer=, holds 4 extra credits and refunds down to 1 credit per started 25 posts that were newly judged: posts already labelled are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. With `seen`, a page's price falls with its share of rows you already received under that id; credits for joined data (`include=`) are never discounted. Parameters: - query (required): Search keyword or phrase to find Instagram reels - date_posted (optional, enum: last-week | last-month | last-year): Date posted - page (optional, integer): The page number to return. - region (optional, enum: ES | MX | DE | BR | KR | FR | ZA | NG | KE | AE | SA): Two-letter market code (ES, MX, DE, BR, KR, FR, ZA, NG, KE, AE, SA) to localise the results. Instagram does not rank search by country, so the market's own name is appended to your query (`fitness` with `ES` searches `fitness españa`), unless the query already names the market or is already written in the market's own script (Hangul for KR), which Instagram localises by itself. The query actually searched is returned in `data.region.query_sent`. It favours creators from the market but does not promise that every creator is from it; send `country` for that. For a topic about a place, such as eSIMs or travel, a phrasing locals use works better (`esim móvil` rather than `esim`). Same price as a plain call. Only these markets are supported; any other code is refused with the list, at no charge. A typo is a free 400. - include (optional, string): Send `creator` to add each reel's creator card, read from the creator's About this account panel: `post.ext.author_country`, `author_followers`, `author_following`, `author_posts_count`, `author_public_email` and `author_public_phone`, plus the creator's name, avatar and verified flag where the row lacks them. `author_country` is the country the creator declares there, not where the reel was filmed; it is null when Instagram does not publish one, and nothing is guessed. 2 extra credits per creator looked up; two reels by one creator cost one lookup, a creator that cannot be found is refunded, and a creator looked up in the last 15 minutes is free. Send `country` for the declared country alone (`post.ext.author_country`, the same value): 1 extra credit per creator whose country came back, refunded for a creator who publishes none or cannot be found, and free for a creator looked up in the last 30 days, with no counts or contact. `creator,country` is the same call as `creator`. A typo is a free 400. - country (optional, enum: ES | MX | DE | BR | KR | FR | ZA | NG | KE | AE | SA): Two-letter market code (ES, MX, DE, BR, KR, FR, ZA, NG, KE, AE, SA) to keep only reels whose creator is based in that market. Costs up to 61 credits a page, not 1: 1 credit plus 2 per creator looked up, kept or not, because the country is read from each creator's own profile (`post.ext.author_country`). It therefore adds the creator card (`include=creator`, reported as `data.hydration.implied_by: "country"`) and, when `region` is not sent, localises the query for the same market. To localise without the lookups, send `region` alone (1 credit). Rows from another country, or whose creator publishes none, are dropped and counted in `data.country` (`kept`, `dropped_other`, `dropped_unknown`); kept rows keep their order. When no row matches, the page is `items: []` with the warning `country_no_match`, and the lookups are still billed. A page left empty only because the creator lookups failed costs nothing. Works with `max_pages`. A typo is a free 400. - relevance (optional, enum: score | filter): Optional. Without this param every row already carries computed.relevance against your query, free ({ p, sense, depth, spam }: is this row about what your query means, or a different thing that shares its words?), and nothing is dropped or reordered. score asks for it explicitly and waits for every row; filter also drops the rows that are not about your query, and lists their ids in data.relevance.dropped_ids. Your query is the topic; nothing to configure. A row that could not be judged is never dropped and carries relevance: null. Pagination is unchanged, so a filtered page can hold fewer rows. Free with your query as the topic; with relevant_to it adds 1 credit per started 25 newly judged rows (rows already judged for the same topic are free, and so is a cached page). - relevance_threshold (optional, string): Optional, only with relevance. The probability (0 to 1) a row must reach to be kept by relevance=filter. Default 0.5. Lower keeps more rows, higher keeps fewer. - relevant_to (optional, string): Optional, only with relevance. Up to 200 characters describing what you mean, used as the topic instead of the query. Use it when the query is ambiguous, for example query=cleopatra with relevant_to=Cleopatra, the IGT slot game. - judgments (optional, enum: on | off): Optional, on (the default) or off. By default every row gains free SocialCrawl judgments (computed.labels, and computed.relevance on search endpoints), reported in data.labels (mode default) and data.relevance (origin default), each with a status (complete, partial or skipped) and pending: the rows still being judged when the page was sent, which carry null now and are filled on your next call or cached read. Default judgments never add credits, never change an existing field, and never drop or reorder a row. off returns the page exactly as before, with none of those keys. label=none does the same. - dry_run (optional, enum: 1): Optional. When 1, return a cost preview for this labelled or relevance-filtered request without fetching the page or judging any row. data.estimate reports rows_expected, rows_cached, label_credits_min, label_credits_max and base_credits. 0 credits charged. - fit (optional, enum: goal): Optional. When goal, keep the rows and fields needed for the goal you pass in goal= (plus any that are uncertain, and the first and last), and replace the rest with a stub. data.held_back lists the held ids and a recall id that re-reads the full page from cache at no extra charge. Without this param the page is unchanged. - goal (optional, string): Required by fit=goal. What you are trying to do, in your own words, up to 300 characters. - fit_tokens (optional, integer): Optional, only with fit=goal. Soft cap on how much of the page to keep, in tokens. Uncertain blocks and the first and last block are kept even if they exceed it. - label (optional, string): Optional CSV of SocialCrawl labels to add to every post. Without this param every page already carries sponsored, intent and niche, free; label= adds the labels you name to them (the defaults keep running). sponsored, intent and niche are free when asked for too; mention, quality, ai_style, intent with offer=, and injection add 1 credit per started 25 newly judged posts. judgments=off (or label=none) turns the default labels off. sponsored: is the post a paid or gifted promotion (p, 0 to 1), did it carry a disclosure marker such as #ad or 광고 (disclosed), is it likely paid with no marker (undisclosed), and which of the accounts it mentions does it promote (brand, or null). These are signals to review, never a finding. mention (needs brand=): is the post about that brand rather than something that shares its name (about_brand, 0 to 1), how it feels about the brand on five levels (sentiment_level 0 to 4 and sentiment_score 0 to 1, null when the post is not about the brand), is it sarcastic, which aspect it talks about (taste_or_quality, price_or_value, availability_or_delivery, health_or_safety, advertising_or_campaign, customer_service, none), and did the author buy or use it (first_hand). intent: what the author is mainly doing (label: asking_for_recommendation, comparing_options, switching_away, complaining, promoting, news_or_discussion, other, or null when unsure, with confidence), whether they read as a potential buyer rather than a seller (buyer, seller), how pressing the need is (urgency, 0 to 3), and, when you pass offer=, whether your offer would plausibly help them (fits_offer). niche: which of the 33 niches of the published taxonomy sc-niche-v1 the caption belongs to, or personal_no_niche, or other (label, confidence, taxonomy), with label null when the caption is too thin to tell or the pick is unsure. It reads the caption only, not the video. quality: how much checkable detail the caption carries (fact_density 0 to 3), whether it mainly asks for likes, replies, shares, follows or tags (engagement_bait, 0 to 1), whether it is written to provoke anger as a way to get engagement (rage_bait, 0 to 1; about the writing, never the side taken), whether it only repeats someone else's news or view (secondhand, 0 to 1), and what the post is mainly doing (post_aim: inform, opinion, sell, entertain, provoke, other). exclude=engagement_bait drops posts whose engagement_bait is 0.8 or higher. ai_style: writing patterns common in AI-generated text, judged on the text alone. It does not say who wrote the post, and a person who writes in this style is flagged too. Six patterns are judged, each a probability (0 to 1): binary_contrast ("This is not X. It's Y."), faux_insight ("Here's what nobody tells you" and throat-clearing openers), colon_reveal ("The best part: it learns."), profound_kicker (ends on an aphorism or mic-drop line), puffery ("marks a pivotal moment", "highlighting the importance of") and rhetorical_setup ("What if I told you", a question the post answers itself, "Not a X. Not a Y. A Z."). Four are counted in the text: slop_words (words and phrases such as delve, leverage, robust, game changer, at the end of the day), em_dashes, emoji_bullets (lines that start with an emoji or a symbol bullet) and one_line_paragraphs (paragraphs that are one short line). pattern_count is how many patterns are present (a judged pattern at 0.8 or higher, slop_words 1 or more, em_dashes 2 or more, emoji_bullets 2 or more, one_line_paragraphs 5 or more) and markers lists their names, comma-separated. A post with less than about eight words of text is only counted, with every judged pattern 0. The judged patterns read the first 400 and the last 150 characters of a long post, and the counts read the whole post. exclude=ai_style drops posts with 2 or more patterns. ai_written: how likely the post was written by an AI model, from SocialCrawl's own text classifier, reading the opening of the post (about 1,000 characters): p (0 to 1, calibrated), level (likely (p 0.95 or higher), possible (p 0.71 or higher) or unlikely) and model (the model name; a new name means a new scale). A post under 8 words is too short to read: p null, level too_short. It is a probability, not proof: a person can be flagged and an AI-written post can pass, and the shorter the post the less certain it is. exclude=ai_written drops likely posts. injection flags text that addresses an AI system and tries to direct it (flagged, p); it never drops or rewrites a row. A post that could not be judged carries labels: null. Posts already labelled for anyone are free, and so is a cached page. data.labels reports what was judged and billed. - exclude (optional, string): Optional CSV (engagement_bait, ai_style, ai_written). engagement_bait, only with label=quality, drops posts whose engagement_bait probability is 0.8 or higher. ai_style, only with label=ai_style, drops posts that show 2 or more of its writing patterns (pattern_count 2 or higher). ai_written, only with label=ai_written, drops posts whose level is likely (p 0.95 or higher), or whose p reaches your ai_written_threshold; a post too short to read is never dropped. Dropped posts are listed in data.labels.dropped_ids. A post that could not be judged is never dropped. - ai_written_threshold (optional, string): Optional, only with label=ai_written and exclude=ai_written. The p (0 to 1) at which exclude=ai_written drops a post. Default 0.95 (level likely). 0.71 also drops possible posts: more AI posts go, and more posts by people with them. - brand (optional, string): Required by label=mention, ignored otherwise. The brand or product the posts are judged against, up to 300 characters, for example brand=Buldak. Without it label=mention is skipped with the warning label_mention_needs_brand and is not billed. - brand_description (optional, string): Optional, with label=mention. One plain sentence saying what the brand is, for example brand_description=Samyang's spicy instant noodle brand. Use it when the name is also an ordinary word or another company's name. - offer (optional, string): Optional, with label=intent. One or two plain sentences saying what you sell, up to 300 characters, for example offer=A web design agency that builds websites for small businesses. Adds fits_offer to every post; without it fits_offer is null. - label_evidence (optional, enum: 1): Optional, only with label=. When 1, every labelled row also carries computed.labels_evidence. = { quote, sentence_index }: the sentence in the row that most clearly shows the label, copied verbatim. Absent or null when no single sentence shows it. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - min_views (optional, integer): Keep only rows with at least this many views (`post.engagement.views`). A row whose view count is unknown is discarded, so every returned row meets the floor. Filtering runs on our side after each page is fetched, so every page walked is billed as usual; `data.walk.discarded` counts what was removed and why. Each page is its own entry in `GET /v1/credits/transactions`, under the page's request id listed in `data.walk.request_ids`. - max_age_days (optional, integer): Keep only rows published within this many days (`post.published_at`), 1 to 3650. A row with no date is discarded. Filtering runs on our side after each page is fetched, so every page walked is billed as usual; `data.walk.discarded` counts what was removed and why. Each page is its own entry in `GET /v1/credits/transactions`, under the page's request id listed in `data.walk.request_ids`. - sort_rows (optional, enum: views): `views`: return the kept rows ordered by view count, highest first, across every page walked. Rows without a view count go last. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them (after any filter). Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows (after any filter), then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. - seen (optional, string): An id you choose (1 to 64 letters, digits, `.`, `_` or `-`). Rows your account already received under the same `seen` id are removed from the page, and the page price falls with the share of repeats: page credits x new rows / rows on the page, rounded up, so a page of nothing but repeats is free. Only the page's own price is discounted: credits for joined data (`include=`) are charged only for rows you have not received, and are never discounted. The memory lasts 24 hours from the last call that used the id, holds row ids only, and is private to your account. Use one id across a set of related searches; `data.walk.repeats` counts what was removed. ``` curl -G "https://www.socialcrawl.dev/v1/instagram/search/reels" \ --data-urlencode "query=workout routine" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /instagram/media/transcript Get Instagram media transcript Credit cost: 10 (premium) Parameters: - url (required): Full URL of the Instagram video or reel (a `/reel/`, `/p/` or `/tv/` link to a video post). Photo posts have no audio to transcribe. A share link copied from the app (`instagram.com/share/...`) works too; a share link to a profile is refused at no charge with the handle to send, and a dead one is a free 404. ``` curl -G "https://www.socialcrawl.dev/v1/instagram/media/transcript" \ --data-urlencode "url=https://www.instagram.com/reel/DHsD6HGqJhp/" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /instagram/media/screen-text Get Instagram post on-screen text Credit cost: 5-10 (metered): 5 credits a call, the same for a photo, a reel's cover or a carousel page of 10 slides. 10 credits with `frames=all` on a reel; when the post has no video to read, or the video cannot be read and the cover is read instead, the call costs 5 and the response says so in `warnings`. Refunded when the post cannot be found, when nothing on it could be read, or when a parameter does not fit the post (a `slide_offset` past the last slide, a reel over 180 seconds with `frames=all`). Parameters: - url (required): Full URL of the Instagram post, reel or video (a `/p/`, `/reel/`, `/reels/` or `/tv/` link). A share link copied from the app (`instagram.com/share/...`) works too; a share link to a profile is refused at no charge with the handle to send, and a dead one is a free 404. - frames (optional, enum: cover | all): What to read on a reel or video. `cover` (the default) reads the cover frame for 5 credits. `all` reads the whole video, up to 180 seconds, for 10 credits, and adds `subtitles`, `timeline` and `duration_seconds`. Has no effect on photos and carousels, which are read as usual for 5 credits. - slide_offset (optional, integer): Carousels only: the 0-based index of the first slide to read, 10 slides a call. Default 0. `slide_offset=10` reads slides 11 to 20. Each image in `images` keeps its index in the whole carousel, and `next_slide_offset` gives the value for the next call. ``` curl -G "https://www.socialcrawl.dev/v1/instagram/media/screen-text" \ --data-urlencode "url=https://www.instagram.com/p/DdEqEl5Dl-S/" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /instagram/user/embed Get Instagram user embed HTML Credit cost: 1 (standard) Parameters: - handle (required): Instagram username without the @ symbol ``` curl -G "https://www.socialcrawl.dev/v1/instagram/user/embed" \ --data-urlencode "handle=instagram" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /instagram/audio/reels List Instagram reels using an audio track Credit cost: 1 (standard) Parameters: - audio_id (required): Instagram audio ID: the numeric id from an instagram.com/reels/audio/{audio_id}/ URL - cursor (optional, string): Pagination cursor. Use the `next_cursor` from the previous response to fetch the next page. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/instagram/audio/reels" \ --data-urlencode "audio_id=1392969992841787" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /instagram/search/hashtag Feed of a known hashtag, 1 credit a page, every ranking paginates (for keyword or topic discovery use instagram/search/reels) Credit cost: 1-31 (metered): 1 credit per page, on every `type`. `max_items` and `max_pages` walk more pages in one call at 1 credit each, and `max_credits` caps the total. A page Instagram returns empty is not charged. `include=country` holds 30 more credits a page and keeps 1 per creator whose declared country came back, refunding a creator who publishes none or cannot be found, so a page with it costs 1 to 31. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. With `seen`, a page's price falls with its share of rows you already received under that id; credits for joined data (`include=`) are never discounted. Parameters: - hashtag (required): The hashtag to search for. The leading # is optional. - type (optional, enum: top | recent | clips): Ranking of the returned posts: `top` (default), `recent` (newest first), or `clips` (reels only). Every ranking paginates. - cursor (optional, string): Pagination cursor. Use the `next_cursor` from the previous response and send the same `type` again. - safe_url (optional, boolean): When true, returns URL-safe media links suitable for embedding. - include (optional, enum: country): Send `country` to add each post's creator country as `post.ext.author_country`: the country the creator declares on Instagram's About this account panel, not where the post was filmed, and null when the creator publishes none. Every row carries the creator's numeric id as `post.ext.author_id`, which is what it is read by. 1 extra credit per creator whose country came back; two posts by one creator cost one lookup, a creator looked up in the last 30 days is free, and a creator who publishes none or cannot be found is refunded. A typo is a free 400. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off the page. With `type=recent` the pages run from newest to oldest (the posts inside one page are not in date order), so the first page with no post on or after the date ends the walk. It comes back empty,`next_cursor` is not returned and `pagination.stopped_at` is `since`, and you pay for the pages you walk and no more. Other `type` values are not in date order, so there the walk does not stop at the date and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - min_views (optional, integer): Keep only rows with at least this many views (`post.engagement.views`). A row whose view count is unknown is discarded, so every returned row meets the floor. Filtering runs on our side after each page is fetched, so every page walked is billed as usual; `data.walk.discarded` counts what was removed and why. Each page is its own entry in `GET /v1/credits/transactions`, under the page's request id listed in `data.walk.request_ids`. - max_age_days (optional, integer): Keep only rows published within this many days (`post.published_at`), 1 to 3650. A row with no date is discarded. Filtering runs on our side after each page is fetched, so every page walked is billed as usual; `data.walk.discarded` counts what was removed and why. Each page is its own entry in `GET /v1/credits/transactions`, under the page's request id listed in `data.walk.request_ids`. - sort_rows (optional, enum: views): `views`: return the kept rows ordered by view count, highest first, across every page walked. Rows without a view count go last. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them (after any filter). Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows (after any filter), then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. - seen (optional, string): An id you choose (1 to 64 letters, digits, `.`, `_` or `-`). Rows your account already received under the same `seen` id are removed from the page, and the page price falls with the share of repeats: page credits x new rows / rows on the page, rounded up, so a page of nothing but repeats is free. Only the page's own price is discounted: credits for joined data (`include=`) are charged only for rows you have not received, and are never discounted. The memory lasts 24 hours from the last call that used the id, holds row ids only, and is private to your account. Use one id across a set of related searches; `data.walk.repeats` counts what was removed. ``` curl -G "https://www.socialcrawl.dev/v1/instagram/search/hashtag" \ --data-urlencode "hashtag=makeup" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /instagram/search/profiles Search Instagram profiles by keyword Credit cost: 1-25 (metered): 1 credit for the search page. With `include=about`, 1 plus 2 credits per row filled, held at the top 8 (17) unless you send `limit`, refunded down to the rows that actually received an About-panel leaf. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - query (required): Keyword or phrase matched against account usernames and display names, as Instagram's own account search does. - cursor (optional, string): Pagination cursor. Pass the `next_cursor` from the previous response to get the next page; treat it as an opaque token. - include (optional, enum: about): Send `about` to fill `author.ext.country`, join month and public contact from the About panel, 2 extra credits a row filled, never more than 12 rows. A typo is a free 400. - limit (optional, integer): Cap the rows served, and with `include=about` the credits held. 1 to 12. Default 8 when the token is present so the hold is 17, not 25. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/instagram/search/profiles" \ --data-urlencode "query=yoga" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /instagram/reels/trending Get trending Instagram reels Credit cost: 5 (advanced) Parameters: - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. ``` curl "https://www.socialcrawl.dev/v1/instagram/reels/trending" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /instagram/followers List Instagram followers (oneOf: handle|user_id) Credit cost: 5-10 (metered): 5 credits a page. Up to three reads are merged into one page, typically about 50 accounts, and a page can carry fewer. With `coverage=full`, up to 10 credits a page, settled down to 5 when the plain walk covered the whole page. A page that fails is refunded. With `query`, 5 credits for its one window, and nothing when no account matches. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - handle (optional, string): Instagram username without the @ symbol. - user_id (optional, string): Instagram numeric user ID. Use this for faster responses. - cursor (optional, string): Pagination cursor. Use the `next_cursor` from the previous response to fetch the next page. - coverage (optional, enum: full): Send `full` to walk a merged list: accounts are added from several reads of the list and no account appears twice inside a page, until the rows reach the profile's count in `data.total`. On the pages one source covered on its own a handful of accounts can repeat across consecutive pages; wherever that source cannot cover the list the merged walk runs instead, and it never returns an account it has already given you. Requires `handle`. 10 credits a page instead of 5, settled down to 5 on any page a single read covered in full. Up to three reads are merged into one page, typically about 50 accounts, so a full list costs at most roughly the account count divided by 50, times 10 credits: on 13/09/2026 a following list of 2,652 took 54 pages and a follower list of 2,360 took 59, because the later pages of a follower walk add fewer new accounts. A page usually takes about 8 seconds and can take up to about 45, and a `503` is retried with the same cursor. A cursor from this mode only continues this mode. - query (optional, string): A username or name to look for in the list. Returns the accounts whose username or display name contains it (ignoring case and accents) in one window with no cursor, at the price of a page, and nothing is charged when no account matches. Cannot be combined with `cursor` or `coverage`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. - Constraint: one of handle, user_id (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/instagram/followers" \ --data-urlencode "handle=mrbeast" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /instagram/following List Instagram following (oneOf: handle|user_id) Credit cost: 5-10 (metered): 5 credits a page. Up to three reads are merged into one page, typically about 50 accounts, and a page can carry fewer. With `coverage=full`, up to 10 credits a page, settled down to 5 when the plain walk covered the whole page. A page that fails is refunded. With `query`, 5 credits for its one window, and nothing when no account matches. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - handle (optional, string): Instagram username without the @ symbol. - user_id (optional, string): Instagram numeric user ID. Use this for faster responses. - cursor (optional, string): Pagination cursor. Use the `next_cursor` from the previous response to fetch the next page. - coverage (optional, enum: full): Send `full` to walk a merged list: accounts are added from several reads of the list and no account appears twice inside a page, until the rows reach the profile's count in `data.total`. On the pages one source covered on its own a handful of accounts can repeat across consecutive pages; wherever that source cannot cover the list the merged walk runs instead, and it never returns an account it has already given you. Requires `handle`. 10 credits a page instead of 5, settled down to 5 on any page a single read covered in full. Up to three reads are merged into one page, typically about 50 accounts, so a full list costs at most roughly the account count divided by 50, times 10 credits: on 13/09/2026 a following list of 2,652 took 54 pages and a follower list of 2,360 took 59, because the later pages of a follower walk add fewer new accounts. A page usually takes about 8 seconds and can take up to about 45, and a `503` is retried with the same cursor. A cursor from this mode only continues this mode. - query (optional, string): A username or name to look for in the list. Returns the accounts whose username or display name contains it (ignoring case and accents) in one window with no cursor, at the price of a page, and nothing is charged when no account matches. Cannot be combined with `cursor` or `coverage`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. - Constraint: one of handle, user_id (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/instagram/following" \ --data-urlencode "handle=mrbeast" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /instagram/similar List similar Instagram accounts (oneOf: handle|user_id) Credit cost: 5-85 (metered): 5 credits for the list. include=profile holds 1 credit per row and keeps only the rows filled from a fresh lookup; the hydrated list is its top 20 accounts by default (at most 25 credits) and `limit=80` buys the full roster (at most 85). Rows served from cache are free, unfilled rows are refunded, and a repeat within the cache window costs 0. The list and the per-account lookups are cached for 15 minutes. Parameters: - handle (optional, string): Instagram username without the @ symbol. - user_id (optional, string): Instagram numeric user ID. Use this for faster responses. - include (optional, enum: profile): Set to `profile` (one token only) to fill `author.followers`, `author.following` and `author.bio` on every row in this one call, plus `author.posts_count` where the lookup has an exact total. The hydrated list is its top 20 accounts unless you pass `limit` (1 to 80). Holds 1 credit per row and keeps only the rows filled from a fresh lookup; rows already in cache and rows that could not be filled are refunded, so the most a call can cost is 5 + rows (25 by default, 85 with `limit=80`) and the usual bill is lower. Adds 3 to 6 seconds for 20 rows (never more than 12) and about four times that for 80, and nothing when the accounts are already cached; read `data.hydration` for the rows, the credits held and kept, and the time. - limit (optional, integer): Take the top N accounts of the list (1 to 80). With `include=profile` the list defaults to its top 20 (at most 25 credits) and `limit` caps both the rows and the extra credits: `limit=80&include=profile` buys the full roster for at most 85 credits. Without `include=profile` the full list is returned unless you cap it. - Constraint: one of handle, user_id (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/instagram/similar" \ --data-urlencode "handle=mrbeast" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /instagram/post/likers List Instagram post likers Credit cost: 5 (advanced) Parameters: - url (required): URL of the Instagram post (a /p/ or /reel/ link). A share link copied from the app (`instagram.com/share/...`) works too; a share link to a profile is refused at no charge with the handle to send, and a dead one is a free 404. - safe_url (optional, boolean): When true, returns URL-safe profile picture links suitable for embedding. ``` curl -G "https://www.socialcrawl.dev/v1/instagram/post/likers" \ --data-urlencode "url=https://www.instagram.com/p/CnpPou9hWqq/" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /instagram/post/stats Get Instagram post stats including the share count Credit cost: 5-9 (metered): 5 credits for the post. include=saves holds 4 more and keeps them only when it fills a save count the plain call lacked, refunding them otherwise, so a hydrated call is at most 9 credits; on a reel whose save count the plain call already carries, and on any photo or carousel, it settles at 5. Post lookups are cached for 10 minutes. Parameters: - url (required): URL of the Instagram post (a /p/, /reel/, or /tv/ link). A share link copied from the app (`instagram.com/share/...`) works too; a share link to a profile is refused at no charge with the handle to send, and a dead one is a free 404. - safe_url (optional, boolean): When true, returns URL-safe media links suitable for embedding. - include (optional, enum: saves): Set to `saves` (one token only) to ask a third source for a reel's save count (`post.engagement.saves`) and repost count (`post.ext.repost_count`) when the plain call returned without them. 4 extra credits, kept only when a save count comes back; a repeat within the cache window is 0. Photos and carousels are never looked up, because Instagram publishes no save count on them. Adds 3 to 6 seconds when a lookup runs; read `data.hydration` for the credits held and kept and the time. ``` curl -G "https://www.socialcrawl.dev/v1/instagram/post/stats" \ --data-urlencode "url=https://www.instagram.com/p/CnpPou9hWqq/" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /instagram/tagged List posts an Instagram user is tagged in (oneOf: handle|user_id) Credit cost: 5 (advanced) Parameters: - handle (optional, string): Instagram username without the @ symbol. - user_id (optional, string): Instagram numeric user ID. Use this for faster responses. - cursor (optional, string): Pagination cursor. Use the `next_cursor` from the previous response to fetch the next page. - safe_url (optional, boolean): When true, returns URL-safe media links suitable for embedding. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. - Constraint: one of handle, user_id (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/instagram/tagged" \ --data-urlencode "handle=mrbeast" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /instagram/location/posts List recent posts at an Instagram location Credit cost: 5-68 (metered): 5 credits per page. `include=country` holds 63 more credits a page and keeps 1 per creator whose declared country came back, refunding a creator who publishes none or cannot be found, so a page with it costs 5 to 68. A page that fails is refunded in full and looks no creator up. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - location_id (required): Instagram location id. Use location.pk from /v1/instagram/search/location. - cursor (optional, string): Pagination cursor. Use the `next_cursor` from the previous response to fetch the next, older page. - safe_url (optional, boolean): When true, returns URL-safe media links suitable for embedding. - include (optional, enum: country): Send `country` to add each post's creator country as `post.ext.author_country`: the country the creator declares on Instagram's About this account panel, not the place the post is tagged at, and null when the creator publishes none. Every row carries the creator's numeric id as `post.ext.author_id`, which is what it is read by. 1 extra credit per creator whose country came back; two posts by one creator cost one lookup, a creator looked up in the last 30 days is free, and a creator who publishes none or cannot be found is refunded. A typo is a free 400. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/instagram/location/posts" \ --data-urlencode "location_id=331004901" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /instagram/engagement Get Instagram engagement statistics Credit cost: 5 (advanced) Parameters: - handle (required): Instagram username without the @ symbol. ``` curl -G "https://www.socialcrawl.dev/v1/instagram/engagement" \ --data-urlencode "handle=mrbeast" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /instagram/search/location Search Instagram locations Credit cost: 5 (advanced) Parameters: - query (required): Search keyword or phrase to find Instagram locations. ``` curl -G "https://www.socialcrawl.dev/v1/instagram/search/location" \ --data-urlencode "query=Paris" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /instagram/search Search Instagram accounts, hashtags, and places Credit cost: 1 (standard) Parameters: - query (required): An account, hashtag or place name as typed in Instagram's search box (a name, not a topic sentence). For posts about a topic use instagram/search/reels. ``` curl -G "https://www.socialcrawl.dev/v1/instagram/search" \ --data-urlencode "query=nike" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /instagram/search/popular Search popular Instagram posts Credit cost: 1-13 (metered): 1 credit for the page. include=engagement holds 1 credit per row, at most 12 (or `limit`), and keeps only the rows filled from a fresh lookup, so a full page is at most 13 credits; rows served from cache are free, unfilled rows are refunded, and a repeat of the same call within the cache window costs 0. Search pages are cached for 2 minutes and the per-post lookups for 10 minutes. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - query (required): Keyword or topic to find popular Instagram posts. - cursor (optional, string): Cursor from the previous response to fetch the next page. - include (optional, enum: engagement): Set to `engagement` (one token only) to fill `post.engagement.likes`, `.comments`, `.shares`, `.views`, `post.published_at` and `post.content.duration_seconds` on every row in this one call. Holds 1 credit per row (at most 12, or `limit`) and keeps only the rows filled from a fresh lookup; rows already in cache and rows that could not be filled are refunded, so the most a call can cost is 1 + rows and the usual bill is lower. Adds 2 to 9 seconds on a fresh page (never more than 12) and nothing when the rows are already cached; read `data.hydration` for the rows, the credits held and kept, and the time. - limit (optional, integer): Take the top N rows of the page (1 to 12) after the search has run. With `include=engagement` it also caps the extra credits at N: `limit=5&include=engagement` costs at most 6. It is not a page size: `next_cursor` still advances past the full page, so rows beyond N on this page are not returned by the next page. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/instagram/search/popular" \ --data-urlencode "query=basketball" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /instagram/post/comment/replies List replies under an Instagram comment Credit cost: 1-5 (metered): 1 credit per page, with the default labels (sentiment, question, purchase_intent, complaint) included free. label=spam, toxic or low_quality holds 4 extra credits and refunds down to 1 credit per started 25 comments that were newly judged: comments already labelled are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - url (required): URL of the Instagram post that holds the parent comment. A share link copied from the app (`instagram.com/share/...`) works too; a share link to a profile is refused at no charge with the handle to send, and a dead one is a free 404. - comment_id (required): Id of the parent comment whose replies to list. - cursor (optional, string): Cursor from the previous response to fetch the next page. - label (optional, string): Optional CSV of SocialCrawl labels to add to every comment: sentiment, spam, question, purchase_intent, complaint, toxic, low_quality, injection. Without this param every page already carries sentiment, question, purchase_intent and complaint, free; label= adds the labels you name to them (the defaults keep running). Each comment gains computed.labels with probabilities (0 to 1), so you choose the cut-off; a comment that could not be judged carries labels: null. sentiment, question, purchase_intent and complaint are free when asked for too; spam, toxic, low_quality and injection add 1 credit per started 25 newly judged comments (comments already labelled for anyone are free, and so is a cached page). injection flags text that addresses an AI system and tries to direct it (flagged, p); it never drops or rewrites a row. judgments=off (or label=none) turns the default labels off. data.labels reports what was judged and billed. - exclude (optional, string): Optional CSV (spam, low_quality), only with the same preset in label. Drops comments whose probability is 0.8 or higher and lists their ids in data.labels.dropped_ids. A comment that could not be judged is never dropped. - judgments (optional, enum: on | off): Optional, on (the default) or off. By default every row gains free SocialCrawl judgments (computed.labels, and computed.relevance on search endpoints), reported in data.labels (mode default) and data.relevance (origin default), each with a status (complete, partial or skipped) and pending: the rows still being judged when the page was sent, which carry null now and are filled on your next call or cached read. Default judgments never add credits, never change an existing field, and never drop or reorder a row. off returns the page exactly as before, with none of those keys. label=none does the same. - dry_run (optional, enum: 1): Optional. When 1, return a cost preview for this labelled or relevance-filtered request without fetching the page or judging any row. data.estimate reports rows_expected, rows_cached, label_credits_min, label_credits_max and base_credits. 0 credits charged. - label_evidence (optional, enum: 1): Optional, only with label=. When 1, every labelled row also carries computed.labels_evidence. = { quote, sentence_index }: the sentence in the row that most clearly shows the label, copied verbatim. Absent or null when no single sentence shows it. - fit (optional, enum: goal): Optional. When goal, keep the rows and fields needed for the goal you pass in goal= (plus any that are uncertain, and the first and last), and replace the rest with a stub. data.held_back lists the held ids and a recall id that re-reads the full page from cache at no extra charge. Without this param the page is unchanged. - goal (optional, string): Required by fit=goal. What you are trying to do, in your own words, up to 300 characters. - fit_tokens (optional, integer): Optional, only with fit=goal. Soft cap on how much of the page to keep, in tokens. Uncertain blocks and the first and last block are kept even if they exceed it. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/instagram/post/comment/replies" \ --data-urlencode "url=https://www.instagram.com/reel/C8rKmYvsrck" \ --data-urlencode "comment_id=18038110327814211" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /instagram/username-suggestions Get Instagram username suggestions Credit cost: 5 (advanced) Parameters: - query (required): Keyword to seed the username suggestions. ``` curl -G "https://www.socialcrawl.dev/v1/instagram/username-suggestions" \ --data-urlencode "query=mrbeast" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /instagram/search/music Search Instagram music Credit cost: 5 (advanced) Parameters: - query (required): Search keyword or phrase to find Instagram audio tracks. - cursor (optional, string): Pagination cursor. Use the `next_cursor` from the previous response to fetch the next page. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/instagram/search/music" \ --data-urlencode "query=beyonce" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /instagram/stories List an Instagram user's active stories (oneOf: handle|user_id) Credit cost: 5 (advanced) Parameters: - handle (optional, string): Instagram username without the @ symbol. - user_id (optional, string): Instagram numeric user ID. Use this for faster responses. - safe_url (optional, boolean): Optional and usually ignored: most responses carry ordinary media URLs whether or not you send it. Same price either way. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - Constraint: one of handle, user_id (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/instagram/stories" \ --data-urlencode "handle=instagram" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /instagram/story/download Download a single Instagram story (oneOf: user_id) (oneOf: story_id) Credit cost: 5 (advanced) Parameters: - user_id (optional, string): Instagram numeric user ID of the story's author. - story_id (optional, string): ID of the individual story to download. - safe_url (optional, boolean): When true, returns URL-safe media links suitable for embedding. - Constraint: one of user_id (at least one required) - Constraint: one of story_id (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/instagram/story/download" \ --data-urlencode "user_id=25025320" \ --data-urlencode "story_id=3926693776893966474" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /instagram/music/trending List trending Instagram music Credit cost: 5 (advanced) ``` curl "https://www.socialcrawl.dev/v1/instagram/music/trending" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /instagram/hashtag Get an Instagram hashtag's size: its exact media count (oneOf: hashtag) Credit cost: 1 (standard) Parameters: - hashtag (optional, string): Hashtag name, with or without the leading # symbol. - Constraint: one of hashtag (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/instagram/hashtag" \ --data-urlencode "hashtag=streetfood" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /instagram/location Get an Instagram location's profile (oneOf: location_id) Credit cost: 1 (standard) Parameters: - location_id (optional, string): Instagram location id: the numeric `location.pk` from /v1/instagram/search/location, or the number in an instagram.com/explore/locations/{location_id}/ URL. - Constraint: one of location_id (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/instagram/location" \ --data-urlencode "location_id=103912118089363" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /instagram/comment/likers List the accounts that liked an Instagram comment (oneOf: comment_id|url) Credit cost: 1 (standard) Parameters: - comment_id (optional, string): Numeric id of the Instagram comment. - url (optional, string): Permalink of the comment: https://www.instagram.com/p/{shortcode}/c/{comment_id}/ (a reply permalink names the reply). - cursor (optional, string): Pagination cursor. Use the `next_cursor` from the previous response to fetch the next page. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. - Constraint: one of comment_id, url (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/instagram/comment/likers" \ --data-urlencode "comment_id=18133095883781285" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /instagram/profile/full Instagram profile, recent posts, and computed analytics in one call. (oneOf: handle) Credit cost: 5 (override; tier standard) Parameters: - handle (optional, string): Instagram username or handle, with or without a leading @. One of the identity params is required. - posts (optional, integer): How many of the fetched recent posts to return and compute the metrics over (1-100, default 25). One call reads one page, which on Instagram is 12 posts, so a larger value returns that page and a `_warnings` note; pass posts_cursor back as cursor for the next page. - cursor (optional, string): Pass a prior response's posts_cursor to read the next page of posts. Each call is a separate 5-credit call whose metrics cover that page only. - include (optional, string): CSV subset of posts,computed (default both). include=computed omits the raw posts[] array from the response to save payload. The metrics are computed from the same fetched posts either way, so they do not change. - Constraint: one of handle (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/instagram/profile/full" \ --data-urlencode "handle=mrbeast" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /instagram/profile/reels/full Instagram reels with views, likes, comments, and per-reel share counts where available, in one call. (oneOf: handle|user_id) Credit cost: 5-25 (metered): 5 credits per upstream page of about 12 reels. With no `limit` that is one page and a flat 5 credits. With `limit` set the composite walks `ceil(limit / 12)` pages - 3 for limit=30, 5 for the 50 maximum, so 25 credits at the top - holds that many up front and refunds every page it did not need. One partial refund sits inside the per-page price: a shares leg that fails outright refunds 4 of the 5 credits for that page, leaving the 1-credit list price. A shares leg that succeeds with low coverage is still the full 5, because the second source was queried either way. A `user_id` call is priced exactly like a `handle` call Parameters: - handle (optional, string): Instagram username (with or without a leading @). Required unless user_id is given. - user_id (optional, string): Numeric Instagram user id. Alternative to handle: the account's username is looked up from the id first, so a user_id call returns the same items, share counts and price as a handle call. An id that matches no account returns 404 and is not charged. - cursor (optional, string): Opaque cursor from a prior response's next_cursor to page deeper. - limit (optional, integer): Return up to this many items in one call (1-50). The endpoint pages the underlying source server-side until it has collected this many (or runs out), and bills per upstream page consumed (5 credits/page). Omit for a single page. - Constraint: one of handle, user_id (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/instagram/profile/reels/full" \ --data-urlencode "handle=sid.and.listen" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /instagram/profile/posts/full Instagram posts with views, likes, comments, and per-post share counts where available, in one call. (oneOf: handle|user_id) Credit cost: 5-25 (metered): 5 credits per upstream page of about 12 posts. With no `limit` that is one page and a flat 5 credits. With `limit` set the composite walks `ceil(limit / 12)` pages - 3 for limit=30, 5 for the 50 maximum, so 25 credits at the top - holds that many up front and refunds every page it did not need. One partial refund sits inside the per-page price: a shares leg that fails outright refunds 4 of the 5 credits for that page, leaving the 1-credit list price. A shares leg that succeeds with low coverage is still the full 5, because the second source was queried either way. A `user_id` call is priced exactly like a `handle` call Parameters: - handle (optional, string): Instagram username (with or without a leading @). Required unless user_id is given. - user_id (optional, string): Numeric Instagram user id. Alternative to handle: the account's username is looked up from the id first, so a user_id call returns the same items, share counts and price as a handle call. An id that matches no account returns 404 and is not charged. - cursor (optional, string): Opaque cursor from a prior response's next_cursor to page deeper. - limit (optional, integer): Return up to this many items in one call (1-50). The endpoint pages the underlying source server-side until it has collected this many (or runs out), and bills per upstream page consumed (5 credits/page). Omit for a single page. - Constraint: one of handle, user_id (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/instagram/profile/posts/full" \ --data-urlencode "handle=sid.and.listen" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /instagram/search/mentions Search Instagram posts that tag or mention an account Credit cost: 0-6 (metered): Metered, 0-6 credits: each call reads one page of /v1/instagram/tagged (5 credits) and /v1/instagram/search/reels (1 credit), each billed at that endpoint's own price exactly as a direct call is. A page that found nothing or failed costs nothing, and a page served from cache is free. A page is billed when it returned rows, even if none of them carried the mention; data.legs shows each page's charge and data.excluded what was left out. Parameters: - handle (required): The account's username, with or without the @, or its profile URL. - cursor (optional, string): data.next_cursor from the previous response, sent back verbatim for the next page. - since (optional, string): Only posts published on or after this date, YYYY-MM-DD (UTC). - until (optional, string): Only posts published on or before this date, YYYY-MM-DD (UTC); that whole day is included. - include_self (optional, boolean): When true, also keep posts by the account itself. Default false. ``` curl -G "https://www.socialcrawl.dev/v1/instagram/search/mentions" \ --data-urlencode "handle=myntra" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## Jobs ### GET /jobs/linkedin/search Search LinkedIn job listings (oneOf: query|cursor) Credit cost: 10 (premium) Parameters: - query (optional, string): Keywords, job title, or company name. - location (optional, string): City or country. Defaults to worldwide when omitted. - organization_ids (optional, string): Semicolon-separated LinkedIn organization ids from GET /v1/jobs/linkedin/organizations. - date_posted (optional, enum: month | week | day): Maximum age: month, week, or day. - employment_types (optional, string): Semicolon-separated: contractor, fulltime, parttime, intern, temporary. - experience_levels (optional, string): Semicolon-separated: intern, entry, associate, midSenior, director. - workplace_types (optional, string): Semicolon-separated: remote, hybrid, onSite. - cursor (optional, string): Opaque cursor from the previous page. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. - Constraint: one of query, cursor (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/jobs/linkedin/search" \ --data-urlencode "query=software engineer" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /jobs/linkedin Get a LinkedIn job listing by id Credit cost: 5 (advanced) Parameters: - id (required): Numeric LinkedIn job id from linkedin/search. ``` curl -G "https://www.socialcrawl.dev/v1/jobs/linkedin" \ --data-urlencode "id=4460359588" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /jobs/linkedin/organizations Resolve LinkedIn organization ids Credit cost: 1 (standard) Parameters: - query (required): Company or school name. ``` curl -G "https://www.socialcrawl.dev/v1/jobs/linkedin/organizations" \ --data-urlencode "query=google" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /jobs/indeed/search Search Indeed job listings (oneOf: query|cursor) (oneOf: country_code|cursor) Credit cost: 10 (premium) Parameters: - query (optional, string): Keywords, job title, or company name. - location (optional, string): City or region within the country. - country_code (optional, string): ISO 3166-1 alpha-2 country code (for example us, de, kr). - sort_type (optional, enum: relevance | date): relevance (default) or date. - radius (optional, integer): Distance from location. - radius_type (optional, enum: km | miles): km (default) or miles. - cursor (optional, string): Opaque cursor from the previous page. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. - Constraint: one of query, cursor (at least one required) - Constraint: one of country_code, cursor (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/jobs/indeed/search" \ --data-urlencode "query=software engineer" \ --data-urlencode "country_code=us" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /jobs/indeed Get an Indeed job listing by id Credit cost: 5 (advanced) Parameters: - id (required): Indeed job id from indeed/search. ``` curl -G "https://www.socialcrawl.dev/v1/jobs/indeed" \ --data-urlencode "id=aWQ9NWExYzEwM2YzOTEzODljNDtjYz11cw==" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /jobs/bing/search Search Bing job listings (oneOf: query|cursor) (oneOf: location|cursor) Credit cost: 10 (premium) Parameters: - query (optional, string): Keywords, job title, or company name. - location (optional, string): City or country. Required on page 1. - date_posted (optional, enum: week | day): Maximum age: week or day. - employment_types (optional, string): Semicolon-separated: contractor, fulltime, parttime, temporary. - remote_only (optional, boolean): When true, only remote listings. - cursor (optional, string): Opaque cursor from the previous page. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. - Constraint: one of query, cursor (at least one required) - Constraint: one of location, cursor (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/jobs/bing/search" \ --data-urlencode "query=software engineer" \ --data-urlencode "location=United States" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /jobs/bing Get a Bing job listing by id Credit cost: 5 (advanced) Parameters: - id (required): Bing job id from bing/search. ``` curl -G "https://www.socialcrawl.dev/v1/jobs/bing" \ --data-urlencode "id=NTU5NDY0MDEzLlJldHJv" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /jobs/xing/search Search Xing job listings (oneOf: query|cursor) Credit cost: 10 (premium) Parameters: - query (optional, string): Keywords, job title, or company name. Required on page 1. - location (optional, string): City or country, preferably in German (for example Berlin, Schweiz). - date_posted (optional, enum: month | week | day): Maximum age: month, week, or day. - employment_types (optional, string): Semicolon-separated: contractor, fulltime, intern, parttime, seasonal, temporary, voluntary. - career_levels (optional, string): Semicolon-separated: student, entry, professional, manager, executive, seniorExecutive. - remote_options (optional, string): Semicolon-separated: remote, hybrid, onSite. - minimum_salary (optional, integer): Minimum yearly salary. Listings whose range intersects this floor are returned. - cursor (optional, string): Opaque cursor from the previous page. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. - Constraint: one of query, cursor (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/jobs/xing/search" \ --data-urlencode "query=software engineer" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /jobs/xing Get a Xing job listing by id Credit cost: 5 (advanced) Parameters: - id (required): Xing job id from xing/search. ``` curl -G "https://www.socialcrawl.dev/v1/jobs/xing" \ --data-urlencode "id=YmVybGluLXNvZnR3YXJlZW50d2lja2xlci0xNTY4OTYwMjk=" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /jobs/salary/titles Suggest job titles for a salary lookup Credit cost: 1 (standard) Parameters: - query (required): Partial job title (for example programming, nurse). - country_code (required): ISO 3166-1 alpha-2 country code. ``` curl -G "https://www.socialcrawl.dev/v1/jobs/salary/titles" \ --data-urlencode "query=software" \ --data-urlencode "country_code=us" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /jobs/salary Get salary ranges for a job title and country Credit cost: 1 (standard) Parameters: - query (required): Job title, preferably one returned by salary/titles. - country_code (required): ISO 3166-1 alpha-2 country code. ``` curl -G "https://www.socialcrawl.dev/v1/jobs/salary" \ --data-urlencode "query=software engineer" \ --data-urlencode "country_code=us" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## Kick ### GET /kick/clip Get Kick clip details Credit cost: 1 (standard) Parameters: - url (required): Full URL of the Kick clip ``` curl -G "https://www.socialcrawl.dev/v1/kick/clip" \ --data-urlencode "url=https://kick.com/xqc/clips/clip_01JGJHB6CEVFCQRYTVPM8DW892" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## Klarna ### GET /klarna/product Get a Klarna product by id or URL (oneOf: product_id|url) Credit cost: 5 (advanced) Parameters: - product_id (optional, string): Klarna product id, the numeric id in a shopping URL: klarna.com/us/shopping/pl/cl94/3331931731/... is product id 3331931731. Product ids also come back on every row of GET /v1/klarna/search and GET /v1/klarna/category. - url (optional, string): Full Klarna shopping URL, for example https://www.klarna.com/us/shopping/pl/cl94/3331931731/Headphones/Apple-AirPods-4-with-Active-Noise-Cancellation-White/. The product id and category id are extracted from the path. - category_id (optional, string): Klarna category id, for example cl94 for Headphones or t1 for TV and Audio. Ids come from GET /v1/klarna/categories and from search rows as category.id. Required for GET /v1/klarna/product when you pass product_id instead of a shopping URL. - region (optional, enum: usa | uk | germany | sweden | france | italy | spain | netherlands | austria | ireland | norway | finland | denmark): Shopping region: usa (default), uk, germany, sweden, france, italy, spain, netherlands, austria, ireland, norway, finland, denmark. Changes currency, language, and the merchant set. - Constraint: one of product_id, url (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/klarna/product" \ --data-urlencode "product_id=3331931731" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /klarna/product/offers List merchant offers for a Klarna product (oneOf: product_id|url) Credit cost: 5 (advanced) Parameters: - product_id (optional, string): Klarna product id, the numeric id in a shopping URL: klarna.com/us/shopping/pl/cl94/3331931731/... is product id 3331931731. Product ids also come back on every row of GET /v1/klarna/search and GET /v1/klarna/category. - url (optional, string): Full Klarna shopping URL, for example https://www.klarna.com/us/shopping/pl/cl94/3331931731/Headphones/Apple-AirPods-4-with-Active-Noise-Cancellation-White/. The product id and category id are extracted from the path. - region (optional, enum: usa | uk | germany | sweden | france | italy | spain | netherlands | austria | ireland | norway | finland | denmark): Shopping region: usa (default), uk, germany, sweden, france, italy, spain, netherlands, austria, ireland, norway, finland, denmark. Changes currency, language, and the merchant set. - Constraint: one of product_id, url (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/klarna/product/offers" \ --data-urlencode "product_id=3331931731" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /klarna/search Search Klarna products by keyword Credit cost: 5 (advanced) Parameters: - query (required): Search keyword, for example headphone or airpods. - region (optional, enum: usa | uk | germany | sweden | france | italy | spain | netherlands | austria | ireland | norway | finland | denmark): Shopping region: usa (default), uk, germany, sweden, france, italy, spain, netherlands, austria, ireland, norway, finland, denmark. Changes currency, language, and the merchant set. ``` curl -G "https://www.socialcrawl.dev/v1/klarna/search" \ --data-urlencode "query=headphone" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /klarna/search/suggestions Get Klarna search suggestions Credit cost: 1 (standard) Parameters: - query (required): Partial search query to autocomplete, for example headphone. - region (optional, enum: usa | uk | germany | sweden | france | italy | spain | netherlands | austria | ireland | norway | finland | denmark): Shopping region: usa (default), uk, germany, sweden, france, italy, spain, netherlands, austria, ireland, norway, finland, denmark. Changes currency, language, and the merchant set. ``` curl -G "https://www.socialcrawl.dev/v1/klarna/search/suggestions" \ --data-urlencode "query=headphone" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /klarna/reviews Get Klarna user reviews for a product (oneOf: product_id|url) Credit cost: 5-9 (metered): 5 credits per page. The default labels (sentiment, issue) are free. label=reports, incentivized or injection holds 4 extra credits and refunds down to 1 credit per started 25 reviews that were newly judged (the first 100 reviews of a page are judged): reviews already labelled are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - product_id (optional, string): Klarna product id, the numeric id in a shopping URL: klarna.com/us/shopping/pl/cl94/3331931731/... is product id 3331931731. Product ids also come back on every row of GET /v1/klarna/search and GET /v1/klarna/category. - url (optional, string): Full Klarna shopping URL, for example https://www.klarna.com/us/shopping/pl/cl94/3331931731/Headphones/Apple-AirPods-4-with-Active-Noise-Cancellation-White/. The product id and category id are extracted from the path. - region (optional, enum: usa | uk | germany | sweden | france | italy | spain | netherlands | austria | ireland | norway | finland | denmark): Shopping region: usa (default), uk, germany, sweden, france, italy, spain, netherlands, austria, ireland, norway, finland, denmark. Changes currency, language, and the merchant set. - limit (optional, integer): Reviews per page, 1 to 50. Default 10. - nextBatchId (optional, integer): Pagination token from the previous page. Prefer the universal cursor parameter. - label (optional, string): Optional CSV of SocialCrawl labels to add to every review. Without this param every page already carries sentiment and issue, free; label= adds the labels you name to them (the defaults keep running). sentiment and issue are free when asked for too; reports, incentivized and injection add 1 credit per started 25 newly judged reviews. judgments=off (or label=none) turns the default labels off. They read the review's title and text, never its stars. sentiment: how the reviewer feels, on five levels (level 0 to 4, score_0_1, confidence), plus rating_mismatch, true when the words clearly contradict the star rating (a glowing text under 1 star, a furious one under 5), null when the review has no rating. issue: the main problem the review reports (label: product_defect, sizing_or_fit, shipping_or_delivery, customer_service, price_or_value, missing_feature, other, or none, with confidence; label is null when unsure). reports (needs reports=): does this review say that the thing you describe happened (p, 0 to 1). incentivized: does the reviewer say they got the product free, discounted or rewarded for the review (p), and did the text carry a disclosure such as 'in exchange for my honest review', Vine or 체험단 (disclosed). injection flags text that addresses an AI system and tries to direct it (flagged, p); it never drops or rewrites a row. These are signals to read, never a verdict on a review or a reviewer. A review that could not be judged carries labels: null. Reviews already labelled for anyone are free, and so is a cached page. data.labels reports what was judged and billed. - reports (optional, string): Required by label=reports, ignored otherwise. What to look for, in plain words, up to 300 characters, for example reports=the battery drains quickly or reports=배송이 늦음. Every review gains labels.reports.p, the probability that it says so, in any wording or language. Without it label=reports is skipped with the warning label_reports_needs_reports and is not billed. - judgments (optional, enum: on | off): Optional, on (the default) or off. By default every row gains free SocialCrawl judgments (computed.labels, and computed.relevance on search endpoints), reported in data.labels (mode default) and data.relevance (origin default), each with a status (complete, partial or skipped) and pending: the rows still being judged when the page was sent, which carry null now and are filled on your next call or cached read. Default judgments never add credits, never change an existing field, and never drop or reorder a row. off returns the page exactly as before, with none of those keys. label=none does the same. - dry_run (optional, enum: 1): Optional. When 1, return a cost preview for this labelled or relevance-filtered request without fetching the page or judging any row. data.estimate reports rows_expected, rows_cached, label_credits_min, label_credits_max and base_credits. 0 credits charged. - label_evidence (optional, enum: 1): Optional, only with label=. When 1, every labelled row also carries computed.labels_evidence. = { quote, sentence_index }: the sentence in the row that most clearly shows the label, copied verbatim. Absent or null when no single sentence shows it. - fit (optional, enum: goal): Optional. When goal, keep the rows and fields needed for the goal you pass in goal= (plus any that are uncertain, and the first and last), and replace the rest with a stub. data.held_back lists the held ids and a recall id that re-reads the full page from cache at no extra charge. Without this param the page is unchanged. - goal (optional, string): Required by fit=goal. What you are trying to do, in your own words, up to 300 characters. - fit_tokens (optional, integer): Optional, only with fit=goal. Soft cap on how much of the page to keep, in tokens. Uncertain blocks and the first and last block are kept even if they exceed it. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. - Constraint: one of product_id, url (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/klarna/reviews" \ --data-urlencode "product_id=3331931731" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /klarna/reviews/pro Get Klarna professional reviews for a product (oneOf: product_id|url) Credit cost: 5-9 (metered): 5 credits per page. The default labels (sentiment, issue) are free. label=reports, incentivized or injection holds 4 extra credits and refunds down to 1 credit per started 25 reviews that were newly judged (the first 100 reviews of a page are judged): reviews already labelled are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - product_id (optional, string): Klarna product id, the numeric id in a shopping URL: klarna.com/us/shopping/pl/cl94/3331931731/... is product id 3331931731. Product ids also come back on every row of GET /v1/klarna/search and GET /v1/klarna/category. - url (optional, string): Full Klarna shopping URL, for example https://www.klarna.com/us/shopping/pl/cl94/3331931731/Headphones/Apple-AirPods-4-with-Active-Noise-Cancellation-White/. The product id and category id are extracted from the path. - region (optional, enum: usa | uk | germany | sweden | france | italy | spain | netherlands | austria | ireland | norway | finland | denmark): Shopping region: usa (default), uk, germany, sweden, france, italy, spain, netherlands, austria, ireland, norway, finland, denmark. Changes currency, language, and the merchant set. - limit (optional, integer): Reviews per page, 1 to 50. Default 10. - nextBatchId (optional, integer): Pagination token from the previous page. Prefer the universal cursor parameter. - label (optional, string): Optional CSV of SocialCrawl labels to add to every review. Without this param every page already carries sentiment and issue, free; label= adds the labels you name to them (the defaults keep running). sentiment and issue are free when asked for too; reports, incentivized and injection add 1 credit per started 25 newly judged reviews. judgments=off (or label=none) turns the default labels off. They read the review's title and text, never its stars. sentiment: how the reviewer feels, on five levels (level 0 to 4, score_0_1, confidence), plus rating_mismatch, true when the words clearly contradict the star rating (a glowing text under 1 star, a furious one under 5), null when the review has no rating. issue: the main problem the review reports (label: product_defect, sizing_or_fit, shipping_or_delivery, customer_service, price_or_value, missing_feature, other, or none, with confidence; label is null when unsure). reports (needs reports=): does this review say that the thing you describe happened (p, 0 to 1). incentivized: does the reviewer say they got the product free, discounted or rewarded for the review (p), and did the text carry a disclosure such as 'in exchange for my honest review', Vine or 체험단 (disclosed). injection flags text that addresses an AI system and tries to direct it (flagged, p); it never drops or rewrites a row. These are signals to read, never a verdict on a review or a reviewer. A review that could not be judged carries labels: null. Reviews already labelled for anyone are free, and so is a cached page. data.labels reports what was judged and billed. - reports (optional, string): Required by label=reports, ignored otherwise. What to look for, in plain words, up to 300 characters, for example reports=the battery drains quickly or reports=배송이 늦음. Every review gains labels.reports.p, the probability that it says so, in any wording or language. Without it label=reports is skipped with the warning label_reports_needs_reports and is not billed. - judgments (optional, enum: on | off): Optional, on (the default) or off. By default every row gains free SocialCrawl judgments (computed.labels, and computed.relevance on search endpoints), reported in data.labels (mode default) and data.relevance (origin default), each with a status (complete, partial or skipped) and pending: the rows still being judged when the page was sent, which carry null now and are filled on your next call or cached read. Default judgments never add credits, never change an existing field, and never drop or reorder a row. off returns the page exactly as before, with none of those keys. label=none does the same. - dry_run (optional, enum: 1): Optional. When 1, return a cost preview for this labelled or relevance-filtered request without fetching the page or judging any row. data.estimate reports rows_expected, rows_cached, label_credits_min, label_credits_max and base_credits. 0 credits charged. - label_evidence (optional, enum: 1): Optional, only with label=. When 1, every labelled row also carries computed.labels_evidence. = { quote, sentence_index }: the sentence in the row that most clearly shows the label, copied verbatim. Absent or null when no single sentence shows it. - fit (optional, enum: goal): Optional. When goal, keep the rows and fields needed for the goal you pass in goal= (plus any that are uncertain, and the first and last), and replace the rest with a stub. data.held_back lists the held ids and a recall id that re-reads the full page from cache at no extra charge. Without this param the page is unchanged. - goal (optional, string): Required by fit=goal. What you are trying to do, in your own words, up to 300 characters. - fit_tokens (optional, integer): Optional, only with fit=goal. Soft cap on how much of the page to keep, in tokens. Uncertain blocks and the first and last block are kept even if they exceed it. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. - Constraint: one of product_id, url (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/klarna/reviews/pro" \ --data-urlencode "product_id=3331931731" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /klarna/reviews/overview Get Klarna review totals and score distributions (oneOf: product_id|url) Credit cost: 5 (advanced) Parameters: - product_id (optional, string): Klarna product id, the numeric id in a shopping URL: klarna.com/us/shopping/pl/cl94/3331931731/... is product id 3331931731. Product ids also come back on every row of GET /v1/klarna/search and GET /v1/klarna/category. - url (optional, string): Full Klarna shopping URL, for example https://www.klarna.com/us/shopping/pl/cl94/3331931731/Headphones/Apple-AirPods-4-with-Active-Noise-Cancellation-White/. The product id and category id are extracted from the path. - region (optional, enum: usa | uk | germany | sweden | france | italy | spain | netherlands | austria | ireland | norway | finland | denmark): Shopping region: usa (default), uk, germany, sweden, france, italy, spain, netherlands, austria, ireland, norway, finland, denmark. Changes currency, language, and the merchant set. - Constraint: one of product_id, url (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/klarna/reviews/overview" \ --data-urlencode "product_id=3331931731" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /klarna/product/price-history Get Klarna price history for a product (oneOf: product_id|url) Credit cost: 5 (advanced) Parameters: - product_id (optional, string): Klarna product id, the numeric id in a shopping URL: klarna.com/us/shopping/pl/cl94/3331931731/... is product id 3331931731. Product ids also come back on every row of GET /v1/klarna/search and GET /v1/klarna/category. - url (optional, string): Full Klarna shopping URL, for example https://www.klarna.com/us/shopping/pl/cl94/3331931731/Headphones/Apple-AirPods-4-with-Active-Noise-Cancellation-White/. The product id and category id are extracted from the path. - region (optional, enum: usa | uk | germany | sweden | france | italy | spain | netherlands | austria | ireland | norway | finland | denmark): Shopping region: usa (default), uk, germany, sweden, france, italy, spain, netherlands, austria, ireland, norway, finland, denmark. Changes currency, language, and the merchant set. - interval (optional, enum: ONE_MONTH | THREE_MONTHS | INFINITE_DAYS): History window: ONE_MONTH, THREE_MONTHS (default), or INFINITE_DAYS. - Constraint: one of product_id, url (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/klarna/product/price-history" \ --data-urlencode "product_id=3331931731" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /klarna/product/compare Compare two Klarna products Credit cost: 5 (advanced) Parameters: - product_id (required): First Klarna product id. - product_id_2 (required): Second Klarna product id. - region (optional, enum: usa | uk | germany | sweden | france | italy | spain | netherlands | austria | ireland | norway | finland | denmark): Shopping region: usa (default), uk, germany, sweden, france, italy, spain, netherlands, austria, ireland, norway, finland, denmark. Changes currency, language, and the merchant set. ``` curl -G "https://www.socialcrawl.dev/v1/klarna/product/compare" \ --data-urlencode "product_id=3331931731" \ --data-urlencode "product_id_2=3216399795" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /klarna/category Browse Klarna products in a category Credit cost: 5 (advanced) Parameters: - category_id (required): Klarna category id, for example cl94 for Headphones or t1 for TV and Audio. Ids come from GET /v1/klarna/categories and from search rows as category.id. Required for GET /v1/klarna/product when you pass product_id instead of a shopping URL. - region (optional, enum: usa | uk | germany | sweden | france | italy | spain | netherlands | austria | ireland | norway | finland | denmark): Shopping region: usa (default), uk, germany, sweden, france, italy, spain, netherlands, austria, ireland, norway, finland, denmark. Changes currency, language, and the merchant set. - size (optional, integer): Products per page, 1 to 100. Default 48. - offset (optional, integer): Row offset, starting at 0. Prefer the universal cursor parameter. - sort_by (optional, enum: popularity | price_low | price_high | trend | name | rating | price_drop): Result ordering: popularity (default), price_low, price_high, trend, name, rating, or price_drop. - price_range (optional, string): Price filter as min-max, for example 50-500, or 50- for 50 and above. - filter (optional, string): Facet filter as keyID-valueID from GET /v1/klarna/category/filters. - category_path_id (optional, string): Sub-category path id as keyID-valueID. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/klarna/category" \ --data-urlencode "category_id=cl94" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /klarna/categories List Klarna root categories Credit cost: 1 (standard) Parameters: - region (optional, enum: usa | uk | germany | sweden | france | italy | spain | netherlands | austria | ireland | norway | finland | denmark): Shopping region: usa (default), uk, germany, sweden, france, italy, spain, netherlands, austria, ireland, norway, finland, denmark. Changes currency, language, and the merchant set. ``` curl "https://www.socialcrawl.dev/v1/klarna/categories" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /klarna/categories/children Get Klarna sub-categories Credit cost: 1 (standard) Parameters: - category_id (required): Klarna category id, for example cl94 for Headphones or t1 for TV and Audio. Ids come from GET /v1/klarna/categories and from search rows as category.id. Required for GET /v1/klarna/product when you pass product_id instead of a shopping URL. - region (optional, enum: usa | uk | germany | sweden | france | italy | spain | netherlands | austria | ireland | norway | finland | denmark): Shopping region: usa (default), uk, germany, sweden, france, italy, spain, netherlands, austria, ireland, norway, finland, denmark. Changes currency, language, and the merchant set. ``` curl -G "https://www.socialcrawl.dev/v1/klarna/categories/children" \ --data-urlencode "category_id=t1" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /klarna/category/filters Get Klarna filters for a category Credit cost: 1 (standard) Parameters: - category_id (required): Klarna category id, for example cl94 for Headphones or t1 for TV and Audio. Ids come from GET /v1/klarna/categories and from search rows as category.id. Required for GET /v1/klarna/product when you pass product_id instead of a shopping URL. - region (optional, enum: usa | uk | germany | sweden | france | italy | spain | netherlands | austria | ireland | norway | finland | denmark): Shopping region: usa (default), uk, germany, sweden, france, italy, spain, netherlands, austria, ireland, norway, finland, denmark. Changes currency, language, and the merchant set. - category_path_id (optional, string): Optional sub-category path id as keyID-valueID. ``` curl -G "https://www.socialcrawl.dev/v1/klarna/category/filters" \ --data-urlencode "category_id=cl94" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /klarna/category/keywords Get popular Klarna search keywords for a category Credit cost: 1 (standard) Parameters: - category_id (required): Klarna category id, for example cl94 for Headphones or t1 for TV and Audio. Ids come from GET /v1/klarna/categories and from search rows as category.id. Required for GET /v1/klarna/product when you pass product_id instead of a shopping URL. - region (optional, enum: usa | uk | germany | sweden | france | italy | spain | netherlands | austria | ireland | norway | finland | denmark): Shopping region: usa (default), uk, germany, sweden, france, italy, spain, netherlands, austria, ireland, norway, finland, denmark. Changes currency, language, and the merchant set. ``` curl -G "https://www.socialcrawl.dev/v1/klarna/category/keywords" \ --data-urlencode "category_id=t1" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /klarna/category/guide Get Klarna buying-guide content for a category Credit cost: 1 (standard) Parameters: - category_id (required): Klarna category id, for example cl94 for Headphones or t1 for TV and Audio. Ids come from GET /v1/klarna/categories and from search rows as category.id. Required for GET /v1/klarna/product when you pass product_id instead of a shopping URL. - region (optional, enum: usa | uk | germany | sweden | france | italy | spain | netherlands | austria | ireland | norway | finland | denmark): Shopping region: usa (default), uk, germany, sweden, france, italy, spain, netherlands, austria, ireland, norway, finland, denmark. Changes currency, language, and the merchant set. - size (optional, integer): Number of guiding items, 1 to 20. Default 4. ``` curl -G "https://www.socialcrawl.dev/v1/klarna/category/guide" \ --data-urlencode "category_id=cl94" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /klarna/stores List Klarna shopping stores Credit cost: 1 (standard) Parameters: - region (optional, enum: usa | uk | germany | sweden | france | italy | spain | netherlands | austria | ireland | norway | finland | denmark): Shopping region: usa (default), uk, germany, sweden, france, italy, spain, netherlands, austria, ireland, norway, finland, denmark. Changes currency, language, and the merchant set. - size (optional, integer): Stores per page, 1 to 100. Default 24. - offset (optional, integer): Row offset, starting at 0. Prefer the universal cursor parameter. - categories (optional, string): Store category filter, for example fashion or travel. - cashback (optional, boolean): When true, keep stores that offer cashback. - shop_in_app (optional, boolean): When true, keep stores available in the Klarna app. - klarna_at_checkout (optional, boolean): When true, keep stores with Klarna at checkout. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl "https://www.socialcrawl.dev/v1/klarna/stores" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /klarna/store/products List products from a Klarna store Credit cost: 5 (advanced) Parameters: - id (required): Klarna store UUID, the last segment of storeKrn: 09f4d635-0452-4f63-9455-c45420d95669 for Walmart US. Ids come back on every row of GET /v1/klarna/stores. - region (optional, enum: usa | uk | germany | sweden | france | italy | spain | netherlands | austria | ireland | norway | finland | denmark): Shopping region: usa (default), uk, germany, sweden, france, italy, spain, netherlands, austria, ireland, norway, finland, denmark. Changes currency, language, and the merchant set. - size (optional, integer): Products per page, 1 to 100. Default 25. - offset (optional, integer): Row offset, starting at 0. Prefer the universal cursor parameter. - sort_by (optional, enum: popularity | price_low | price_high | trend | name | rating | price_drop): Result ordering: popularity (default), price_low, price_high, trend, name, rating, or price_drop. - price_range (optional, string): Price filter as min-max, for example 50-500, or 50- for 50 and above. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/klarna/store/products" \ --data-urlencode "id=09f4d635-0452-4f63-9455-c45420d95669" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /klarna/store/filters Get Klarna filters for a store page Credit cost: 1 (standard) Parameters: - id (required): Klarna store UUID, the last segment of storeKrn: 09f4d635-0452-4f63-9455-c45420d95669 for Walmart US. Ids come back on every row of GET /v1/klarna/stores. - region (optional, enum: usa | uk | germany | sweden | france | italy | spain | netherlands | austria | ireland | norway | finland | denmark): Shopping region: usa (default), uk, germany, sweden, france, italy, spain, netherlands, austria, ireland, norway, finland, denmark. Changes currency, language, and the merchant set. ``` curl -G "https://www.socialcrawl.dev/v1/klarna/store/filters" \ --data-urlencode "id=09f4d635-0452-4f63-9455-c45420d95669" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## Kohls ### GET /kohls/search Search Kohl's products by keyword Credit cost: 5 (advanced) Parameters: - query (required): Search keyword, for example jeans, tshirt, towels. - page (optional, integer): Page number, starting at 1. Prefer the universal cursor parameter. - limit (optional, integer): Products per page, 1 to 48. Default 12. Values above 48 are clamped. The origin default of 120 is a half-megabyte facet dump and is not forwarded. - sort_by (optional, enum: featured | newest | price_low | price_high | rating | percent_off): Result ordering: featured (default), newest, price_low, price_high, rating, or percent_off. - filters (optional, string): Kohl's dimension filter string. Example: Color:Black. Combine with commas. Dimension ids appear on every search payload under payload.dimensions. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/kohls/search" \ --data-urlencode "query=jeans" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /kohls/reviews Get Kohl's product reviews (oneOf: product_id|url) Credit cost: 5-9 (metered): 5 credits per page. The default labels (sentiment, issue) are free. label=reports, incentivized or injection holds 4 extra credits and refunds down to 1 credit per started 25 reviews that were newly judged (the first 100 reviews of a page are judged): reviews already labelled are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - product_id (optional, string): Kohl's product id, the digits after prd- in a product URL: www.kohls.com/product/prd-1695762/... is product id 1695762. Product ids also come back on every row of GET /v1/kohls/search. - url (optional, string): Kohl's product page URL, for example https://www.kohls.com/product/prd-1695762/gloria-vanderbilt-amanda-tapered-jeans-womens.jsp. The product id is parsed from prd-{id}. - page (optional, integer): Page number, starting at 1. Prefer the universal cursor parameter. - limit (optional, integer): Reviews per page, 1 to 50. Default 8. - label (optional, string): Optional CSV of SocialCrawl labels to add to every review. Without this param every page already carries sentiment and issue, free; label= adds the labels you name to them (the defaults keep running). sentiment and issue are free when asked for too; reports, incentivized and injection add 1 credit per started 25 newly judged reviews. judgments=off (or label=none) turns the default labels off. They read the review's title and text, never its stars. sentiment: how the reviewer feels, on five levels (level 0 to 4, score_0_1, confidence), plus rating_mismatch, true when the words clearly contradict the star rating (a glowing text under 1 star, a furious one under 5), null when the review has no rating. issue: the main problem the review reports (label: product_defect, sizing_or_fit, shipping_or_delivery, customer_service, price_or_value, missing_feature, other, or none, with confidence; label is null when unsure). reports (needs reports=): does this review say that the thing you describe happened (p, 0 to 1). incentivized: does the reviewer say they got the product free, discounted or rewarded for the review (p), and did the text carry a disclosure such as 'in exchange for my honest review', Vine or 체험단 (disclosed). injection flags text that addresses an AI system and tries to direct it (flagged, p); it never drops or rewrites a row. These are signals to read, never a verdict on a review or a reviewer. A review that could not be judged carries labels: null. Reviews already labelled for anyone are free, and so is a cached page. data.labels reports what was judged and billed. - reports (optional, string): Required by label=reports, ignored otherwise. What to look for, in plain words, up to 300 characters, for example reports=the battery drains quickly or reports=배송이 늦음. Every review gains labels.reports.p, the probability that it says so, in any wording or language. Without it label=reports is skipped with the warning label_reports_needs_reports and is not billed. - judgments (optional, enum: on | off): Optional, on (the default) or off. By default every row gains free SocialCrawl judgments (computed.labels, and computed.relevance on search endpoints), reported in data.labels (mode default) and data.relevance (origin default), each with a status (complete, partial or skipped) and pending: the rows still being judged when the page was sent, which carry null now and are filled on your next call or cached read. Default judgments never add credits, never change an existing field, and never drop or reorder a row. off returns the page exactly as before, with none of those keys. label=none does the same. - dry_run (optional, enum: 1): Optional. When 1, return a cost preview for this labelled or relevance-filtered request without fetching the page or judging any row. data.estimate reports rows_expected, rows_cached, label_credits_min, label_credits_max and base_credits. 0 credits charged. - label_evidence (optional, enum: 1): Optional, only with label=. When 1, every labelled row also carries computed.labels_evidence. = { quote, sentence_index }: the sentence in the row that most clearly shows the label, copied verbatim. Absent or null when no single sentence shows it. - fit (optional, enum: goal): Optional. When goal, keep the rows and fields needed for the goal you pass in goal= (plus any that are uncertain, and the first and last), and replace the rest with a stub. data.held_back lists the held ids and a recall id that re-reads the full page from cache at no extra charge. Without this param the page is unchanged. - goal (optional, string): Required by fit=goal. What you are trying to do, in your own words, up to 300 characters. - fit_tokens (optional, integer): Optional, only with fit=goal. Soft cap on how much of the page to keep, in tokens. Uncertain blocks and the first and last block are kept even if they exceed it. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. - Constraint: one of product_id, url (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/kohls/reviews" \ --data-urlencode "product_id=1695762" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /kohls/questions Get Kohl's product questions and answers (oneOf: product_id|url) Credit cost: 5 (advanced) Parameters: - product_id (optional, string): Kohl's product id, the digits after prd- in a product URL: www.kohls.com/product/prd-1695762/... is product id 1695762. Product ids also come back on every row of GET /v1/kohls/search. - url (optional, string): Kohl's product page URL, for example https://www.kohls.com/product/prd-1695762/gloria-vanderbilt-amanda-tapered-jeans-womens.jsp. The product id is parsed from prd-{id}. - page (optional, integer): Page number, starting at 1. Prefer the universal cursor parameter. - limit (optional, integer): Questions per page, 1 to 50. Default 10. - Constraint: one of product_id, url (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/kohls/questions" \ --data-urlencode "product_id=1695762" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /kohls/stores Find Kohl's stores near a coordinate (oneOf: location_coordinate|latitude) Credit cost: 5 (advanced) Parameters: - location_coordinate (optional, string): Latitude and longitude as lat,lng, for example 40.758,-73.9855. - latitude (optional, string): Latitude of the search centre, for example 40.758. - longitude (optional, string): Longitude of the search centre, for example -73.9855. - radius (optional, integer): Search radius in miles, 1 to 100. Default 25. - Constraint: one of location_coordinate, latitude (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/kohls/stores" \ --data-urlencode "location_coordinate=40.758,-73.9855" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /kohls/categories Get the Kohl's category tree Credit cost: 1 (standard) ``` curl "https://www.socialcrawl.dev/v1/kohls/categories" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## Komi ### GET /komi/page Get Komi page Credit cost: 1 (standard) Parameters: - url (required): Full URL of the Komi page ``` curl -G "https://www.socialcrawl.dev/v1/komi/page" \ --data-urlencode "url=https://kimkardashian.komi.io/" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## Kwai ### GET /kwai/profile Get a Kwai user profile (oneOf: handle|url) Credit cost: 1 (standard) Parameters: - handle (optional, string): Kwai profile handle (without the @) - url (optional, string): Kwai profile URL - Constraint: one of handle, url (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/kwai/profile" \ --data-urlencode "handle=KwaiBrasilOficial" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /kwai/user/posts List a Kwai user's posts (oneOf: handle|url) Credit cost: 1 (standard) Parameters: - handle (optional, string): Kwai profile handle (without the @) - url (optional, string): Kwai profile URL - cursor (optional, string): Cursor from the previous response for the next page - count (optional, integer): Number of posts to return (max 50) - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. - Constraint: one of handle, url (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/kwai/user/posts" \ --data-urlencode "handle=KwaiBrasilOficial" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /kwai/post Get a Kwai post Credit cost: 1 (standard) Parameters: - url (optional, string): Full URL of the Kwai post ``` curl "https://www.socialcrawl.dev/v1/kwai/post" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## Linkbio ### GET /linkbio/page Get Linkbio page Credit cost: 1 (standard) Parameters: - url (required): Full URL of the Linkbio page ``` curl -G "https://www.socialcrawl.dev/v1/linkbio/page" \ --data-urlencode "url=https://lnk.bio/example" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## Linkedin ### GET /linkedin/profile Get LinkedIn user profile Credit cost: 5 (advanced) Parameters: - url (required): Full URL of the LinkedIn profile page ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/profile" \ --data-urlencode "url=https://www.linkedin.com/in/williamhgates/" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/company Get LinkedIn company page with its full About tab Credit cost: 5-8 (metered): 5 credits for the company page. include=about holds 3 more and keeps them only when it fills the company type or tagline the plain call lacked, refunding them otherwise, so a call with include=about is at most 8 credits; when the plain call already carried both, it settles at 5. Company lookups are cached. Parameters: - url (required): Full URL of the LinkedIn company page - include (optional, enum: about): Set to `about` (one token only) to add the page's company type (`author.ext.company_type`, for example "Public Company") and tagline (`author.ext.tagline`) when the plain call returned without them. 3 extra credits, kept only when one of the two comes back; a page that sets neither is refunded. Adds about 7 to 18 seconds when a lookup runs; read `data.hydration` for the credits held and kept and the time. ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/company" \ --data-urlencode "url=https://www.linkedin.com/company/microsoft/" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/company/by-domain Look up a LinkedIn company page from its website domain Credit cost: 5 (advanced) Parameters: - domain (required): Company website domain, with or without a scheme. `microsoft.com` and `https://www.microsoft.com/` resolve the same page. ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/company/by-domain" \ --data-urlencode "domain=microsoft.com" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/profile/similar List LinkedIn members similar to a given profile Credit cost: 5 (advanced) Parameters: - url (required): Full URL of the LinkedIn profile to find similar members for. ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/profile/similar" \ --data-urlencode "url=https://www.linkedin.com/in/williamhgates/" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/search/companies Search LinkedIn company pages Credit cost: 10 (premium) Parameters: - query (required): Company name or keyword. - page (optional, integer): 1-based page number. Defaults to 1. - company_size (optional, enum: 1-10 | 11-50 | 51-200 | 201-500 | 501-1000 | 1001-5000 | 5001-10000 | 10001+): Headcount bucket. One of `1-10`, `11-50`, `51-200`, `201-500`, `501-1000`, `1001-5000`, `5001-10000`, `10001+`. - has_jobs (optional, boolean): When true, return only companies that currently have open jobs. - industry_ids (optional, string): Comma-separated industry ids from /v1/linkedin/search/industry. - geocode (optional, string): Comma-separated location ids from /v1/linkedin/search/location. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/search/companies" \ --data-urlencode "query=microsoft" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/profile/posted-jobs List jobs a LinkedIn member has posted Credit cost: 5 (advanced) Parameters: - url (required): Full URL of the LinkedIn profile whose posted jobs to list. ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/profile/posted-jobs" \ --data-urlencode "url=https://www.linkedin.com/in/jipeng-han/" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/job/hiring-team List the hiring team on a LinkedIn job (oneOf: id|url) Credit cost: 5 (advanced) Parameters: - id (optional, string): LinkedIn job ID (from /v1/linkedin/search/jobs or /v1/linkedin/company/jobs). - url (optional, string): Full URL of the LinkedIn job posting. - Constraint: one of id, url (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/job/hiring-team" \ --data-urlencode "id=4463963538" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/profile/articles List Pulse articles a LinkedIn member published Credit cost: 5 (advanced) Parameters: - url (required): Full URL of the LinkedIn profile whose articles to list. - page (optional, integer): 1-based page number. Defaults to 1. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/profile/articles" \ --data-urlencode "url=https://www.linkedin.com/in/williamhgates/" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/article Get a LinkedIn Pulse article Credit cost: 5 (advanced) Parameters: - url (required): Full URL of the LinkedIn Pulse article. ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/article" \ --data-urlencode "url=https://www.linkedin.com/pulse/hidden-costs-unreliable-electricity-bill-gates/" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/article/comments List comments on a LinkedIn Pulse article Credit cost: 5 (advanced) Parameters: - url (required): Full URL of the LinkedIn Pulse article. - page (optional, integer): 1-based page number. Defaults to 1. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/article/comments" \ --data-urlencode "url=https://www.linkedin.com/pulse/2024-corporate-climate-pivot-bill-gates-u89mc/" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/article/reactions List reactors on a LinkedIn Pulse article Credit cost: 5 (advanced) Parameters: - url (required): Full URL of the LinkedIn Pulse article. - page (optional, integer): 1-based page number. Defaults to 1. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/article/reactions" \ --data-urlencode "url=https://www.linkedin.com/pulse/2024-corporate-climate-pivot-bill-gates-u89mc/" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/search/hashtag Search LinkedIn posts by hashtag Credit cost: 10 (premium) Parameters: - query (required): Hashtag to search, with or without a leading #. - sort_by (optional, enum: recent | oldest | relevance): Result ordering: recent, oldest, or relevance. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/search/hashtag" \ --data-urlencode "query=hiring" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/profile/activity Get when a LinkedIn member was last active Credit cost: 5 (advanced) Parameters: - url (required): Full URL of the LinkedIn profile. ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/profile/activity" \ --data-urlencode "url=https://www.linkedin.com/in/adamselipsky/" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/company/also-viewed List company pages people also viewed (oneOf: company_id|url) Credit cost: 5 (advanced) Parameters: - company_id (optional, string): Numeric LinkedIn company id, for example 1441 for Google. It is `author.id` on `/v1/linkedin/company`. Send this or `url`. - url (optional, string): Full URL of the LinkedIn company page, for example https://www.linkedin.com/company/google/. Send this or `company_id`. - Constraint: one of company_id, url (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/company/also-viewed" \ --data-urlencode "company_id=1441" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/company/employees-count Get a company's employee count, optionally by location Credit cost: 5 (advanced) Parameters: - company_id (required): LinkedIn numeric company ID (from /v1/linkedin/company). - geocode (optional, string): Comma-separated location ids from /v1/linkedin/search/location. ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/company/employees-count" \ --data-urlencode "company_id=1441" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/profile/interests/schools List schools a LinkedIn member follows Credit cost: 5 (advanced) Parameters: - url (required): Full URL of the LinkedIn profile. - page (optional, integer): 1-based page number. Defaults to 1. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/profile/interests/schools" \ --data-urlencode "url=https://www.linkedin.com/in/williamhgates/" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/profile/interests/newsletters List newsletters a LinkedIn member follows Credit cost: 5 (advanced) Parameters: - url (required): Full URL of the LinkedIn profile. - page (optional, integer): 1-based page number. Defaults to 1. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/profile/interests/newsletters" \ --data-urlencode "url=https://www.linkedin.com/in/williamhgates/" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/profile/interests/top-voices List Top Voices a LinkedIn member follows Credit cost: 5 (advanced) Parameters: - url (required): Full URL of the LinkedIn profile. ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/profile/interests/top-voices" \ --data-urlencode "url=https://www.linkedin.com/in/williamhgates/" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/profile/position-skills List a member's positions with the skills on each role Credit cost: 5 (advanced) Parameters: - url (required): Full URL of the LinkedIn profile. ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/profile/position-skills" \ --data-urlencode "url=https://www.linkedin.com/in/tedgaubert/" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/profile/top-position Get the company page of a member's top position Credit cost: 5 (advanced) Parameters: - url (required): Full URL of the LinkedIn profile. ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/profile/top-position" \ --data-urlencode "url=https://www.linkedin.com/in/adamselipsky/" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/search/people/by-url Search LinkedIn people from a search-results URL Credit cost: 10 (premium) Parameters: - url (required): LinkedIn people-search results URL. ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/search/people/by-url" \ --data-urlencode "url=https://www.linkedin.com/search/results/people/?keywords=kyc" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/profile/all Get a member's entire profile, counts included, in one call (oneOf: url|urls) Credit cost: 5-50 (metered) Parameters: - url (optional, string): Full URL of the LinkedIn profile. - urls (optional, string): Up to 10 full LinkedIn profile URLs in one call, comma separated. The response becomes `{items, total}` instead of a single profile. Billed 5 credits per profile RETURNED, the same rate as a single call: duplicates are collapsed before the call and profiles that do not resolve are not charged, so a list with repeats or dead handles costs less than its length. A list where nothing resolves is a free 404. - Constraint: one of url, urls (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/profile/all" \ --data-urlencode "url=https://www.linkedin.com/in/williamhgates/" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/profile/complete Get a member's profile, experience, education, skills and recommendations Credit cost: 10 (premium) Parameters: - url (required): Full URL of the LinkedIn profile. ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/profile/complete" \ --data-urlencode "url=https://www.linkedin.com/in/ryanroslansky/" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/profile/with-posts Get a member profile with follower and connection counts and recent posts Credit cost: 10 (premium) Parameters: - url (required): Full URL of the LinkedIn profile. ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/profile/with-posts" \ --data-urlencode "url=https://www.linkedin.com/in/adamselipsky/" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/post/with-comments Get a LinkedIn post together with its comments Credit cost: 5 (advanced) Parameters: - url (required): Activity URL of the LinkedIn post. ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/post/with-comments" \ --data-urlencode "url=https://www.linkedin.com/feed/update/urn:li:activity:7501466755261820928" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/post Get LinkedIn post details Credit cost: 5 (advanced) Parameters: - url (required): Full URL of the LinkedIn post. It has to carry the post's ACTIVITY id, either `https://www.linkedin.com/feed/update/urn:li:activity:` or `https://www.linkedin.com/posts/-activity--`. A `urn:li:share:` or `urn:li:ugcPost:` URL names a different id for the same post and is rejected with a 400 before any credits are charged, because it cannot be resolved to the post. Every endpoint that lists posts publishes the activity form as `post.url`, so copy that field rather than rebuilding the URL from `post.id`, which is not always the activity id. ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/post" \ --data-urlencode "url=https://www.linkedin.com/feed/update/urn:li:activity:7501466755261820928" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/search/people Search LinkedIn people Credit cost: 10-50 (metered): 10 credits for the list. include=profile holds 4 credits per row and keeps only the rows filled from a fresh lookup: at most 50 for the 10-row page, or 10 + 4 x limit when you pass `limit`. Rows served from cache are free, unfilled rows are refunded, and a repeat of a fully joined page within the list's cache window costs 0. The list is cached for 2 minutes and each profile lookup for 15 minutes. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - query (required): Name or display-name keyword to search for (e.g. 'Bill Gates'). - page (optional, integer): Page number for pagination (default 1). - first_name (optional, string): Filter by first name. - last_name (optional, string): Filter by last name. - title (optional, string): Filter by job title or headline. - current_company (optional, string): Filter by current company, using a numeric company id from /v1/linkedin/company (`author.id`). Comma-separated for multiple. - past_company (optional, string): Filter by a previously-worked company, using a numeric company id from /v1/linkedin/company (`author.id`). - school (optional, string): Filter by school, using a school id from /v1/linkedin/search/schools (`id`). - industry (optional, string): Filter by industry, using an industry id from /v1/linkedin/search/industry (`industry_id`). - geocode_location (optional, string): Filter by location, using a geocode id from /v1/linkedin/search/location (`geocode`). - profile_language (optional, string): Filter by profile language (ISO 2-letter code, e.g. 'en'). - service_category (optional, string): Filter by service category, using a numeric service-category id. No endpoint resolves these, so pass one you already hold. - follower_of (optional, string): Return people who follow a specific member, using the member's URN from /v1/linkedin/profile (`author.ext.urn`). This one is a URN and not a numeric id. - include (optional, enum: profile): Set to `profile` (one token only) to join every row to the member's profile lookup in this one call: `author.followers` becomes the exact count and `author.ext.followers_approximate` reads false on every row the lookup filled, `author.following` is filled with the member's connection count, and the location, joined date, country, website, cover image and profile flags land where the row lacks them. Holds 4 credits per row and keeps only the rows filled from a fresh lookup; rows already in cache and rows that could not be filled are refunded, so a call costs at most 50 (10 + 4 per row) and the usual bill is lower. Adds about 1 second for 10 rows, up to 3 on a slow second and never more than 8, and nothing when the members are already cached; read `data.hydration` for the rows, the credits held and kept, and the time. - limit (optional, integer): Take the top N rows of this page (1 to 10). With `include=profile` it caps both the rows joined and the extra credits: `limit=3&include=profile` costs at most 22. The page cursor still advances past the full page of 10, so the rows past your limit are skipped, not carried to the next page. Without `include=profile` the call stays 10 credits. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/search/people" \ --data-urlencode "query=Bill Gates" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/company/people List people at a LinkedIn company Credit cost: 10-50 (metered): 10 credits for the list. include=profile holds 4 credits per row and keeps only the rows filled from a fresh lookup: at most 50 for the 10-row page, or 10 + 4 x limit when you pass `limit`. Rows served from cache are free, unfilled rows are refunded, and a repeat of a fully joined page within the list's cache window costs 0. The list is cached for 15 minutes and each profile lookup for 15 minutes. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - company_id (required): LinkedIn numeric company ID (from /v1/linkedin/company). - page (optional, integer): Page number for pagination (default 1). - include (optional, enum: profile): Set to `profile` (one token only) to join every row to the member's profile lookup in this one call: `author.followers` becomes the exact count and `author.ext.followers_approximate` reads false on every row the lookup filled, `author.following` is filled with the member's connection count, and the location, joined date, country, website, cover image and profile flags land where the row lacks them. Holds 4 credits per row and keeps only the rows filled from a fresh lookup; rows already in cache and rows that could not be filled are refunded, so a call costs at most 50 (10 + 4 per row) and the usual bill is lower. Adds about 1 second for 10 rows, up to 3 on a slow second and never more than 8, and nothing when the members are already cached; read `data.hydration` for the rows, the credits held and kept, and the time. - limit (optional, integer): Take the top N rows of this page (1 to 10). With `include=profile` it caps both the rows joined and the extra credits: `limit=3&include=profile` costs at most 22. The page cursor still advances past the full page of 10, so the rows past your limit are skipped, not carried to the next page. Without `include=profile` the call stays 10 credits. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/company/people" \ --data-urlencode "company_id=1035" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/post/comments Get LinkedIn post comments Credit cost: 5 (advanced) Parameters: - url (required): Full URL of the LinkedIn post. It has to carry the post's ACTIVITY id, either `https://www.linkedin.com/feed/update/urn:li:activity:` or `https://www.linkedin.com/posts/-activity--`. A `urn:li:share:` or `urn:li:ugcPost:` URL names a different id for the same post and is rejected with a 400 before any credits are charged, because it cannot be resolved to the post. Every endpoint that lists posts publishes the activity form as `post.url`, so copy that field rather than rebuilding the URL from `post.id`, which is not always the activity id. - page (optional, integer): Page number for pagination (default 1). - post_type (optional, enum: activity | ugc): Upstream post type: 'activity' (default) or 'ugc'. - sort_order (optional, enum: recent | relevance): Comment ordering: 'relevance' or 'recent'. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off the page, and the page that reaches one ends the walk: `next_cursor` is not returned and `pagination.stopped_at` is `since`. Sending it sets the newest-first sort shown in this endpoint's sort parameter (any other sort value is a free 400). The page still costs what a page costs, so you pay for the pages you walk and no more. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/post/comments" \ --data-urlencode "url=https://www.linkedin.com/feed/update/urn:li:activity:7244804629786419202" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/profile/posts List a LinkedIn member's posts Credit cost: 5-200 (metered): 5 credits for a call with no `limit`, or `limit` at 50 or below. Above 50 the call is metered at 2 credits per post RETURNED: it holds `limit x 2` up front (the 100 maximum holds 200) and refunds down to the posts that actually came back, never below 5, so a member with 60 posts settles at 120 credits however high `limit` was set Parameters: - url (required): Full URL of the LinkedIn profile, company, or post. - limit (optional, integer): How many posts to return in the one call, 1 to 100. Defaults to the upstream's 20. 100 is the provider's hard ceiling. Up to 50 the call is a flat 5 credits; above 50 it is metered at 2 credits per post returned (holds `limit x 2`, never below 5). Ask for the depth you need up front - there is no second page to fetch. - urn (optional, string): The member's opaque LinkedIn URN, as returned in `author.ext.urn` by /v1/linkedin/profile. Optional, and a pure optimisation: pass it ALONGSIDE `url` (which stays required) and this call skips the internal handle lookup, so it answers faster and consumes half the upstream rate-limit budget. Verified on the deployed API 05/09/2026. To address a member by URN alone, send the URN-form profile URL as `url`, for example https://www.linkedin.com/in/ACoAAA8BYqEBCGLg_vT_ca6mMEqkpp9nVffJ3hc: it resolves to the same member as the public address (verified 20/10/2026 on profile/posts and profile/comments) and holds only ASCII, which helps when the public address carries an emoji or other symbol. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/profile/posts" \ --data-urlencode "url=https://www.linkedin.com/in/williamhgates/" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/profile/reactions List posts a LinkedIn member reacted to Credit cost: 5 (advanced) Parameters: - url (required): Full URL of the LinkedIn profile, company, or post. - urn (optional, string): The member's opaque LinkedIn URN, as returned in `author.ext.urn` by /v1/linkedin/profile. Optional, and a pure optimisation: pass it ALONGSIDE `url` (which stays required) and this call skips the internal handle lookup, so it answers faster and consumes half the upstream rate-limit budget. Verified on the deployed API 05/09/2026. To address a member by URN alone, send the URN-form profile URL as `url`, for example https://www.linkedin.com/in/ACoAAA8BYqEBCGLg_vT_ca6mMEqkpp9nVffJ3hc: it resolves to the same member as the public address (verified 20/10/2026 on profile/posts and profile/comments) and holds only ASCII, which helps when the public address carries an emoji or other symbol. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/profile/reactions" \ --data-urlencode "url=https://www.linkedin.com/in/williamhgates/" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/post/reposts List reposts of a LinkedIn post Credit cost: 5 (advanced) Parameters: - url (required): Full URL of the LinkedIn post. It has to carry the post's ACTIVITY id, either `https://www.linkedin.com/feed/update/urn:li:activity:` or `https://www.linkedin.com/posts/-activity--`. A `urn:li:share:` or `urn:li:ugcPost:` URL names a different id for the same post and is rejected with a 400 before any credits are charged, because it cannot be resolved to the post. Every endpoint that lists posts publishes the activity form as `post.url`, so copy that field rather than rebuilding the URL from `post.id`, which is not always the activity id. - cursor (optional, string): LinkedIn cursor. - page (optional, integer): 1-based page number. Defaults to 1. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/post/reposts" \ --data-urlencode "url=https://www.linkedin.com/feed/update/urn:li:activity:7501466755261820928" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/group/posts List posts in a LinkedIn group Credit cost: 5 (advanced) Parameters: - group_id (required): LinkedIn numeric group ID. - page (optional, integer): 1-based page number. Defaults to 1. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/group/posts" \ --data-urlencode "group_id=62438" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/company/affiliated-pages List a company's affiliated/showcase pages Credit cost: 5 (advanced) Parameters: - company_id (required): LinkedIn numeric company ID (from /v1/linkedin/company). ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/company/affiliated-pages" \ --data-urlencode "company_id=1035" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/post/comments/replies List replies to a LinkedIn comment Credit cost: 5 (advanced) Parameters: - url (required): Full URL of the LinkedIn post. It has to carry the post's ACTIVITY id, either `https://www.linkedin.com/feed/update/urn:li:activity:` or `https://www.linkedin.com/posts/-activity--`. A `urn:li:share:` or `urn:li:ugcPost:` URL names a different id for the same post and is rejected with a 400 before any credits are charged, because it cannot be resolved to the post. Every endpoint that lists posts publishes the activity form as `post.url`, so copy that field rather than rebuilding the URL from `post.id`, which is not always the activity id. - comment_id (required): The parent comment's `comment.id` from /v1/linkedin/post/comments, or its `comment.ext.urn` (urn:li:comment:(ugcPost:,)). - cursor (optional, string): LinkedIn cursor. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/post/comments/replies" \ --data-urlencode "url=https://www.linkedin.com/feed/update/urn:li:activity:7501466755261820928" \ --data-urlencode "comment_id=7501480403283886080" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/profile/experiences List a member's work experiences Credit cost: 5 (advanced) Parameters: - url (required): Full URL of the LinkedIn profile, company, or post. - page (optional, integer): 1-based page number. Defaults to 1. - urn (optional, string): The member's opaque LinkedIn URN, as returned in `author.ext.urn` by /v1/linkedin/profile. Optional, and a pure optimisation: pass it ALONGSIDE `url` (which stays required) and this call skips the internal handle lookup, so it answers faster and consumes half the upstream rate-limit budget. Verified on the deployed API 05/09/2026. To address a member by URN alone, send the URN-form profile URL as `url`, for example https://www.linkedin.com/in/ACoAAA8BYqEBCGLg_vT_ca6mMEqkpp9nVffJ3hc: it resolves to the same member as the public address (verified 20/10/2026 on profile/posts and profile/comments) and holds only ASCII, which helps when the public address carries an emoji or other symbol. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/profile/experiences" \ --data-urlencode "url=https://www.linkedin.com/in/williamhgates/" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/profile/educations List a member's education history Credit cost: 5 (advanced) Parameters: - url (required): Full URL of the LinkedIn profile, company, or post. - page (optional, integer): 1-based page number. Defaults to 1. - urn (optional, string): The member's opaque LinkedIn URN, as returned in `author.ext.urn` by /v1/linkedin/profile. Optional, and a pure optimisation: pass it ALONGSIDE `url` (which stays required) and this call skips the internal handle lookup, so it answers faster and consumes half the upstream rate-limit budget. Verified on the deployed API 05/09/2026. To address a member by URN alone, send the URN-form profile URL as `url`, for example https://www.linkedin.com/in/ACoAAA8BYqEBCGLg_vT_ca6mMEqkpp9nVffJ3hc: it resolves to the same member as the public address (verified 20/10/2026 on profile/posts and profile/comments) and holds only ASCII, which helps when the public address carries an emoji or other symbol. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/profile/educations" \ --data-urlencode "url=https://www.linkedin.com/in/williamhgates/" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/profile/skills List a member's skills Credit cost: 5 (advanced) Parameters: - url (required): Full URL of the LinkedIn profile, company, or post. - page (optional, integer): 1-based page number. Defaults to 1. - urn (optional, string): The member's opaque LinkedIn URN, as returned in `author.ext.urn` by /v1/linkedin/profile. Optional, and a pure optimisation: pass it ALONGSIDE `url` (which stays required) and this call skips the internal handle lookup, so it answers faster and consumes half the upstream rate-limit budget. Verified on the deployed API 05/09/2026. To address a member by URN alone, send the URN-form profile URL as `url`, for example https://www.linkedin.com/in/ACoAAA8BYqEBCGLg_vT_ca6mMEqkpp9nVffJ3hc: it resolves to the same member as the public address (verified 20/10/2026 on profile/posts and profile/comments) and holds only ASCII, which helps when the public address carries an emoji or other symbol. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/profile/skills" \ --data-urlencode "url=https://www.linkedin.com/in/williamhgates/" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/profile/honors List a member's honors and awards Credit cost: 5 (advanced) Parameters: - url (required): Full URL of the LinkedIn profile, company, or post. - page (optional, integer): 1-based page number. Defaults to 1. - urn (optional, string): The member's opaque LinkedIn URN, as returned in `author.ext.urn` by /v1/linkedin/profile. Optional, and a pure optimisation: pass it ALONGSIDE `url` (which stays required) and this call skips the internal handle lookup, so it answers faster and consumes half the upstream rate-limit budget. Verified on the deployed API 05/09/2026. To address a member by URN alone, send the URN-form profile URL as `url`, for example https://www.linkedin.com/in/ACoAAA8BYqEBCGLg_vT_ca6mMEqkpp9nVffJ3hc: it resolves to the same member as the public address (verified 20/10/2026 on profile/posts and profile/comments) and holds only ASCII, which helps when the public address carries an emoji or other symbol. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/profile/honors" \ --data-urlencode "url=https://www.linkedin.com/in/williamhgates/" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/profile/certifications List a member's licenses and certifications Credit cost: 5 (advanced) Parameters: - url (required): Full URL of the LinkedIn profile, company, or post. - page (optional, integer): 1-based page number. Defaults to 1. - urn (optional, string): The member's opaque LinkedIn URN, as returned in `author.ext.urn` by /v1/linkedin/profile. Optional, and a pure optimisation: pass it ALONGSIDE `url` (which stays required) and this call skips the internal handle lookup, so it answers faster and consumes half the upstream rate-limit budget. Verified on the deployed API 05/09/2026. To address a member by URN alone, send the URN-form profile URL as `url`, for example https://www.linkedin.com/in/ACoAAA8BYqEBCGLg_vT_ca6mMEqkpp9nVffJ3hc: it resolves to the same member as the public address (verified 20/10/2026 on profile/posts and profile/comments) and holds only ASCII, which helps when the public address carries an emoji or other symbol. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/profile/certifications" \ --data-urlencode "url=https://www.linkedin.com/in/adamgrant/" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/profile/publications List a member's publications Credit cost: 5 (advanced) Parameters: - url (required): Full URL of the LinkedIn profile, company, or post. - page (optional, integer): 1-based page number. Defaults to 1. - urn (optional, string): The member's opaque LinkedIn URN, as returned in `author.ext.urn` by /v1/linkedin/profile. Optional, and a pure optimisation: pass it ALONGSIDE `url` (which stays required) and this call skips the internal handle lookup, so it answers faster and consumes half the upstream rate-limit budget. Verified on the deployed API 05/09/2026. To address a member by URN alone, send the URN-form profile URL as `url`, for example https://www.linkedin.com/in/ACoAAA8BYqEBCGLg_vT_ca6mMEqkpp9nVffJ3hc: it resolves to the same member as the public address (verified 20/10/2026 on profile/posts and profile/comments) and holds only ASCII, which helps when the public address carries an emoji or other symbol. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/profile/publications" \ --data-urlencode "url=https://www.linkedin.com/in/williamhgates/" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/profile/volunteers List a member's volunteer experiences Credit cost: 5 (advanced) Parameters: - url (required): Full URL of the LinkedIn profile, company, or post. - page (optional, integer): 1-based page number. Defaults to 1. - urn (optional, string): The member's opaque LinkedIn URN, as returned in `author.ext.urn` by /v1/linkedin/profile. Optional, and a pure optimisation: pass it ALONGSIDE `url` (which stays required) and this call skips the internal handle lookup, so it answers faster and consumes half the upstream rate-limit budget. Verified on the deployed API 05/09/2026. To address a member by URN alone, send the URN-form profile URL as `url`, for example https://www.linkedin.com/in/ACoAAA8BYqEBCGLg_vT_ca6mMEqkpp9nVffJ3hc: it resolves to the same member as the public address (verified 20/10/2026 on profile/posts and profile/comments) and holds only ASCII, which helps when the public address carries an emoji or other symbol. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/profile/volunteers" \ --data-urlencode "url=https://www.linkedin.com/in/reidhoffman/" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/profile/recommendations List recommendations for a member Credit cost: 5 (advanced) Parameters: - url (required): Full URL of the LinkedIn profile, company, or post. - page (optional, integer): 1-based page number. Defaults to 1. - type (optional, enum: received | given): Filter by kind. See the allowed values. - urn (optional, string): The member's opaque LinkedIn URN, as returned in `author.ext.urn` by /v1/linkedin/profile. Optional, and a pure optimisation: pass it ALONGSIDE `url` (which stays required) and this call skips the internal handle lookup, so it answers faster and consumes half the upstream rate-limit budget. Verified on the deployed API 05/09/2026. To address a member by URN alone, send the URN-form profile URL as `url`, for example https://www.linkedin.com/in/ACoAAA8BYqEBCGLg_vT_ca6mMEqkpp9nVffJ3hc: it resolves to the same member as the public address (verified 20/10/2026 on profile/posts and profile/comments) and holds only ASCII, which helps when the public address carries an emoji or other symbol. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/profile/recommendations" \ --data-urlencode "url=https://www.linkedin.com/in/reidhoffman/" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/profile/interests/companies List companies a member follows Credit cost: 5 (advanced) Parameters: - url (required): Full URL of the LinkedIn profile, company, or post. - page (optional, integer): 1-based page number. Defaults to 1. - urn (optional, string): The member's opaque LinkedIn URN, as returned in `author.ext.urn` by /v1/linkedin/profile. Optional, and a pure optimisation: pass it ALONGSIDE `url` (which stays required) and this call skips the internal handle lookup, so it answers faster and consumes half the upstream rate-limit budget. Verified on the deployed API 05/09/2026. To address a member by URN alone, send the URN-form profile URL as `url`, for example https://www.linkedin.com/in/ACoAAA8BYqEBCGLg_vT_ca6mMEqkpp9nVffJ3hc: it resolves to the same member as the public address (verified 20/10/2026 on profile/posts and profile/comments) and holds only ASCII, which helps when the public address carries an emoji or other symbol. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/profile/interests/companies" \ --data-urlencode "url=https://www.linkedin.com/in/williamhgates/" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/profile/interests/groups List groups a member follows Credit cost: 5 (advanced) Parameters: - url (required): Full URL of the LinkedIn profile, company, or post. - page (optional, integer): 1-based page number. Defaults to 1. - urn (optional, string): The member's opaque LinkedIn URN, as returned in `author.ext.urn` by /v1/linkedin/profile. Optional, and a pure optimisation: pass it ALONGSIDE `url` (which stays required) and this call skips the internal handle lookup, so it answers faster and consumes half the upstream rate-limit budget. Verified on the deployed API 05/09/2026. To address a member by URN alone, send the URN-form profile URL as `url`, for example https://www.linkedin.com/in/ACoAAA8BYqEBCGLg_vT_ca6mMEqkpp9nVffJ3hc: it resolves to the same member as the public address (verified 20/10/2026 on profile/posts and profile/comments) and holds only ASCII, which helps when the public address carries an emoji or other symbol. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/profile/interests/groups" \ --data-urlencode "url=https://www.linkedin.com/in/williamhgates/" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/profile/images List a member's image posts Credit cost: 5 (advanced) Parameters: - url (required): Full URL of the LinkedIn profile, company, or post. - urn (optional, string): The member's opaque LinkedIn URN, as returned in `author.ext.urn` by /v1/linkedin/profile. Optional, and a pure optimisation: pass it ALONGSIDE `url` (which stays required) and this call skips the internal handle lookup, so it answers faster and consumes half the upstream rate-limit budget. Verified on the deployed API 05/09/2026. To address a member by URN alone, send the URN-form profile URL as `url`, for example https://www.linkedin.com/in/ACoAAA8BYqEBCGLg_vT_ca6mMEqkpp9nVffJ3hc: it resolves to the same member as the public address (verified 20/10/2026 on profile/posts and profile/comments) and holds only ASCII, which helps when the public address carries an emoji or other symbol. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/profile/images" \ --data-urlencode "url=https://www.linkedin.com/in/williamhgates/" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/profile/videos List a member's video posts Credit cost: 5 (advanced) Parameters: - url (required): Full URL of the LinkedIn profile, company, or post. - urn (optional, string): The member's opaque LinkedIn URN, as returned in `author.ext.urn` by /v1/linkedin/profile. Optional, and a pure optimisation: pass it ALONGSIDE `url` (which stays required) and this call skips the internal handle lookup, so it answers faster and consumes half the upstream rate-limit budget. Verified on the deployed API 05/09/2026. To address a member by URN alone, send the URN-form profile URL as `url`, for example https://www.linkedin.com/in/ACoAAA8BYqEBCGLg_vT_ca6mMEqkpp9nVffJ3hc: it resolves to the same member as the public address (verified 20/10/2026 on profile/posts and profile/comments) and holds only ASCII, which helps when the public address carries an emoji or other symbol. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/profile/videos" \ --data-urlencode "url=https://www.linkedin.com/in/williamhgates/" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/profile/comments List a member's comments Credit cost: 5 (advanced) Parameters: - url (required): Full URL of the LinkedIn profile, company, or post. - urn (optional, string): The member's opaque LinkedIn URN, as returned in `author.ext.urn` by /v1/linkedin/profile. Optional, and a pure optimisation: pass it ALONGSIDE `url` (which stays required) and this call skips the internal handle lookup, so it answers faster and consumes half the upstream rate-limit budget. Verified on the deployed API 05/09/2026. To address a member by URN alone, send the URN-form profile URL as `url`, for example https://www.linkedin.com/in/ACoAAA8BYqEBCGLg_vT_ca6mMEqkpp9nVffJ3hc: it resolves to the same member as the public address (verified 20/10/2026 on profile/posts and profile/comments) and holds only ASCII, which helps when the public address carries an emoji or other symbol. ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/profile/comments" \ --data-urlencode "url=https://www.linkedin.com/in/williamhgates/" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/post/reactions List reactors on a LinkedIn post Credit cost: 10-50 (metered): 10 credits for the list. include=profile holds 4 credits per row and keeps only the rows filled from a fresh lookup: at most 50 for the 10-row page, or 10 + 4 x limit when you pass `limit`. Rows served from cache are free, unfilled rows are refunded, and a repeat of a fully joined page within the list's cache window costs 0. The list is cached for 5 minutes and each profile lookup for 15 minutes. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - url (required): Full URL of the LinkedIn post. It has to carry the post's ACTIVITY id, either `https://www.linkedin.com/feed/update/urn:li:activity:` or `https://www.linkedin.com/posts/-activity--`. A `urn:li:share:` or `urn:li:ugcPost:` URL names a different id for the same post and is rejected with a 400 before any credits are charged, because it cannot be resolved to the post. Every endpoint that lists posts publishes the activity form as `post.url`, so copy that field rather than rebuilding the URL from `post.id`, which is not always the activity id. - page (optional, integer): 1-based page number. Defaults to 1. - type (optional, enum: all | like | praise | empathy | appreciation | interest): Filter by kind. See the allowed values. - include (optional, enum: profile): Set to `profile` (one token only) to join every row to the member's profile lookup in this one call: `author.followers` becomes the exact count and `author.ext.followers_approximate` reads false on every row the lookup filled, `author.following` is filled with the member's connection count, and the location, joined date, country, website, cover image and profile flags land where the row lacks them. Holds 4 credits per row and keeps only the rows filled from a fresh lookup; rows already in cache and rows that could not be filled are refunded, so a call costs at most 50 (10 + 4 per row) and the usual bill is lower. Adds about 1 second for 10 rows, up to 3 on a slow second and never more than 8, and nothing when the members are already cached; read `data.hydration` for the rows, the credits held and kept, and the time. - limit (optional, integer): Take the top N rows of this page (1 to 10). With `include=profile` it caps both the rows joined and the extra credits: `limit=3&include=profile` costs at most 22. The page cursor still advances past the full page of 10, so the rows past your limit are skipped, not carried to the next page. Without `include=profile` the call stays 10 credits. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/post/reactions" \ --data-urlencode "url=https://www.linkedin.com/feed/update/urn:li:activity:7501466755261820928" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/company/jobs List a company's job postings Credit cost: 10 (premium) Parameters: - company_id (required): LinkedIn numeric company ID (from /v1/linkedin/company). - page (optional, integer): 1-based page number. Defaults to 1. - date_posted (optional, enum: anytime | past_24_hours | past_week | past_month): How recently the job was listed. One of `anytime`, `past_24_hours`, `past_week`, `past_month`. Note the job lanes use `past_24_hours` while /v1/linkedin/search/posts uses `past_24h`; they are different upstream vocabularies and are not interchangeable. - experience_level (optional, enum: internship | entry_level | associate | mid_senior | director | executive): Required seniority for the role. - job_type (optional, enum: full_time | part_time | contract | temporary | volunteer | internship | other): Contract type for the role. - remote (optional, enum: onsite | remote | hybrid): Workplace type: onsite, remote, or hybrid. - easy_apply (optional, boolean): When true, return only jobs that support LinkedIn Easy Apply. - sort_by (optional, enum: recent | relevant): Result ordering. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/company/jobs" \ --data-urlencode "company_id=1035" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/search/jobs Search LinkedIn jobs Credit cost: 10 (premium) Parameters: - query (required): Search keyword. - page (optional, integer): 1-based page number. Defaults to 1. - date_posted (optional, enum: anytime | past_24_hours | past_week | past_month): How recently the job was listed. One of `anytime`, `past_24_hours`, `past_week`, `past_month`. Note the job lanes use `past_24_hours` while /v1/linkedin/search/posts uses `past_24h`; they are different upstream vocabularies and are not interchangeable. - experience_level (optional, enum: internship | entry_level | associate | mid_senior | director | executive): Required seniority for the role. - job_type (optional, enum: full_time | part_time | contract | temporary | volunteer | internship | other): Contract type for the role. - remote (optional, enum: onsite | remote | hybrid): Workplace type: onsite, remote, or hybrid. - easy_apply (optional, boolean): When true, return only jobs that support LinkedIn Easy Apply. - sort_by (optional, enum: recent | relevant): Result ordering. - company (optional, string): Filter by company, using a numeric company id from /v1/linkedin/company (`author.id`). - geocode (optional, string): Filter by location, using a geocode id from /v1/linkedin/search/location. - industry_ids (optional, string): Comma-separated industry ids from /v1/linkedin/search/industry. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/search/jobs" \ --data-urlencode "query=marketing" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/search/location Resolve a location to a LinkedIn geocode id Credit cost: 1 (standard) Parameters: - query (required): Search keyword. ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/search/location" \ --data-urlencode "query=London" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/search/schools Search LinkedIn schools Credit cost: 1 (standard) Parameters: - query (required): Search keyword. - page (optional, integer): 1-based page number. Defaults to 1. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/search/schools" \ --data-urlencode "query=Stanford" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/search/industry Resolve an industry name to a LinkedIn industry id Credit cost: 1 (standard) Parameters: - query (required): Search keyword. ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/search/industry" \ --data-urlencode "query=Software" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/profile/about Get a member's account freshness signals (NOT the profile About section) Credit cost: 5 (advanced) Parameters: - url (required): Full URL of the LinkedIn profile, company, or post. - urn (optional, string): The member's opaque LinkedIn URN, as returned in `author.ext.urn` by /v1/linkedin/profile. Optional, and a pure optimisation: pass it ALONGSIDE `url` (which stays required) and this call skips the internal handle lookup, so it answers faster and consumes half the upstream rate-limit budget. Verified on the deployed API 05/09/2026. To address a member by URN alone, send the URN-form profile URL as `url`, for example https://www.linkedin.com/in/ACoAAA8BYqEBCGLg_vT_ca6mMEqkpp9nVffJ3hc: it resolves to the same member as the public address (verified 20/10/2026 on profile/posts and profile/comments) and holds only ASCII, which helps when the public address carries an emoji or other symbol. ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/profile/about" \ --data-urlencode "url=https://www.linkedin.com/in/williamhgates/" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/profile/contact Get a member's public contact info Credit cost: 5 (advanced) Parameters: - url (required): Full URL of the LinkedIn profile, company, or post. ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/profile/contact" \ --data-urlencode "url=https://www.linkedin.com/in/williamhgates/" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/profile/stats Get a member's follower + connection counts Credit cost: 5 (advanced) Parameters: - url (required): Full URL of the LinkedIn profile, company, or post. ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/profile/stats" \ --data-urlencode "url=https://www.linkedin.com/in/williamhgates/" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/company/job-count Get a company's open job count Credit cost: 5 (advanced) Parameters: - company_id (required): LinkedIn numeric company ID (from /v1/linkedin/company). ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/company/job-count" \ --data-urlencode "company_id=1035" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/company/insights Get aggregate insights about a company's members Credit cost: 5 (advanced) Parameters: - company_id (required): LinkedIn numeric company ID (from /v1/linkedin/company). ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/company/insights" \ --data-urlencode "company_id=1035" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/group Get LinkedIn group details Credit cost: 5 (advanced) Parameters: - group_id (required): LinkedIn numeric group ID. ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/group" \ --data-urlencode "group_id=62438" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/job Get LinkedIn job details Credit cost: 5 (advanced) Parameters: - id (required): LinkedIn job ID. - include_skills (optional, boolean): When true, include the job's required skills in `job.ext.skills`. ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/job" \ --data-urlencode "id=4462185352" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/company/posts List LinkedIn company posts Credit cost: 5 (advanced) Parameters: - company_id (required): LinkedIn numeric company ID (from /v1/linkedin/company). - page (optional, integer): Page number for pagination (default 1). - sort_by (optional, enum: top | recent): Post ordering: 'top' or 'recent'. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off the page, and the page that reaches one ends the walk: `next_cursor` is not returned and `pagination.stopped_at` is `since`. Sending it sets the newest-first sort shown in this endpoint's sort parameter (any other sort value is a free 400). The page still costs what a page costs, so you pay for the pages you walk and no more. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/company/posts" \ --data-urlencode "company_id=1035" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/ad Get LinkedIn ad details Credit cost: 5 (advanced) Parameters: - url (required): Full URL of the LinkedIn ad ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/ad" \ --data-urlencode "url=https://www.linkedin.com/ad-library/detail/1515437353" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/ads/search Search LinkedIn ads (oneOf: company|keyword|companyId) Credit cost: 5 (advanced) Parameters: - company (optional, string): The company name to search for. 'Microsoft' for example - keyword (optional, string): The keyword to search for - companyId (optional, string): The company id to search for - countries (optional, string): Comma separated list of countries. Example: US,CA,MX - startDate (optional, string): Start date to search for. Format: YYYY-MM-DD - endDate (optional, string): End date to search for. Format: YYYY-MM-DD - paginationToken (optional, string): Pagination token to paginate through results - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. - Constraint: one of company, keyword, companyId (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/ads/search" \ --data-urlencode "company=microsoft" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/profile/posts/archive Walk a LinkedIn member's COMPLETE post history, 100 posts a page, with exact publish times and share counts the other lanes cannot return. Metered: 5 credits per post returned, so try the cheaper /v1/linkedin/profile/posts first Credit cost: 5-500 (metered): 5 credits per post RETURNED. The call holds `limit x 5` up front - the default limit of 20 holds 100 and the 100 maximum holds 500 - and refunds down to the posts that actually came back, so a member with 12 posts settles at 60 credits however high `limit` was set. This is the most expensive read on the platform per row: check whether /v1/linkedin/profile/posts (5 credits up to 50 posts, 2 a post above that) already covers the window you need before walking the archive With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - url (required): The member's LinkedIn profile URL, for example https://www.linkedin.com/in/williamhgates/. - limit (optional, integer): How many posts to return, 1 to 100 (default 20). Pass 100 to walk the full archive: only a full page returns a `next_cursor`. You are charged 5 credits for each post actually returned, not for the limit you asked for. - pagination_token (optional, string): The `next_cursor` from the previous page. Omit it for the newest posts. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/profile/posts/archive" \ --data-urlencode "url=https://www.linkedin.com/in/williamhgates/" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/search/posts Search public LinkedIn posts by keyword (oneOf: query|from_member|from_company) Credit cost: 1-56 (metered): 5 credits per page. Relevance against your query (by default, or with relevance=) is free. Adding relevant_to= holds 8 extra credits and refunds down to 1 credit per started 25 rows that were newly judged: rows already judged for the same topic are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. The default labels (sponsored, intent, niche) are free. label=mention, quality, ai_style, ai_written, injection, or label=intent with offer=, holds 8 extra credits and refunds down to 1 credit per started 25 posts that were newly judged: posts already labelled are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - page (optional, integer): 1-based page number of Google results. Defaults to 1. - sort_by (optional, enum: date_posted | relevance): Result ordering: `relevance` (default) or `date_posted`. On a subject-only call (`from_member` or `from_company` with no `query`) the results are ordered by date and `relevance` is rejected, because there is no query for them to be relevant to. - date_posted (optional, enum: past_24h | past_week | past_month): Date filter based on Google-indexed results. One of `past_24h`, `past_week`, `past_month` (underscores, not hyphens). Note the JOB lanes use `past_24_hours` for the same concept; the two vocabularies are not interchangeable. - content_type (optional, enum: videos | photos | jobs | live_videos | documents | collaborative_articles): Narrow to one kind of post. - from_company (optional, string): Filter to posts from a company, by numeric LinkedIn company id (for example `1035`). A company slug or URL is rejected; read the id from `author.id` on /v1/linkedin/company. - from_member (optional, string): Filter to posts from one member, by their LinkedIn member urn — the bare `ACoAA…` value published as `author.ext.urn` by /v1/linkedin/profile. A profile URL, a public slug (`williamhgates`) or a `urn:li:fsd_profile:`-prefixed value is rejected, because the source cannot use any of them. Pass this on its own, with no `query`, to walk one member's back catalogue. - query (optional, string): Keyword or phrase to search for. Optional when `from_member` or `from_company` is set: omit it to get that subject's posts unfiltered, and note that supplying a real content word narrows the results to posts matching it. - limit (optional, integer): Return up to this many posts in one call, 1 to 200. The call reads as many pages as it needs, removes duplicates, and is charged 1 credit for every 5 posts returned (200 posts = 40 credits). Repeating the identical call inside the 2-minute cache window is free. Requires `query`. Cannot be combined with `page` or `cursor`: the result always starts at the top and `pagination.next_cursor` is null. `pagination.has_more` says whether more posts were available when the call stopped, and `walk.stopped` says why it stopped (`limit`, `exhausted`, `deadline` or `sources_unavailable`). - relevance (optional, enum: score | filter): Optional. Without this param every row already carries computed.relevance against your query, free ({ p, sense, depth, spam }: is this row about what your query means, or a different thing that shares its words?), and nothing is dropped or reordered. score asks for it explicitly and waits for every row; filter also drops the rows that are not about your query, and lists their ids in data.relevance.dropped_ids. Your query is the topic; nothing to configure. A row that could not be judged is never dropped and carries relevance: null. Pagination is unchanged, so a filtered page can hold fewer rows. Free with your query as the topic; with relevant_to it adds 1 credit per started 25 newly judged rows (rows already judged for the same topic are free, and so is a cached page). - relevance_threshold (optional, string): Optional, only with relevance. The probability (0 to 1) a row must reach to be kept by relevance=filter. Default 0.5. Lower keeps more rows, higher keeps fewer. - relevant_to (optional, string): Optional, only with relevance. Up to 200 characters describing what you mean, used as the topic instead of the query. Use it when the query is ambiguous, for example query=cleopatra with relevant_to=Cleopatra, the IGT slot game. - judgments (optional, enum: on | off): Optional, on (the default) or off. By default every row gains free SocialCrawl judgments (computed.labels, and computed.relevance on search endpoints), reported in data.labels (mode default) and data.relevance (origin default), each with a status (complete, partial or skipped) and pending: the rows still being judged when the page was sent, which carry null now and are filled on your next call or cached read. Default judgments never add credits, never change an existing field, and never drop or reorder a row. off returns the page exactly as before, with none of those keys. label=none does the same. - dry_run (optional, enum: 1): Optional. When 1, return a cost preview for this labelled or relevance-filtered request without fetching the page or judging any row. data.estimate reports rows_expected, rows_cached, label_credits_min, label_credits_max and base_credits. 0 credits charged. - fit (optional, enum: goal): Optional. When goal, keep the rows and fields needed for the goal you pass in goal= (plus any that are uncertain, and the first and last), and replace the rest with a stub. data.held_back lists the held ids and a recall id that re-reads the full page from cache at no extra charge. Without this param the page is unchanged. - goal (optional, string): Required by fit=goal. What you are trying to do, in your own words, up to 300 characters. - fit_tokens (optional, integer): Optional, only with fit=goal. Soft cap on how much of the page to keep, in tokens. Uncertain blocks and the first and last block are kept even if they exceed it. - label (optional, string): Optional CSV of SocialCrawl labels to add to every post. Without this param every page already carries sponsored, intent and niche, free; label= adds the labels you name to them (the defaults keep running). sponsored, intent and niche are free when asked for too; mention, quality, ai_style, intent with offer=, and injection add 1 credit per started 25 newly judged posts. judgments=off (or label=none) turns the default labels off. sponsored: is the post a paid or gifted promotion (p, 0 to 1), did it carry a disclosure marker such as #ad or 광고 (disclosed), is it likely paid with no marker (undisclosed), and which of the accounts it mentions does it promote (brand, or null). These are signals to review, never a finding. mention (needs brand=): is the post about that brand rather than something that shares its name (about_brand, 0 to 1), how it feels about the brand on five levels (sentiment_level 0 to 4 and sentiment_score 0 to 1, null when the post is not about the brand), is it sarcastic, which aspect it talks about (taste_or_quality, price_or_value, availability_or_delivery, health_or_safety, advertising_or_campaign, customer_service, none), and did the author buy or use it (first_hand). intent: what the author is mainly doing (label: asking_for_recommendation, comparing_options, switching_away, complaining, promoting, news_or_discussion, other, or null when unsure, with confidence), whether they read as a potential buyer rather than a seller (buyer, seller), how pressing the need is (urgency, 0 to 3), and, when you pass offer=, whether your offer would plausibly help them (fits_offer). niche: which of the 33 niches of the published taxonomy sc-niche-v1 the caption belongs to, or personal_no_niche, or other (label, confidence, taxonomy), with label null when the caption is too thin to tell or the pick is unsure. It reads the caption only, not the video. quality: how much checkable detail the caption carries (fact_density 0 to 3), whether it mainly asks for likes, replies, shares, follows or tags (engagement_bait, 0 to 1), whether it is written to provoke anger as a way to get engagement (rage_bait, 0 to 1; about the writing, never the side taken), whether it only repeats someone else's news or view (secondhand, 0 to 1), and what the post is mainly doing (post_aim: inform, opinion, sell, entertain, provoke, other). exclude=engagement_bait drops posts whose engagement_bait is 0.8 or higher. ai_style: writing patterns common in AI-generated text, judged on the text alone. It does not say who wrote the post, and a person who writes in this style is flagged too. Six patterns are judged, each a probability (0 to 1): binary_contrast ("This is not X. It's Y."), faux_insight ("Here's what nobody tells you" and throat-clearing openers), colon_reveal ("The best part: it learns."), profound_kicker (ends on an aphorism or mic-drop line), puffery ("marks a pivotal moment", "highlighting the importance of") and rhetorical_setup ("What if I told you", a question the post answers itself, "Not a X. Not a Y. A Z."). Four are counted in the text: slop_words (words and phrases such as delve, leverage, robust, game changer, at the end of the day), em_dashes, emoji_bullets (lines that start with an emoji or a symbol bullet) and one_line_paragraphs (paragraphs that are one short line). pattern_count is how many patterns are present (a judged pattern at 0.8 or higher, slop_words 1 or more, em_dashes 2 or more, emoji_bullets 2 or more, one_line_paragraphs 5 or more) and markers lists their names, comma-separated. A post with less than about eight words of text is only counted, with every judged pattern 0. The judged patterns read the first 400 and the last 150 characters of a long post, and the counts read the whole post. exclude=ai_style drops posts with 2 or more patterns. ai_written: how likely the post was written by an AI model, from SocialCrawl's own text classifier, reading the opening of the post (about 1,000 characters): p (0 to 1, calibrated), level (likely (p 0.95 or higher), possible (p 0.71 or higher) or unlikely) and model (the model name; a new name means a new scale). A post under 8 words is too short to read: p null, level too_short. It is a probability, not proof: a person can be flagged and an AI-written post can pass, and the shorter the post the less certain it is. exclude=ai_written drops likely posts. injection flags text that addresses an AI system and tries to direct it (flagged, p); it never drops or rewrites a row. A post that could not be judged carries labels: null. Posts already labelled for anyone are free, and so is a cached page. data.labels reports what was judged and billed. - exclude (optional, string): Optional CSV (engagement_bait, ai_style, ai_written). engagement_bait, only with label=quality, drops posts whose engagement_bait probability is 0.8 or higher. ai_style, only with label=ai_style, drops posts that show 2 or more of its writing patterns (pattern_count 2 or higher). ai_written, only with label=ai_written, drops posts whose level is likely (p 0.95 or higher), or whose p reaches your ai_written_threshold; a post too short to read is never dropped. Dropped posts are listed in data.labels.dropped_ids. A post that could not be judged is never dropped. - ai_written_threshold (optional, string): Optional, only with label=ai_written and exclude=ai_written. The p (0 to 1) at which exclude=ai_written drops a post. Default 0.95 (level likely). 0.71 also drops possible posts: more AI posts go, and more posts by people with them. - brand (optional, string): Required by label=mention, ignored otherwise. The brand or product the posts are judged against, up to 300 characters, for example brand=Buldak. Without it label=mention is skipped with the warning label_mention_needs_brand and is not billed. - brand_description (optional, string): Optional, with label=mention. One plain sentence saying what the brand is, for example brand_description=Samyang's spicy instant noodle brand. Use it when the name is also an ordinary word or another company's name. - offer (optional, string): Optional, with label=intent. One or two plain sentences saying what you sell, up to 300 characters, for example offer=A web design agency that builds websites for small businesses. Adds fits_offer to every post; without it fits_offer is null. - label_evidence (optional, enum: 1): Optional, only with label=. When 1, every labelled row also carries computed.labels_evidence. = { quote, sentence_index }: the sentence in the row that most clearly shows the label, copied verbatim. Absent or null when no single sentence shows it. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. - Constraint: one of query, from_member, from_company (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/search/posts" \ --data-urlencode "query=ai agents" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/post/transcript Get a LinkedIn post video transcript Credit cost: 10 (premium) Parameters: - url (required): Full URL of the LinkedIn post to transcribe. ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/post/transcript" \ --data-urlencode "url=https://www.linkedin.com/posts/gemini-35-flash-is-a-step-forward-for-google-ugcPost-7465082215316525056-MHBd/" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /linkedin/profile/full LinkedIn company profile, recent posts, and computed analytics in one call. Company pages only; a member's profile is `/v1/linkedin/profile/all`. (oneOf: url) Credit cost: 5 (override; tier standard) Parameters: - url (optional, string): Full LinkedIn company profile or page URL. One of the identity params is required. - posts (optional, integer): How many of the fetched recent posts to return and compute the metrics over (1-100, default 25). One call reads one page, which on LinkedIn company is 10 posts, so a larger value returns that page and a `_warnings` note; pass posts_cursor back as cursor for the next page. - cursor (optional, string): Pass a prior response's posts_cursor to read the next page of posts. Each call is a separate 5-credit call whose metrics cover that page only. - include (optional, string): CSV subset of posts,computed (default both). include=computed omits the raw posts[] array from the response to save payload. The metrics are computed from the same fetched posts either way, so they do not change. - Constraint: one of url (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/linkedin/profile/full" \ --data-urlencode "url=https://www.linkedin.com/company/microsoft" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## Linkme ### GET /linkme/page Get Linkme profile Credit cost: 1 (standard) Parameters: - url (required): Full URL of the Linkme page ``` curl -G "https://www.socialcrawl.dev/v1/linkme/page" \ --data-urlencode "url=https://link.me/example" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## Linktree ### GET /linktree/page Get Linktree page Credit cost: 1 (standard) Parameters: - url (required): Full URL of the Linktree page ``` curl -G "https://www.socialcrawl.dev/v1/linktree/page" \ --data-urlencode "url=https://linktr.ee/charlidamelio" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## Naver ### GET /naver/blog/search Search Naver Blog Credit cost: 1-9 (metered): 1 credit per page. Relevance against your query (by default, or with relevance=) is free. Adding relevant_to= holds 4 extra credits and refunds down to 1 credit per started 25 rows that were newly judged: rows already judged for the same topic are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. The default labels (sponsored, intent, niche) are free. label=mention, quality, ai_style, ai_written, injection, or label=intent with offer=, holds 4 extra credits and refunds down to 1 credit per started 25 posts that were newly judged: posts already labelled are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - query (required): Free-text search term (UTF-8). Required. Search short noun phrases (for example `다한증` or `다한증 수술`), which match best: Naver does not treat quotes as an exact phrase. Add `relevance=filter` to drop the rows that are not about the query. - display (optional, integer): Number of items to return per page. Defaults to 10, cap 100; `display=101` returns 400. - start (optional, integer): 1-indexed offset for pagination. Defaults to 1, cap 1000; `start=1001` is a 400 that costs nothing, and the page that reaches rank 1000 is the last one (`has_more: false`). Past rank 100, `sim` and `point` pages repeat Naver's top 100: those rows are removed and counted in `_warnings` (`recycled_rows`), and an emptied page is free. Page deeper with `sort=date`. - sort (optional, enum: sim | date): Sort order. Accepted values: sim|date. Defaults to `sim` (relevance) when the corpus supports sort. Values outside this corpus's domain are rejected with a 400 before any upstream call. `date` lists the newest first, but Naver serves only the first 1,000 rows of a query (`start` caps at 1000), so on a common word it reaches back hours or days, not months, and on a multi-word query Naver also loosens the match. To cover a longer window, search narrower terms so the newest 1,000 rows span it, and add `since=`: rows dated before it are left off each page. - relevance (optional, enum: score | filter): Optional. Without this param every row already carries computed.relevance against your query, free ({ p, sense, depth, spam }: is this row about what your query means, or a different thing that shares its words?), and nothing is dropped or reordered. score asks for it explicitly and waits for every row; filter also drops the rows that are not about your query, and lists their ids in data.relevance.dropped_ids. Your query is the topic; nothing to configure. A row that could not be judged is never dropped and carries relevance: null. Pagination is unchanged, so a filtered page can hold fewer rows. Free with your query as the topic; with relevant_to it adds 1 credit per started 25 newly judged rows (rows already judged for the same topic are free, and so is a cached page). - relevance_threshold (optional, string): Optional, only with relevance. The probability (0 to 1) a row must reach to be kept by relevance=filter. Default 0.5. Lower keeps more rows, higher keeps fewer. - relevant_to (optional, string): Optional, only with relevance. Up to 200 characters describing what you mean, used as the topic instead of the query. Use it when the query is ambiguous, for example query=cleopatra with relevant_to=Cleopatra, the IGT slot game. - judgments (optional, enum: on | off): Optional, on (the default) or off. By default every row gains free SocialCrawl judgments (computed.labels, and computed.relevance on search endpoints), reported in data.labels (mode default) and data.relevance (origin default), each with a status (complete, partial or skipped) and pending: the rows still being judged when the page was sent, which carry null now and are filled on your next call or cached read. Default judgments never add credits, never change an existing field, and never drop or reorder a row. off returns the page exactly as before, with none of those keys. label=none does the same. - dry_run (optional, enum: 1): Optional. When 1, return a cost preview for this labelled or relevance-filtered request without fetching the page or judging any row. data.estimate reports rows_expected, rows_cached, label_credits_min, label_credits_max and base_credits. 0 credits charged. - fit (optional, enum: goal): Optional. When goal, keep the rows and fields needed for the goal you pass in goal= (plus any that are uncertain, and the first and last), and replace the rest with a stub. data.held_back lists the held ids and a recall id that re-reads the full page from cache at no extra charge. Without this param the page is unchanged. - goal (optional, string): Required by fit=goal. What you are trying to do, in your own words, up to 300 characters. - fit_tokens (optional, integer): Optional, only with fit=goal. Soft cap on how much of the page to keep, in tokens. Uncertain blocks and the first and last block are kept even if they exceed it. - label (optional, string): Optional CSV of SocialCrawl labels to add to every post. Without this param every page already carries sponsored, intent and niche, free; label= adds the labels you name to them (the defaults keep running). sponsored, intent and niche are free when asked for too; mention, quality, ai_style, intent with offer=, and injection add 1 credit per started 25 newly judged posts. judgments=off (or label=none) turns the default labels off. sponsored: is the post a paid or gifted promotion (p, 0 to 1), did it carry a disclosure marker such as #ad or 광고 (disclosed), is it likely paid with no marker (undisclosed), and which of the accounts it mentions does it promote (brand, or null). These are signals to review, never a finding. mention (needs brand=): is the post about that brand rather than something that shares its name (about_brand, 0 to 1), how it feels about the brand on five levels (sentiment_level 0 to 4 and sentiment_score 0 to 1, null when the post is not about the brand), is it sarcastic, which aspect it talks about (taste_or_quality, price_or_value, availability_or_delivery, health_or_safety, advertising_or_campaign, customer_service, none), and did the author buy or use it (first_hand). intent: what the author is mainly doing (label: asking_for_recommendation, comparing_options, switching_away, complaining, promoting, news_or_discussion, other, or null when unsure, with confidence), whether they read as a potential buyer rather than a seller (buyer, seller), how pressing the need is (urgency, 0 to 3), and, when you pass offer=, whether your offer would plausibly help them (fits_offer). niche: which of the 33 niches of the published taxonomy sc-niche-v1 the caption belongs to, or personal_no_niche, or other (label, confidence, taxonomy), with label null when the caption is too thin to tell or the pick is unsure. It reads the caption only, not the video. quality: how much checkable detail the caption carries (fact_density 0 to 3), whether it mainly asks for likes, replies, shares, follows or tags (engagement_bait, 0 to 1), whether it is written to provoke anger as a way to get engagement (rage_bait, 0 to 1; about the writing, never the side taken), whether it only repeats someone else's news or view (secondhand, 0 to 1), and what the post is mainly doing (post_aim: inform, opinion, sell, entertain, provoke, other). exclude=engagement_bait drops posts whose engagement_bait is 0.8 or higher. ai_style: writing patterns common in AI-generated text, judged on the text alone. It does not say who wrote the post, and a person who writes in this style is flagged too. Six patterns are judged, each a probability (0 to 1): binary_contrast ("This is not X. It's Y."), faux_insight ("Here's what nobody tells you" and throat-clearing openers), colon_reveal ("The best part: it learns."), profound_kicker (ends on an aphorism or mic-drop line), puffery ("marks a pivotal moment", "highlighting the importance of") and rhetorical_setup ("What if I told you", a question the post answers itself, "Not a X. Not a Y. A Z."). Four are counted in the text: slop_words (words and phrases such as delve, leverage, robust, game changer, at the end of the day), em_dashes, emoji_bullets (lines that start with an emoji or a symbol bullet) and one_line_paragraphs (paragraphs that are one short line). pattern_count is how many patterns are present (a judged pattern at 0.8 or higher, slop_words 1 or more, em_dashes 2 or more, emoji_bullets 2 or more, one_line_paragraphs 5 or more) and markers lists their names, comma-separated. A post with less than about eight words of text is only counted, with every judged pattern 0. The judged patterns read the first 400 and the last 150 characters of a long post, and the counts read the whole post. exclude=ai_style drops posts with 2 or more patterns. ai_written: how likely the post was written by an AI model, from SocialCrawl's own text classifier, reading the opening of the post (about 1,000 characters): p (0 to 1, calibrated), level (likely (p 0.95 or higher), possible (p 0.71 or higher) or unlikely) and model (the model name; a new name means a new scale). A post under 8 words is too short to read: p null, level too_short. It is a probability, not proof: a person can be flagged and an AI-written post can pass, and the shorter the post the less certain it is. exclude=ai_written drops likely posts. injection flags text that addresses an AI system and tries to direct it (flagged, p); it never drops or rewrites a row. A post that could not be judged carries labels: null. Posts already labelled for anyone are free, and so is a cached page. data.labels reports what was judged and billed. - exclude (optional, string): Optional CSV (engagement_bait, ai_style, ai_written). engagement_bait, only with label=quality, drops posts whose engagement_bait probability is 0.8 or higher. ai_style, only with label=ai_style, drops posts that show 2 or more of its writing patterns (pattern_count 2 or higher). ai_written, only with label=ai_written, drops posts whose level is likely (p 0.95 or higher), or whose p reaches your ai_written_threshold; a post too short to read is never dropped. Dropped posts are listed in data.labels.dropped_ids. A post that could not be judged is never dropped. - ai_written_threshold (optional, string): Optional, only with label=ai_written and exclude=ai_written. The p (0 to 1) at which exclude=ai_written drops a post. Default 0.95 (level likely). 0.71 also drops possible posts: more AI posts go, and more posts by people with them. - brand (optional, string): Required by label=mention, ignored otherwise. The brand or product the posts are judged against, up to 300 characters, for example brand=Buldak. Without it label=mention is skipped with the warning label_mention_needs_brand and is not billed. - brand_description (optional, string): Optional, with label=mention. One plain sentence saying what the brand is, for example brand_description=Samyang's spicy instant noodle brand. Use it when the name is also an ordinary word or another company's name. - offer (optional, string): Optional, with label=intent. One or two plain sentences saying what you sell, up to 300 characters, for example offer=A web design agency that builds websites for small businesses. Adds fits_offer to every post; without it fits_offer is null. - label_evidence (optional, enum: 1): Optional, only with label=. When 1, every labelled row also carries computed.labels_evidence. = { quote, sentence_index }: the sentence in the row that most clearly shows the label, copied verbatim. Absent or null when no single sentence shows it. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/naver/blog/search" \ --data-urlencode "query=소셜크롤" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /naver/news/search Search Naver News Credit cost: 1-9 (metered): 1 credit per page. Relevance against your query (by default, or with relevance=) is free. Adding relevant_to= holds 4 extra credits and refunds down to 1 credit per started 25 rows that were newly judged: rows already judged for the same topic are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. The default labels (sponsored, intent, niche) are free. label=mention, quality, ai_style, ai_written, injection, or label=intent with offer=, holds 4 extra credits and refunds down to 1 credit per started 25 posts that were newly judged: posts already labelled are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - query (required): Free-text search term (UTF-8). Required. Search short noun phrases (for example `다한증` or `다한증 수술`), which match best: Naver does not treat quotes as an exact phrase. Add `relevance=filter` to drop the rows that are not about the query. - display (optional, integer): Number of items to return per page. Defaults to 10, cap 100; `display=101` returns 400. - start (optional, integer): 1-indexed offset for pagination. Defaults to 1, cap 1000; `start=1001` is a 400 that costs nothing, and the page that reaches rank 1000 is the last one (`has_more: false`). Past rank 100, `sim` and `point` pages repeat Naver's top 100: those rows are removed and counted in `_warnings` (`recycled_rows`), and an emptied page is free. Page deeper with `sort=date`. - sort (optional, enum: sim | date): Sort order. Accepted values: sim|date. Defaults to `sim` (relevance) when the corpus supports sort. Values outside this corpus's domain are rejected with a 400 before any upstream call. `date` lists the newest first, but Naver serves only the first 1,000 rows of a query (`start` caps at 1000), so on a common word it reaches back hours or days, not months, and on a multi-word query Naver also loosens the match. `since=` sets `sort=date` and ends the walk at the date, so narrow the query to reach further back. - relevance (optional, enum: score | filter): Optional. Without this param every row already carries computed.relevance against your query, free ({ p, sense, depth, spam }: is this row about what your query means, or a different thing that shares its words?), and nothing is dropped or reordered. score asks for it explicitly and waits for every row; filter also drops the rows that are not about your query, and lists their ids in data.relevance.dropped_ids. Your query is the topic; nothing to configure. A row that could not be judged is never dropped and carries relevance: null. Pagination is unchanged, so a filtered page can hold fewer rows. Free with your query as the topic; with relevant_to it adds 1 credit per started 25 newly judged rows (rows already judged for the same topic are free, and so is a cached page). - relevance_threshold (optional, string): Optional, only with relevance. The probability (0 to 1) a row must reach to be kept by relevance=filter. Default 0.5. Lower keeps more rows, higher keeps fewer. - relevant_to (optional, string): Optional, only with relevance. Up to 200 characters describing what you mean, used as the topic instead of the query. Use it when the query is ambiguous, for example query=cleopatra with relevant_to=Cleopatra, the IGT slot game. - judgments (optional, enum: on | off): Optional, on (the default) or off. By default every row gains free SocialCrawl judgments (computed.labels, and computed.relevance on search endpoints), reported in data.labels (mode default) and data.relevance (origin default), each with a status (complete, partial or skipped) and pending: the rows still being judged when the page was sent, which carry null now and are filled on your next call or cached read. Default judgments never add credits, never change an existing field, and never drop or reorder a row. off returns the page exactly as before, with none of those keys. label=none does the same. - dry_run (optional, enum: 1): Optional. When 1, return a cost preview for this labelled or relevance-filtered request without fetching the page or judging any row. data.estimate reports rows_expected, rows_cached, label_credits_min, label_credits_max and base_credits. 0 credits charged. - fit (optional, enum: goal): Optional. When goal, keep the rows and fields needed for the goal you pass in goal= (plus any that are uncertain, and the first and last), and replace the rest with a stub. data.held_back lists the held ids and a recall id that re-reads the full page from cache at no extra charge. Without this param the page is unchanged. - goal (optional, string): Required by fit=goal. What you are trying to do, in your own words, up to 300 characters. - fit_tokens (optional, integer): Optional, only with fit=goal. Soft cap on how much of the page to keep, in tokens. Uncertain blocks and the first and last block are kept even if they exceed it. - label (optional, string): Optional CSV of SocialCrawl labels to add to every post. Without this param every page already carries sponsored, intent and niche, free; label= adds the labels you name to them (the defaults keep running). sponsored, intent and niche are free when asked for too; mention, quality, ai_style, intent with offer=, and injection add 1 credit per started 25 newly judged posts. judgments=off (or label=none) turns the default labels off. sponsored: is the post a paid or gifted promotion (p, 0 to 1), did it carry a disclosure marker such as #ad or 광고 (disclosed), is it likely paid with no marker (undisclosed), and which of the accounts it mentions does it promote (brand, or null). These are signals to review, never a finding. mention (needs brand=): is the post about that brand rather than something that shares its name (about_brand, 0 to 1), how it feels about the brand on five levels (sentiment_level 0 to 4 and sentiment_score 0 to 1, null when the post is not about the brand), is it sarcastic, which aspect it talks about (taste_or_quality, price_or_value, availability_or_delivery, health_or_safety, advertising_or_campaign, customer_service, none), and did the author buy or use it (first_hand). intent: what the author is mainly doing (label: asking_for_recommendation, comparing_options, switching_away, complaining, promoting, news_or_discussion, other, or null when unsure, with confidence), whether they read as a potential buyer rather than a seller (buyer, seller), how pressing the need is (urgency, 0 to 3), and, when you pass offer=, whether your offer would plausibly help them (fits_offer). niche: which of the 33 niches of the published taxonomy sc-niche-v1 the caption belongs to, or personal_no_niche, or other (label, confidence, taxonomy), with label null when the caption is too thin to tell or the pick is unsure. It reads the caption only, not the video. quality: how much checkable detail the caption carries (fact_density 0 to 3), whether it mainly asks for likes, replies, shares, follows or tags (engagement_bait, 0 to 1), whether it is written to provoke anger as a way to get engagement (rage_bait, 0 to 1; about the writing, never the side taken), whether it only repeats someone else's news or view (secondhand, 0 to 1), and what the post is mainly doing (post_aim: inform, opinion, sell, entertain, provoke, other). exclude=engagement_bait drops posts whose engagement_bait is 0.8 or higher. ai_style: writing patterns common in AI-generated text, judged on the text alone. It does not say who wrote the post, and a person who writes in this style is flagged too. Six patterns are judged, each a probability (0 to 1): binary_contrast ("This is not X. It's Y."), faux_insight ("Here's what nobody tells you" and throat-clearing openers), colon_reveal ("The best part: it learns."), profound_kicker (ends on an aphorism or mic-drop line), puffery ("marks a pivotal moment", "highlighting the importance of") and rhetorical_setup ("What if I told you", a question the post answers itself, "Not a X. Not a Y. A Z."). Four are counted in the text: slop_words (words and phrases such as delve, leverage, robust, game changer, at the end of the day), em_dashes, emoji_bullets (lines that start with an emoji or a symbol bullet) and one_line_paragraphs (paragraphs that are one short line). pattern_count is how many patterns are present (a judged pattern at 0.8 or higher, slop_words 1 or more, em_dashes 2 or more, emoji_bullets 2 or more, one_line_paragraphs 5 or more) and markers lists their names, comma-separated. A post with less than about eight words of text is only counted, with every judged pattern 0. The judged patterns read the first 400 and the last 150 characters of a long post, and the counts read the whole post. exclude=ai_style drops posts with 2 or more patterns. ai_written: how likely the post was written by an AI model, from SocialCrawl's own text classifier, reading the opening of the post (about 1,000 characters): p (0 to 1, calibrated), level (likely (p 0.95 or higher), possible (p 0.71 or higher) or unlikely) and model (the model name; a new name means a new scale). A post under 8 words is too short to read: p null, level too_short. It is a probability, not proof: a person can be flagged and an AI-written post can pass, and the shorter the post the less certain it is. exclude=ai_written drops likely posts. injection flags text that addresses an AI system and tries to direct it (flagged, p); it never drops or rewrites a row. A post that could not be judged carries labels: null. Posts already labelled for anyone are free, and so is a cached page. data.labels reports what was judged and billed. - exclude (optional, string): Optional CSV (engagement_bait, ai_style, ai_written). engagement_bait, only with label=quality, drops posts whose engagement_bait probability is 0.8 or higher. ai_style, only with label=ai_style, drops posts that show 2 or more of its writing patterns (pattern_count 2 or higher). ai_written, only with label=ai_written, drops posts whose level is likely (p 0.95 or higher), or whose p reaches your ai_written_threshold; a post too short to read is never dropped. Dropped posts are listed in data.labels.dropped_ids. A post that could not be judged is never dropped. - ai_written_threshold (optional, string): Optional, only with label=ai_written and exclude=ai_written. The p (0 to 1) at which exclude=ai_written drops a post. Default 0.95 (level likely). 0.71 also drops possible posts: more AI posts go, and more posts by people with them. - brand (optional, string): Required by label=mention, ignored otherwise. The brand or product the posts are judged against, up to 300 characters, for example brand=Buldak. Without it label=mention is skipped with the warning label_mention_needs_brand and is not billed. - brand_description (optional, string): Optional, with label=mention. One plain sentence saying what the brand is, for example brand_description=Samyang's spicy instant noodle brand. Use it when the name is also an ordinary word or another company's name. - offer (optional, string): Optional, with label=intent. One or two plain sentences saying what you sell, up to 300 characters, for example offer=A web design agency that builds websites for small businesses. Adds fits_offer to every post; without it fits_offer is null. - label_evidence (optional, enum: 1): Optional, only with label=. When 1, every labelled row also carries computed.labels_evidence. = { quote, sentence_index }: the sentence in the row that most clearly shows the label, copied verbatim. Absent or null when no single sentence shows it. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off the page, and the page that reaches one ends the walk: `next_cursor` is not returned and `pagination.stopped_at` is `since`. Sending it sets the newest-first sort shown in this endpoint's sort parameter (any other sort value is a free 400). The page still costs what a page costs, so you pay for the pages you walk and no more. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/naver/news/search" \ --data-urlencode "query=삼성전자" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /naver/encyc/search Search Naver Encyclopedia Credit cost: 1 (standard) Parameters: - query (required): Free-text search term (UTF-8). Required. - display (optional, integer): Number of items to return per page. Defaults to 10, cap 100; `display=101` returns 400. - start (optional, integer): 1-indexed offset for pagination. Defaults to 1, cap 1000; `start=1001` is a 400 that costs nothing, and the page that reaches rank 1000 is the last one (`has_more: false`). Past rank 100, `sim` and `point` pages repeat Naver's top 100: those rows are removed and counted in `_warnings` (`recycled_rows`), and an emptied page is free. This corpus ignores `sort`, so `sort=date` does not avoid the repeats. - sort (optional, string): Sort order. Accepted values: (sort ignored). Defaults to `sim` (relevance) when the corpus supports sort. Values outside this corpus's domain are rejected with a 400 before any upstream call. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/naver/encyc/search" \ --data-urlencode "query=양자역학" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /naver/cafearticle/search Search Naver Cafe articles Credit cost: 1-9 (metered): 1 credit per page. Relevance against your query (by default, or with relevance=) is free. Adding relevant_to= holds 4 extra credits and refunds down to 1 credit per started 25 rows that were newly judged: rows already judged for the same topic are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. The default labels (sponsored, intent, niche) are free. label=mention, quality, ai_style, ai_written, injection, or label=intent with offer=, holds 4 extra credits and refunds down to 1 credit per started 25 posts that were newly judged: posts already labelled are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - query (required): Free-text search term (UTF-8). Required. Search short noun phrases (for example `다한증` or `다한증 수술`), which match best: Naver does not treat quotes as an exact phrase. Add `relevance=filter` to drop the rows that are not about the query. - display (optional, integer): Number of items to return per page. Defaults to 10, cap 100; `display=101` returns 400. - start (optional, integer): 1-indexed offset for pagination. Defaults to 1, cap 1000; `start=1001` is a 400 that costs nothing, and the page that reaches rank 1000 is the last one (`has_more: false`). Past rank 100, `sim` and `point` pages repeat Naver's top 100: those rows are removed and counted in `_warnings` (`recycled_rows`), and an emptied page is free. Page deeper with `sort=date`. - sort (optional, enum: sim | date): Sort order. Accepted values: sim|date. Defaults to `sim` (relevance) when the corpus supports sort. Values outside this corpus's domain are rejected with a 400 before any upstream call. `date` lists the newest first, but Naver serves only the first 1,000 rows of a query (`start` caps at 1000), so on a common word it reaches back hours or days, not months, and on a multi-word query Naver also loosens the match. Cafe rows carry no date, so `since=` cannot narrow them. - relevance (optional, enum: score | filter): Optional. Without this param every row already carries computed.relevance against your query, free ({ p, sense, depth, spam }: is this row about what your query means, or a different thing that shares its words?), and nothing is dropped or reordered. score asks for it explicitly and waits for every row; filter also drops the rows that are not about your query, and lists their ids in data.relevance.dropped_ids. Your query is the topic; nothing to configure. A row that could not be judged is never dropped and carries relevance: null. Pagination is unchanged, so a filtered page can hold fewer rows. Free with your query as the topic; with relevant_to it adds 1 credit per started 25 newly judged rows (rows already judged for the same topic are free, and so is a cached page). - relevance_threshold (optional, string): Optional, only with relevance. The probability (0 to 1) a row must reach to be kept by relevance=filter. Default 0.5. Lower keeps more rows, higher keeps fewer. - relevant_to (optional, string): Optional, only with relevance. Up to 200 characters describing what you mean, used as the topic instead of the query. Use it when the query is ambiguous, for example query=cleopatra with relevant_to=Cleopatra, the IGT slot game. - judgments (optional, enum: on | off): Optional, on (the default) or off. By default every row gains free SocialCrawl judgments (computed.labels, and computed.relevance on search endpoints), reported in data.labels (mode default) and data.relevance (origin default), each with a status (complete, partial or skipped) and pending: the rows still being judged when the page was sent, which carry null now and are filled on your next call or cached read. Default judgments never add credits, never change an existing field, and never drop or reorder a row. off returns the page exactly as before, with none of those keys. label=none does the same. - dry_run (optional, enum: 1): Optional. When 1, return a cost preview for this labelled or relevance-filtered request without fetching the page or judging any row. data.estimate reports rows_expected, rows_cached, label_credits_min, label_credits_max and base_credits. 0 credits charged. - fit (optional, enum: goal): Optional. When goal, keep the rows and fields needed for the goal you pass in goal= (plus any that are uncertain, and the first and last), and replace the rest with a stub. data.held_back lists the held ids and a recall id that re-reads the full page from cache at no extra charge. Without this param the page is unchanged. - goal (optional, string): Required by fit=goal. What you are trying to do, in your own words, up to 300 characters. - fit_tokens (optional, integer): Optional, only with fit=goal. Soft cap on how much of the page to keep, in tokens. Uncertain blocks and the first and last block are kept even if they exceed it. - label (optional, string): Optional CSV of SocialCrawl labels to add to every post. Without this param every page already carries sponsored, intent and niche, free; label= adds the labels you name to them (the defaults keep running). sponsored, intent and niche are free when asked for too; mention, quality, ai_style, intent with offer=, and injection add 1 credit per started 25 newly judged posts. judgments=off (or label=none) turns the default labels off. sponsored: is the post a paid or gifted promotion (p, 0 to 1), did it carry a disclosure marker such as #ad or 광고 (disclosed), is it likely paid with no marker (undisclosed), and which of the accounts it mentions does it promote (brand, or null). These are signals to review, never a finding. mention (needs brand=): is the post about that brand rather than something that shares its name (about_brand, 0 to 1), how it feels about the brand on five levels (sentiment_level 0 to 4 and sentiment_score 0 to 1, null when the post is not about the brand), is it sarcastic, which aspect it talks about (taste_or_quality, price_or_value, availability_or_delivery, health_or_safety, advertising_or_campaign, customer_service, none), and did the author buy or use it (first_hand). intent: what the author is mainly doing (label: asking_for_recommendation, comparing_options, switching_away, complaining, promoting, news_or_discussion, other, or null when unsure, with confidence), whether they read as a potential buyer rather than a seller (buyer, seller), how pressing the need is (urgency, 0 to 3), and, when you pass offer=, whether your offer would plausibly help them (fits_offer). niche: which of the 33 niches of the published taxonomy sc-niche-v1 the caption belongs to, or personal_no_niche, or other (label, confidence, taxonomy), with label null when the caption is too thin to tell or the pick is unsure. It reads the caption only, not the video. quality: how much checkable detail the caption carries (fact_density 0 to 3), whether it mainly asks for likes, replies, shares, follows or tags (engagement_bait, 0 to 1), whether it is written to provoke anger as a way to get engagement (rage_bait, 0 to 1; about the writing, never the side taken), whether it only repeats someone else's news or view (secondhand, 0 to 1), and what the post is mainly doing (post_aim: inform, opinion, sell, entertain, provoke, other). exclude=engagement_bait drops posts whose engagement_bait is 0.8 or higher. ai_style: writing patterns common in AI-generated text, judged on the text alone. It does not say who wrote the post, and a person who writes in this style is flagged too. Six patterns are judged, each a probability (0 to 1): binary_contrast ("This is not X. It's Y."), faux_insight ("Here's what nobody tells you" and throat-clearing openers), colon_reveal ("The best part: it learns."), profound_kicker (ends on an aphorism or mic-drop line), puffery ("marks a pivotal moment", "highlighting the importance of") and rhetorical_setup ("What if I told you", a question the post answers itself, "Not a X. Not a Y. A Z."). Four are counted in the text: slop_words (words and phrases such as delve, leverage, robust, game changer, at the end of the day), em_dashes, emoji_bullets (lines that start with an emoji or a symbol bullet) and one_line_paragraphs (paragraphs that are one short line). pattern_count is how many patterns are present (a judged pattern at 0.8 or higher, slop_words 1 or more, em_dashes 2 or more, emoji_bullets 2 or more, one_line_paragraphs 5 or more) and markers lists their names, comma-separated. A post with less than about eight words of text is only counted, with every judged pattern 0. The judged patterns read the first 400 and the last 150 characters of a long post, and the counts read the whole post. exclude=ai_style drops posts with 2 or more patterns. ai_written: how likely the post was written by an AI model, from SocialCrawl's own text classifier, reading the opening of the post (about 1,000 characters): p (0 to 1, calibrated), level (likely (p 0.95 or higher), possible (p 0.71 or higher) or unlikely) and model (the model name; a new name means a new scale). A post under 8 words is too short to read: p null, level too_short. It is a probability, not proof: a person can be flagged and an AI-written post can pass, and the shorter the post the less certain it is. exclude=ai_written drops likely posts. injection flags text that addresses an AI system and tries to direct it (flagged, p); it never drops or rewrites a row. A post that could not be judged carries labels: null. Posts already labelled for anyone are free, and so is a cached page. data.labels reports what was judged and billed. - exclude (optional, string): Optional CSV (engagement_bait, ai_style, ai_written). engagement_bait, only with label=quality, drops posts whose engagement_bait probability is 0.8 or higher. ai_style, only with label=ai_style, drops posts that show 2 or more of its writing patterns (pattern_count 2 or higher). ai_written, only with label=ai_written, drops posts whose level is likely (p 0.95 or higher), or whose p reaches your ai_written_threshold; a post too short to read is never dropped. Dropped posts are listed in data.labels.dropped_ids. A post that could not be judged is never dropped. - ai_written_threshold (optional, string): Optional, only with label=ai_written and exclude=ai_written. The p (0 to 1) at which exclude=ai_written drops a post. Default 0.95 (level likely). 0.71 also drops possible posts: more AI posts go, and more posts by people with them. - brand (optional, string): Required by label=mention, ignored otherwise. The brand or product the posts are judged against, up to 300 characters, for example brand=Buldak. Without it label=mention is skipped with the warning label_mention_needs_brand and is not billed. - brand_description (optional, string): Optional, with label=mention. One plain sentence saying what the brand is, for example brand_description=Samyang's spicy instant noodle brand. Use it when the name is also an ordinary word or another company's name. - offer (optional, string): Optional, with label=intent. One or two plain sentences saying what you sell, up to 300 characters, for example offer=A web design agency that builds websites for small businesses. Adds fits_offer to every post; without it fits_offer is null. - label_evidence (optional, enum: 1): Optional, only with label=. When 1, every labelled row also carries computed.labels_evidence. = { quote, sentence_index }: the sentence in the row that most clearly shows the label, copied verbatim. Absent or null when no single sentence shows it. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/naver/cafearticle/search" \ --data-urlencode "query=주식" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /naver/kin/search Search Naver KnowledgeiN (지식iN) Credit cost: 1 (standard) Parameters: - query (required): Free-text search term (UTF-8). Required. - display (optional, integer): Number of items to return per page. Defaults to 10, cap 100; `display=101` returns 400. - start (optional, integer): 1-indexed offset for pagination. Defaults to 1, cap 1000; `start=1001` is a 400 that costs nothing, and the page that reaches rank 1000 is the last one (`has_more: false`). Past rank 100, `sim` and `point` pages repeat Naver's top 100: those rows are removed and counted in `_warnings` (`recycled_rows`), and an emptied page is free. Page deeper with `sort=date`. - sort (optional, enum: sim | date | point): Sort order. Accepted values: sim|date|point. Defaults to `sim` (relevance) when the corpus supports sort. Values outside this corpus's domain are rejected with a 400 before any upstream call. `date` lists the newest first, but Naver serves only the first 1,000 rows of a query (`start` caps at 1000), so on a common word it reaches back hours or days, not months, and on a multi-word query Naver also loosens the match. - group (optional, enum: question): Send `question` to get one row per question. Naver returns one row per answer, so a question with three matching answers fills three rows; grouped, its best-ranked row stands for it and carries `answers_on_page` and `post.ext.answer_nos`. Grouping is within the page, the price is unchanged, and paging still advances by the rows Naver served. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/naver/kin/search" \ --data-urlencode "query=코로나 증상" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /naver/local/search Search Naver Local (장소 검색) Credit cost: 1 (standard) Parameters: - query (required): Free-text search term (UTF-8). Required. - display (optional, integer): Number of places to return. This corpus caps at 5 and **defaults to 1**, so omitting it returns a single place. Values above 5 are silently clamped by Naver rather than rejected. - sort (optional, enum: random | comment): Sort order. Accepted values: random|comment (display max 5, start max 1). Defaults to `sim` (relevance) when the corpus supports sort. Values outside this corpus's domain are rejected with a 400 before any upstream call. ``` curl -G "https://www.socialcrawl.dev/v1/naver/local/search" \ --data-urlencode "query=강남역 카페" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /naver/image/search Search Naver Image Credit cost: 1 (standard) Parameters: - query (required): Free-text search term (UTF-8). Required. - display (optional, integer): Number of items to return per page. Defaults to 10, cap 100; `display=101` returns 400. - start (optional, integer): 1-indexed offset for pagination. Defaults to 1, cap 1000; `start=1001` is a 400 that costs nothing, and the page that reaches rank 1000 is the last one (`has_more: false`). Past rank 100, `sim` and `point` pages repeat Naver's top 100: those rows are removed and counted in `_warnings` (`recycled_rows`), and an emptied page is free. Page deeper with `sort=date`. - sort (optional, enum: sim | date): Sort order. Accepted values: sim|date. Defaults to `sim` (relevance) when the corpus supports sort. Values outside this corpus's domain are rejected with a 400 before any upstream call. `date` lists the newest first, but Naver serves only the first 1,000 rows of a query (`start` caps at 1000), so on a common word it reaches back hours or days, not months, and on a multi-word query Naver also loosens the match. - filter (optional, enum: all | large | medium | small): Restrict to an image size band: `all` (default), `large`, `medium`, or `small`. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/naver/image/search" \ --data-urlencode "query=한라산" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /naver/webkr/search Search Naver Web (웹문서) Credit cost: 1 (standard) Parameters: - query (required): Free-text search term (UTF-8). Required. - display (optional, integer): Number of items to return per page. Defaults to 10, cap 100; `display=101` returns 400. - start (optional, integer): 1-indexed offset for pagination. Defaults to 1, cap 1000; `start=1001` is a 400 that costs nothing, and the page that reaches rank 1000 is the last one (`has_more: false`). Past rank 100, `sim` and `point` pages repeat Naver's top 100: those rows are removed and counted in `_warnings` (`recycled_rows`), and an emptied page is free. This corpus ignores `sort`, so `sort=date` does not avoid the repeats. - sort (optional, string): Sort order. Accepted values: (sort ignored). Defaults to `sim` (relevance) when the corpus supports sort. Values outside this corpus's domain are rejected with a 400 before any upstream call. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/naver/webkr/search" \ --data-urlencode "query=기후변화" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /naver/errata Correct a mistyped Korean search query (오타변환) Credit cost: 1 (standard) Parameters: - query (required): The possibly-mistyped search term (UTF-8). Required. ``` curl -G "https://www.socialcrawl.dev/v1/naver/errata" \ --data-urlencode "query=네이볘" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /naver/adult Check whether a Korean search term is adult-only (성인 검색어 판별) Credit cost: 1 (standard) Parameters: - query (required): The search term to classify (UTF-8). Required. ``` curl -G "https://www.socialcrawl.dev/v1/naver/adult" \ --data-urlencode "query=성인영화" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /naver/search-trend Get Naver search-volume trend for Korean keywords (검색어트렌드) (oneOf: keywords|groups) Credit cost: 5 (advanced) Parameters: - keywords (optional, string): Comma-separated Korean keywords, up to 20, combined into ONE trend series (volumes summed, not compared). Send this or `groups`. - groups (optional, string): Up to 5 keyword groups, each its own series on one shared scale: separate groups with `|` and the keywords inside a group with `,`, for example `손 다한증,발 다한증|미라드라이|암내,액취증`. Each series is named by its first keyword. Up to 20 keywords per group. Send this or `keywords`, not both. - start_date (optional, string): Window start, `YYYY-MM-DD`. Defaults to 12 months before `end_date`. Clamped up to 2016-01-01, the earliest data Naver holds. - end_date (optional, string): Window end, `YYYY-MM-DD`. Defaults to today. - time_unit (optional, enum: date | week | month): Aggregation bucket: `date`, `week`, or `month` (default `month`). - group_name (optional, string): Label for the `keywords` series in the response. Defaults to the first keyword. Not used with `groups`. - device (optional, enum: pc | mo): Restrict to `pc` or `mo` (mobile). Omit for both. - gender (optional, enum: f | m): Restrict to `f` or `m`. Omit for both. - ages (optional, string): Comma-separated age-band codes, `1` to `11`: `1` 0-12, `2` 13-18, `3` 19-24, `4` 25-29, `5` 30-34, `6` 35-39, `7` 40-44, `8` 45-49, `9` 50-54, `10` 55-59, `11` 60+. Several codes are SUMMED into one audience, not split. NOTE this endpoint uses different codes from `shopping-insight/*`, which takes 10/20/30/40/50/60. - Constraint: one of keywords, groups (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/naver/search-trend" \ --data-urlencode "keywords=삼성전자,갤럭시" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /naver/shopping-insight/category Get Naver Shopping click trend for a category (쇼핑인사이트) Credit cost: 5 (advanced) Parameters: - category_code (required): Comma-separated Naver Shopping category ids, up to 3 (e.g. `50000000` for 패션의류). Required. When `breakdown` is set, only the first id is used. Naver publishes no category-list API, so see the platform guide for the id table. - start_date (optional, string): Window start, `YYYY-MM-DD`. Defaults to 12 months before `end_date`. Clamped up to 2017-08-01, the earliest Shopping Insight data Naver holds. - end_date (optional, string): Window end, `YYYY-MM-DD`. Defaults to today. - time_unit (optional, enum: date | week | month): Aggregation bucket: `date`, `week`, or `month` (default `month`). - device (optional, enum: pc | mo): Restrict to `pc` or `mo` (mobile). Omit for both. - gender (optional, enum: f | m): Restrict to `f` or `m`. Omit for both. - ages (optional, string): Comma-separated age buckets: `10`, `20`, `30`, `40`, `50`, `60`. NOTE these differ from `search-trend`, which takes 1-11. - breakdown (optional, enum: device | gender | age): Split a single category by `device`, `gender`, or `age` instead of comparing several categories. ``` curl -G "https://www.socialcrawl.dev/v1/naver/shopping-insight/category" \ --data-urlencode "category_code=50000000" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /naver/shopping-insight/keyword Get Naver Shopping click trend for keywords inside a category (쇼핑인사이트) Credit cost: 5 (advanced) Parameters: - category_code (required): A single Naver Shopping category id to search within (e.g. `50000000`). Required. - keyword (required): Comma-separated keywords, up to 5, each returned as its own comparable series inside that category. Required. When `breakdown` is set, only the first keyword is used. - start_date (optional, string): Window start, `YYYY-MM-DD`. Defaults to 12 months before `end_date`. Clamped up to 2017-08-01, the earliest Shopping Insight data Naver holds. - end_date (optional, string): Window end, `YYYY-MM-DD`. Defaults to today. - time_unit (optional, enum: date | week | month): Aggregation bucket: `date`, `week`, or `month` (default `month`). - device (optional, enum: pc | mo): Restrict to `pc` or `mo` (mobile). Omit for both. - gender (optional, enum: f | m): Restrict to `f` or `m`. Omit for both. - ages (optional, string): Comma-separated age buckets: `10`, `20`, `30`, `40`, `50`, `60`. NOTE these differ from `search-trend`, which takes 1-11. - breakdown (optional, enum: device | gender | age): Split a single keyword by `device`, `gender`, or `age` instead of comparing several keywords. ``` curl -G "https://www.socialcrawl.dev/v1/naver/shopping-insight/keyword" \ --data-urlencode "category_code=50000000" \ --data-urlencode "keyword=코트" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /naver/brief One query across the Korean internet (5 Naver corpora) + optional digest. Credit cost: 10 (override; tier advanced) Parameters: - query (required): Search query (Korean or any language). - corpora (optional, string): CSV subset of news,blog,cafearticle,kin,webkr (default all five). `shop` was retired by Naver on 2026-07-31 and is rejected. - display (optional, integer): Items per corpus (1-100, default 20). - start (optional, integer): 1-indexed offset per corpus (1-1000, default 1). Prefer `cursor` for paging. - sort (optional, string): sim (relevance, default) or date; kin also point. - include (optional, string): Set to `digest` for an LLM English digest with translated quotes. - cursor (optional, string): Opaque pagination token from a prior response's next_cursor. ``` curl -G "https://www.socialcrawl.dev/v1/naver/brief" \ --data-urlencode "query=삼성전자" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /naver/place/rank Check where a place ranks on Naver Map for keywords (플레이스 순위) (oneOf: id|url) Credit cost: 4-42 (metered): 4 credits per keyword, plus 2 credits when lat and lng are left out (the point is read from the place). The whole amount is refunded when the check fails. Parameters: - keywords (required): One to ten keywords, separated by commas, exactly as a person types them into Naver, e.g. `신사역 정형외과,강남 대상포진`. - id (optional, string): The Naver Place id, e.g. 11669922 (the number in the place's link). Send this or url. - url (optional, string): The place's link as Naver shows it: m.place.naver.com/hospital/11669922/home, map.naver.com/p/entry/place/11669922, or a naver.me share link. Send this or id. - lat (optional, string): Latitude of the point to search from (send with lng). Leave both out to search from the place itself. - lng (optional, string): Longitude of the point to search from (send with lat). - top (optional, integer): How many of the top-ranked places to list with each result, 0 to 20 (default 5). - ads (optional, boolean): Also check the paid 플레이스 광고 slots and report the place's `ad_rank` (default false). - Constraint: one of id, url (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/naver/place/rank" \ --data-urlencode "keywords=신사역 정형외과,강남 대상포진" \ --data-urlencode "id=11669922" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /naver/place/search Search Naver Map places for a keyword, in Naver's order (플레이스 검색) Credit cost: 4 (override; tier advanced) Parameters: - query (required): The keyword, exactly as a person types it into Naver Map, e.g. `신사역 정형외과`. - lat (optional, string): Latitude of the point to search from (send with lng). Default: Gangnam Station. - lng (optional, string): Longitude of the point to search from (send with lat). - limit (optional, integer): How many organic places to return, 1 to 100 (default 100). The price is the same at any limit. - ads (optional, boolean): Also return the paid 플레이스 광고 slots, after the organic places (default false). ``` curl -G "https://www.socialcrawl.dev/v1/naver/place/search" \ --data-urlencode "query=신사역 정형외과" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /naver/place/reviews Get the newest visitor reviews of a Naver Place (방문자 리뷰) (oneOf: id|url) Credit cost: 5-9 (metered): 5 credits per page. The default labels (sentiment, issue) are free. label=reports, incentivized or injection holds 4 extra credits and refunds down to 1 credit per started 25 reviews that were newly judged (the first 100 reviews of a page are judged): reviews already labelled are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. Parameters: - id (optional, string): The Naver Place id, e.g. 11669922 (the number in the place's link). Send this or url. - url (optional, string): The place's link as Naver shows it: m.place.naver.com/hospital/11669922/home, map.naver.com/p/entry/place/11669922, or a naver.me share link. Send this or id. - sort (optional, enum: recent | recommended): `recent` (default): newest visits first. `recommended`: Naver's own order. - label (optional, string): Optional CSV of SocialCrawl labels to add to every review. Without this param every page already carries sentiment and issue, free; label= adds the labels you name to them (the defaults keep running). sentiment and issue are free when asked for too; reports, incentivized and injection add 1 credit per started 25 newly judged reviews. judgments=off (or label=none) turns the default labels off. They read the review's title and text, never its stars. sentiment: how the reviewer feels, on five levels (level 0 to 4, score_0_1, confidence), plus rating_mismatch, true when the words clearly contradict the star rating (a glowing text under 1 star, a furious one under 5), null when the review has no rating. issue: the main problem the review reports (label: product_defect, sizing_or_fit, shipping_or_delivery, customer_service, price_or_value, missing_feature, other, or none, with confidence; label is null when unsure). reports (needs reports=): does this review say that the thing you describe happened (p, 0 to 1). incentivized: does the reviewer say they got the product free, discounted or rewarded for the review (p), and did the text carry a disclosure such as 'in exchange for my honest review', Vine or 체험단 (disclosed). injection flags text that addresses an AI system and tries to direct it (flagged, p); it never drops or rewrites a row. These are signals to read, never a verdict on a review or a reviewer. A review that could not be judged carries labels: null. Reviews already labelled for anyone are free, and so is a cached page. data.labels reports what was judged and billed. - reports (optional, string): Required by label=reports, ignored otherwise. What to look for, in plain words, up to 300 characters, for example reports=the battery drains quickly or reports=배송이 늦음. Every review gains labels.reports.p, the probability that it says so, in any wording or language. Without it label=reports is skipped with the warning label_reports_needs_reports and is not billed. - judgments (optional, enum: on | off): Optional, on (the default) or off. By default every row gains free SocialCrawl judgments (computed.labels, and computed.relevance on search endpoints), reported in data.labels (mode default) and data.relevance (origin default), each with a status (complete, partial or skipped) and pending: the rows still being judged when the page was sent, which carry null now and are filled on your next call or cached read. Default judgments never add credits, never change an existing field, and never drop or reorder a row. off returns the page exactly as before, with none of those keys. label=none does the same. - dry_run (optional, enum: 1): Optional. When 1, return a cost preview for this labelled or relevance-filtered request without fetching the page or judging any row. data.estimate reports rows_expected, rows_cached, label_credits_min, label_credits_max and base_credits. 0 credits charged. - label_evidence (optional, enum: 1): Optional, only with label=. When 1, every labelled row also carries computed.labels_evidence. = { quote, sentence_index }: the sentence in the row that most clearly shows the label, copied verbatim. Absent or null when no single sentence shows it. - fit (optional, enum: goal): Optional. When goal, keep the rows and fields needed for the goal you pass in goal= (plus any that are uncertain, and the first and last), and replace the rest with a stub. data.held_back lists the held ids and a recall id that re-reads the full page from cache at no extra charge. Without this param the page is unchanged. - goal (optional, string): Required by fit=goal. What you are trying to do, in your own words, up to 300 characters. - fit_tokens (optional, integer): Optional, only with fit=goal. Soft cap on how much of the page to keep, in tokens. Uncertain blocks and the first and last block are kept even if they exceed it. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off the page, and the page that reaches one ends the walk: `next_cursor` is not returned and `pagination.stopped_at` is `since`. Sending it sets the newest-first sort shown in this endpoint's sort parameter (any other sort value is a free 400). The page still costs what a page costs, so you pay for the pages you walk and no more. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - Constraint: one of id, url (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/naver/place/reviews" \ --data-urlencode "id=11669922" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /naver/place Get a Naver Place card (플레이스 정보) (oneOf: id|url) Credit cost: 3 (override; tier advanced) Parameters: - id (optional, string): The Naver Place id, e.g. 11669922 (the number in the place's link). Send this or url. - url (optional, string): The place's link as Naver shows it: m.place.naver.com/hospital/11669922/home, map.naver.com/p/entry/place/11669922, or a naver.me share link. Send this or id. - Constraint: one of id, url (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/naver/place" \ --data-urlencode "id=11669922" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /naver/blog Get a Naver blog's card and visitor counter (블로그 정보) (oneOf: handle|url) Credit cost: 1 (standard) Parameters: - handle (optional, string): The Naver blog id, e.g. flock8979 (the part after blog.naver.com/). Send this or url. - url (optional, string): The blog's link, e.g. blog.naver.com/flock8979 or m.blog.naver.com/flock8979. Send this or handle. - Constraint: one of handle, url (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/naver/blog" \ --data-urlencode "handle=blogpeople" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /naver/blog/posts List every post of a Naver blog, newest first (블로그 글 목록) (oneOf: handle|url) Credit cost: 1 (standard) Parameters: - handle (optional, string): The Naver blog id, e.g. flock8979 (the part after blog.naver.com/). Send this or url. - url (optional, string): The blog's link, e.g. blog.naver.com/flock8979 or m.blog.naver.com/flock8979. Send this or handle. - page (optional, integer): Page number, 30 posts a page (default 1). Prefer `cursor`. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. - Constraint: one of handle, url (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/naver/blog/posts" \ --data-urlencode "handle=flock8979" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## On_page ### GET /on_page/page Run an on-page SEO audit for one URL Credit cost: 1 (standard) Parameters: - url (required): Absolute page URL to audit, including https://. - enable_javascript (optional, boolean): Set true to execute JavaScript while loading the page. Default false. ``` curl -G "https://www.socialcrawl.dev/v1/on_page/page" \ --data-urlencode "url=https://www.socialcrawl.dev/" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## Perplexity ### GET /perplexity/research Web research via Perplexity Sonar Credit cost: 1 (standard) Parameters: - query (required): Natural-language research prompt. Sonar autonomously searches the live web and grounds the response in real sources. No prompt-engineering required: phrase it as you would to a search engine or research assistant. ``` curl -G "https://www.socialcrawl.dev/v1/perplexity/research" \ --data-urlencode "query=What is the capital of France?" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## Pillar ### GET /pillar/page Get Pillar page Credit cost: 1 (standard) Parameters: - url (required): Full URL of the Pillar page ``` curl -G "https://www.socialcrawl.dev/v1/pillar/page" \ --data-urlencode "url=https://pillar.io/example" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## Pinterest ### GET /pinterest/search Search Pinterest pins Credit cost: 1-34 (metered): 1 credit for the page. include=engagement holds 1 credit per row, at most 25 (or `limit`), and keeps only the rows filled from a fresh lookup, so a page is at most 26 credits; rows served from cache are free, unfilled rows are refunded, and a repeat of the same call within the cache window costs 0. Relevance against your query (by default, or with relevance=) is free. Adding relevant_to= holds 4 extra credits and refunds down to 1 credit per started 25 rows that were newly judged: rows already judged for the same topic are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. The default labels (sponsored, intent, niche) are free. label=mention, quality, ai_style, ai_written, injection, or label=intent with offer=, holds 4 extra credits and refunds down to 1 credit per started 25 posts that were newly judged: posts already labelled are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - query (required): Keyword or phrase to search Pinterest pins for. - cursor (optional, string): Cursor to get the next page. Take it from `pagination.next_cursor` on the previous response. - trim (optional, boolean): Set to true for a trimmed down version of the response - include (optional, enum: engagement): Set to `engagement` (one token only) to fill `post.engagement.saves`, `.likes` (reactions), `.comments` and `.shares` in this one call on every row the per-pin lookup can read. A row whose `post.url` is an encrypted `/pin/e1_...` link (every pin measured on 19/10/2026) cannot be read: it stays null, is refunded, and `_warnings` carries `engagement_unavailable` (no row filled) or `engagement_partial` (some filled). Holds 1 credit per row (at most 25, or `limit`) and keeps only the rows filled from a fresh lookup; rows already in cache and rows that could not be filled are refunded, so the most a call can cost is 1 + rows and the usual bill is lower. Adds 4 to 10 seconds on a fresh page (never more than 12) and nothing when the rows are already cached; read `data.hydration` for the rows, the credits held and kept, and the time. - limit (optional, integer): Take the top N rows of the page (1 to 25) after the search has run. With `include=engagement` it also caps the extra credits at N: `limit=5&include=engagement` costs at most 6. It is not a page size: `next_cursor` still advances past the full page, so rows beyond N on this page are not returned by the next page. - relevance (optional, enum: score | filter): Optional. Without this param every row already carries computed.relevance against your query, free ({ p, sense, depth, spam }: is this row about what your query means, or a different thing that shares its words?), and nothing is dropped or reordered. score asks for it explicitly and waits for every row; filter also drops the rows that are not about your query, and lists their ids in data.relevance.dropped_ids. Your query is the topic; nothing to configure. A row that could not be judged is never dropped and carries relevance: null. Pagination is unchanged, so a filtered page can hold fewer rows. Free with your query as the topic; with relevant_to it adds 1 credit per started 25 newly judged rows (rows already judged for the same topic are free, and so is a cached page). - relevance_threshold (optional, string): Optional, only with relevance. The probability (0 to 1) a row must reach to be kept by relevance=filter. Default 0.5. Lower keeps more rows, higher keeps fewer. - relevant_to (optional, string): Optional, only with relevance. Up to 200 characters describing what you mean, used as the topic instead of the query. Use it when the query is ambiguous, for example query=cleopatra with relevant_to=Cleopatra, the IGT slot game. - judgments (optional, enum: on | off): Optional, on (the default) or off. By default every row gains free SocialCrawl judgments (computed.labels, and computed.relevance on search endpoints), reported in data.labels (mode default) and data.relevance (origin default), each with a status (complete, partial or skipped) and pending: the rows still being judged when the page was sent, which carry null now and are filled on your next call or cached read. Default judgments never add credits, never change an existing field, and never drop or reorder a row. off returns the page exactly as before, with none of those keys. label=none does the same. - dry_run (optional, enum: 1): Optional. When 1, return a cost preview for this labelled or relevance-filtered request without fetching the page or judging any row. data.estimate reports rows_expected, rows_cached, label_credits_min, label_credits_max and base_credits. 0 credits charged. - fit (optional, enum: goal): Optional. When goal, keep the rows and fields needed for the goal you pass in goal= (plus any that are uncertain, and the first and last), and replace the rest with a stub. data.held_back lists the held ids and a recall id that re-reads the full page from cache at no extra charge. Without this param the page is unchanged. - goal (optional, string): Required by fit=goal. What you are trying to do, in your own words, up to 300 characters. - fit_tokens (optional, integer): Optional, only with fit=goal. Soft cap on how much of the page to keep, in tokens. Uncertain blocks and the first and last block are kept even if they exceed it. - label (optional, string): Optional CSV of SocialCrawl labels to add to every post. Without this param every page already carries sponsored, intent and niche, free; label= adds the labels you name to them (the defaults keep running). sponsored, intent and niche are free when asked for too; mention, quality, ai_style, intent with offer=, and injection add 1 credit per started 25 newly judged posts. judgments=off (or label=none) turns the default labels off. sponsored: is the post a paid or gifted promotion (p, 0 to 1), did it carry a disclosure marker such as #ad or 광고 (disclosed), is it likely paid with no marker (undisclosed), and which of the accounts it mentions does it promote (brand, or null). These are signals to review, never a finding. mention (needs brand=): is the post about that brand rather than something that shares its name (about_brand, 0 to 1), how it feels about the brand on five levels (sentiment_level 0 to 4 and sentiment_score 0 to 1, null when the post is not about the brand), is it sarcastic, which aspect it talks about (taste_or_quality, price_or_value, availability_or_delivery, health_or_safety, advertising_or_campaign, customer_service, none), and did the author buy or use it (first_hand). intent: what the author is mainly doing (label: asking_for_recommendation, comparing_options, switching_away, complaining, promoting, news_or_discussion, other, or null when unsure, with confidence), whether they read as a potential buyer rather than a seller (buyer, seller), how pressing the need is (urgency, 0 to 3), and, when you pass offer=, whether your offer would plausibly help them (fits_offer). niche: which of the 33 niches of the published taxonomy sc-niche-v1 the caption belongs to, or personal_no_niche, or other (label, confidence, taxonomy), with label null when the caption is too thin to tell or the pick is unsure. It reads the caption only, not the video. quality: how much checkable detail the caption carries (fact_density 0 to 3), whether it mainly asks for likes, replies, shares, follows or tags (engagement_bait, 0 to 1), whether it is written to provoke anger as a way to get engagement (rage_bait, 0 to 1; about the writing, never the side taken), whether it only repeats someone else's news or view (secondhand, 0 to 1), and what the post is mainly doing (post_aim: inform, opinion, sell, entertain, provoke, other). exclude=engagement_bait drops posts whose engagement_bait is 0.8 or higher. ai_style: writing patterns common in AI-generated text, judged on the text alone. It does not say who wrote the post, and a person who writes in this style is flagged too. Six patterns are judged, each a probability (0 to 1): binary_contrast ("This is not X. It's Y."), faux_insight ("Here's what nobody tells you" and throat-clearing openers), colon_reveal ("The best part: it learns."), profound_kicker (ends on an aphorism or mic-drop line), puffery ("marks a pivotal moment", "highlighting the importance of") and rhetorical_setup ("What if I told you", a question the post answers itself, "Not a X. Not a Y. A Z."). Four are counted in the text: slop_words (words and phrases such as delve, leverage, robust, game changer, at the end of the day), em_dashes, emoji_bullets (lines that start with an emoji or a symbol bullet) and one_line_paragraphs (paragraphs that are one short line). pattern_count is how many patterns are present (a judged pattern at 0.8 or higher, slop_words 1 or more, em_dashes 2 or more, emoji_bullets 2 or more, one_line_paragraphs 5 or more) and markers lists their names, comma-separated. A post with less than about eight words of text is only counted, with every judged pattern 0. The judged patterns read the first 400 and the last 150 characters of a long post, and the counts read the whole post. exclude=ai_style drops posts with 2 or more patterns. ai_written: how likely the post was written by an AI model, from SocialCrawl's own text classifier, reading the opening of the post (about 1,000 characters): p (0 to 1, calibrated), level (likely (p 0.95 or higher), possible (p 0.71 or higher) or unlikely) and model (the model name; a new name means a new scale). A post under 8 words is too short to read: p null, level too_short. It is a probability, not proof: a person can be flagged and an AI-written post can pass, and the shorter the post the less certain it is. exclude=ai_written drops likely posts. injection flags text that addresses an AI system and tries to direct it (flagged, p); it never drops or rewrites a row. A post that could not be judged carries labels: null. Posts already labelled for anyone are free, and so is a cached page. data.labels reports what was judged and billed. - exclude (optional, string): Optional CSV (engagement_bait, ai_style, ai_written). engagement_bait, only with label=quality, drops posts whose engagement_bait probability is 0.8 or higher. ai_style, only with label=ai_style, drops posts that show 2 or more of its writing patterns (pattern_count 2 or higher). ai_written, only with label=ai_written, drops posts whose level is likely (p 0.95 or higher), or whose p reaches your ai_written_threshold; a post too short to read is never dropped. Dropped posts are listed in data.labels.dropped_ids. A post that could not be judged is never dropped. - ai_written_threshold (optional, string): Optional, only with label=ai_written and exclude=ai_written. The p (0 to 1) at which exclude=ai_written drops a post. Default 0.95 (level likely). 0.71 also drops possible posts: more AI posts go, and more posts by people with them. - brand (optional, string): Required by label=mention, ignored otherwise. The brand or product the posts are judged against, up to 300 characters, for example brand=Buldak. Without it label=mention is skipped with the warning label_mention_needs_brand and is not billed. - brand_description (optional, string): Optional, with label=mention. One plain sentence saying what the brand is, for example brand_description=Samyang's spicy instant noodle brand. Use it when the name is also an ordinary word or another company's name. - offer (optional, string): Optional, with label=intent. One or two plain sentences saying what you sell, up to 300 characters, for example offer=A web design agency that builds websites for small businesses. Adds fits_offer to every post; without it fits_offer is null. - label_evidence (optional, enum: 1): Optional, only with label=. When 1, every labelled row also carries computed.labels_evidence. = { quote, sentence_index }: the sentence in the row that most clearly shows the label, copied verbatim. Absent or null when no single sentence shows it. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/pinterest/search" \ --data-urlencode "query=home decor ideas" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /pinterest/pin Get Pinterest pin details Credit cost: 1 (standard) Parameters: - url (required): Full URL of the Pinterest pin with its numeric id, e.g. `https://www.pinterest.com/pin/1142999580460050856/`. An encrypted `/pin/e1_...` link, the form search and board pages return as of 19/10/2026, cannot be read and answers 404 at 0 credits. - trim (optional, boolean): Set to true for a trimmed down version of the response ``` curl -G "https://www.socialcrawl.dev/v1/pinterest/pin" \ --data-urlencode "url=https://www.pinterest.com/pin/1142999580460050856/" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /pinterest/url-stats Get Pinterest save counts for external URLs Credit cost: 1 (standard) Parameters: - urls (required): Comma-separated list of 1-10 absolute http(s):// URLs, passed to Pinterest verbatim. Variants (https vs http, with/without trailing slash, with/without query string) are counted as different URLs. ``` curl -G "https://www.socialcrawl.dev/v1/pinterest/url-stats" \ --data-urlencode "urls=https://www.allrecipes.com/recipe/10813/best-chocolate-chip-cookies/" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /pinterest/board Get Pinterest board Credit cost: 1-16 (metered): 1 credit for the page. include=engagement holds 1 credit per row, at most 15 (or `limit`), and keeps only the rows filled from a fresh lookup, so a page is at most 16 credits; rows served from cache are free, unfilled rows are refunded, and a repeat of the same call within the cache window costs 0. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - url (required): Full URL of the Pinterest board - trim (optional, boolean): Set to true for a trimmed down version of the response - cursor (optional, string): Cursor to get the next page of pins. Take it from `pagination.next_cursor` on the previous response. - include (optional, enum: engagement): Set to `engagement` (one token only) to fill `post.published_at` and `post.engagement.likes` (plus any save, comment or share count the board row lacked) in this one call on every row the per-pin lookup can read. A row whose `post.url` is an encrypted `/pin/e1_...` link (every pin measured on 19/10/2026) cannot be read: it stays as it was, is refunded, and `_warnings` carries `engagement_unavailable` or `engagement_partial`. Holds 1 credit per row (at most 15, or `limit`) and keeps only the rows filled from a fresh lookup; rows already in cache and rows that could not be filled are refunded, so the most a call can cost is 1 + rows. Adds 4 to 10 seconds on a fresh page (never more than 12) and nothing when the rows are already cached; read `data.hydration` for the rows, the credits held and kept, and the time. - limit (optional, integer): Take the top N pins of the page (1 to 15). With `include=engagement` it also caps the extra credits at N: `limit=4&include=engagement` costs at most 5. It is not a page size: `next_cursor` still advances past the full page, so pins beyond N on this page are not returned by the next page. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/pinterest/board" \ --data-urlencode "url=https://www.pinterest.com/lizmrodgers/moms-night/" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /pinterest/user/boards List Pinterest user boards Credit cost: 1 (standard) Parameters: - handle (required): The username of the user to get boards for. (e.g. broadstbullycom from https://www.pinterest.com/broadstbullycom/) - trim (optional, boolean): Set to true for a trimmed down version of the response - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. ``` curl -G "https://www.socialcrawl.dev/v1/pinterest/user/boards" \ --data-urlencode "handle=pinterest" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /pinterest/trends Get Pinterest Trends for a country Credit cost: 10 (premium) Parameters: - country (optional, enum: AR | AU | BR | CA | CO | DE | EG | ES | FR | GB | ID | IE | IN | IT | KR | MX | MY | NZ | PH | SA | TH | TR | US): ISO country code, in any case: AR, AU, BR, CA, CO, DE, EG, ES, FR, GB, ID, IE, IN, IT, KR, MX, MY, NZ, PH, SA, TH, TR, US. Pinterest publishes no list for other countries, and one of them is a free 400. IE reads Pinterest's GB+IE list and AU and NZ read AU+NZ; GB alone has its own list. Defaults to US. - type (optional, enum: growing | seasonal | top_monthly | top_yearly): Which Pinterest Trends table: growing (the default), seasonal, top_monthly or top_yearly. - limit (optional, integer): How many terms to return, from the top, 1 to 50. Defaults to 25. The price is the same at any depth. - include (optional, string): Keep only terms containing one of these keywords, comma-separated (up to 10, e.g. nails,wedding). - exclude (optional, string): Drop terms containing any of these keywords, comma-separated (up to 10, e.g. fall). ``` curl "https://www.socialcrawl.dev/v1/pinterest/trends" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## Polymarket ### GET /polymarket/search Search Polymarket prediction markets Credit cost: 1 (standard) Parameters: - query (required): Free-text search term: keywords, entity names, or topics - limit (optional, integer): Max results per result type (events / markets / profiles). Bounds the response size. Defaults to 10. ``` curl -G "https://www.socialcrawl.dev/v1/polymarket/search" \ --data-urlencode "query=trump 2028" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /polymarket/research Polymarket prediction markets: multi-query research Credit cost: 5 (advanced) Parameters: - query (required): The research topic: free-text natural language (e.g. 'last 30 days bitcoin halving', 'kanye west tour'). Framing prefixes like 'last N days' and 'what are people saying about' are stripped automatically before expansion. - limit (optional, integer): Max results per result type on each fan-out call (events / markets / profiles). Bounds the response size. Defaults to 10. ``` curl -G "https://www.socialcrawl.dev/v1/polymarket/research" \ --data-urlencode "query=trump 2028 election" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## Prism ### GET /prism/lookup Universal post/product URL dispatcher: any post, video, product or repo link → the right detail endpoint's unified response. Credit cost: the resolved endpoint's own price, with no routing surcharge: 1 credit for most social lookups and 5 for the advanced ones (Amazon, LinkedIn). The `resolved.endpoint` field in the response names the endpoint that was charged Parameters: - url (required): Absolute http(s) URL of a single post, video, product or repo. Profile, channel and storefront URLs are NOT accepted: use that platform's own `/profile` endpoint for those. - include (optional, string): CSV of optional flags to forward verbatim to the resolved endpoint (e.g. `trim`). Each member must be an optional param of that endpoint. ``` curl -G "https://www.socialcrawl.dev/v1/prism/lookup" \ --data-urlencode "url=https://www.youtube.com/watch?v=dQw4w9WgXcQ" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /prism/comments Every comment on a post, replies nested, server-paginated to completion. Credit cost: 2-200 (metered): 1 credit per comment page scanned plus 1 per replies call that returned replies. An Instagram post URL is a flat 5 credits whatever `max` you pass, plus 1 per replies call only when you send `replies=true` Parameters: - url (required): Absolute http(s) URL of the post whose comments to harvest. - max (optional, integer): Stop after roughly this many top-level comments (1-5000, default 1000). Whole pages are returned, so the actual count can slightly exceed this. Drives billing and, for `sort=top`, the depth of the ranking scan. - replies (optional, boolean): Nest every reply under the comments that have them (`engagement.replies` above zero), walking each comment's replies pages to the end: up to 100 comments and 10 replies pages per comment per call, 1 credit per replies call that returned replies. Default true on TikTok, YouTube and Facebook. On Instagram it is off unless you send `replies=true`, which adds 1 credit per replies call to the flat 5. A comment a limit or the time budget stopped short is marked `replies_truncated: true` and the response says `completed: false`. Reddit and Hacker News always return their whole nested thread and ignore it. Pair with `replies=false` when you only want the top comments. - cursor (optional, string): Opaque composite cursor from a prior response's `next_cursor` to resume harvesting. - sort (optional, enum: top | recent): `recent` (default: natural order) or `top` (most-liked first, ranked by each comment's like count). With `top`, the response adds `sorted_by: "likes_desc"`. On Instagram, `top` returns the platform's own ranked head (`sorted_by: "popular"`) rather than a re-sort, and that head is finite: use the default `recent` to harvest a whole Instagram thread. - limit (optional, integer): Cap on how many top-level comments to return after sorting/scanning (1-5000, defaults to `max`). Truncates only the returned set, never what was scanned or billed. The response reports `returned`. ``` curl -G "https://www.socialcrawl.dev/v1/prism/comments" \ --data-urlencode "url=https://www.youtube.com/watch?v=dQw4w9WgXcQ" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /prism/profile/comments New comments on a profile's recent posts, in one list, newest first. (oneOf: url|handle) Credit cost: 2-2428 (metered): each page actually read, at that endpoint's own price: the post list page (1 credit) plus the comment pages of each post that returned a new comment (5 credits a page on Instagram, 1 on Facebook, YouTube, TikTok and X). A post whose comments are all older than `since` costs nothing, so a call that finds no new comment costs only the post list page. With `replies=true`, plus 1 credit per replies call that returned replies. The call holds the most that `posts` and `per_post` can take (251 credits for Instagram's defaults, plus up to 100 replies pages, 300 on Instagram, with `replies=true`) and refunds the rest Parameters: - url (optional, string): The profile, page or channel URL: instagram.com/, facebook.com/, youtube.com/@ or youtube.com/channel/UC..., tiktok.com/@, or x.com/. The platform is read from the URL. A link to one post is refused: use that platform's own comment endpoint for one post. One of `url` or `handle` is required. - platform (optional, enum: instagram | facebook | youtube | tiktok | twitter | x): With `handle`: instagram, facebook, youtube, tiktok or twitter (x is accepted for twitter). Not needed with `url`. - handle (optional, string): The account's handle, with or without @, together with `platform`. On YouTube a channel id (UC...) works too. A pasted profile URL is read like `url`. - posts (optional, integer): How many of the profile's most recent posts to scan (1-25, default 10). A post whose comment count is 0 is skipped and costs nothing. - per_post (optional, integer): The most comments read on each post (1-200, default 50), newest first on Instagram and YouTube. Sets the hold; you pay only for the pages read. - since (optional, string): Only comments published at or after this time: an ISO 8601 timestamp or YYYY-MM-DD (midnight UTC). On Instagram and YouTube each post's walk stops at the first older comment; on Facebook and TikTok the comments are filtered. In a Monitor, `now-3h` (or `now-1d`) is resolved to a timestamp at every run. - replies (optional, boolean): `true` adds each returned comment's replies to the list on Instagram, Facebook, YouTube and TikTok, as rows with `comment.parent_id`, at 1 credit per replies call that returned replies (up to 100 comments and 10 replies pages each per call). Default `false`. - Constraint: one of url, handle (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/prism/profile/comments" \ --data-urlencode "url=https://www.instagram.com/nasa/" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /prism/brand-mentions Brand mention volume time-series, sentiment split, top sources, and recent mentions for one keyword. Credit cost: 50 (override; tier advanced) Parameters: - keyword (required): The brand or term to track. Wrap in quotes for exact-phrase semantics. - date_from (required): Window start (YYYY-MM-DD). Required by the trend leg. - date_to (optional, string): Window end (YYYY-MM-DD). Defaults to the latest crawl. - date_group (optional, enum: day | week | month): Trend bucket size: day (default), week, or month. - page_type (optional, enum: ecommerce | news | blogs | message-boards | organization): Optional surface filter: ecommerce, news, blogs, message-boards, or organization. - include (optional, string): Set `digest` to add an LLM narrative summary leg. - brand_description (optional, string): Optional one-line description of the brand, used to tell it apart from other things with the same name. ``` curl -G "https://www.socialcrawl.dev/v1/prism/brand-mentions" \ --data-urlencode "keyword=socialcrawl" \ --data-urlencode "date_from=2026-06-01" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /prism/demand-signals Consumer-demand nowcast: app-review velocity, web mention slope, Reddit velocity, and commerce review levels, fused into a published demand index. Credit cost: 30 (override; tier advanced) Parameters: - keyword (required): The brand or product to nowcast. Drives the mention, Reddit, and Amazon legs. - google_play_id (optional, string): Google Play package name (e.g. com.spotify.music) for the app-review velocity axis. - app_store_id (optional, string): Apple App Store numeric id for the app-review velocity axis. - signals (optional, string): CSV subset of app_reviews,mentions,reddit,commerce (default all). app_reviews is dropped when no app id is supplied. - amazon_query (optional, string): Override the Amazon product-search term if it differs from the brand. - country (optional, string): Location for the app-review legs (default United States); a 2-letter code is also applied to the Amazon leg. - date_from (optional, string): Window start (YYYY-MM-DD) for the mention-slope leg. Defaults to 30 days ago. - date_to (optional, string): Window end (YYYY-MM-DD). Defaults to today. - depth (optional, integer): Reviews per store for the velocity computation (1-600, default 150). ``` curl -G "https://www.socialcrawl.dev/v1/prism/demand-signals" \ --data-urlencode "keyword=spotify" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /prism/campaign Campaign tracker: pre/during/post volume lift, cross-platform engagement, and ranked top amplifiers for a hashtag or phrase. (oneOf: hashtag|phrase) Credit cost: 35 (override; tier advanced) Parameters: - hashtag (optional, string): The campaign hashtag (with or without #). One of hashtag or phrase is required. - phrase (optional, string): A campaign slogan/phrase instead of a hashtag. One of hashtag or phrase is required. - window_start (optional, string): Campaign launch date (YYYY-MM-DD): the pre/during boundary. Optional; defaults to 30 days ago. - window_end (optional, string): Campaign end date (YYYY-MM-DD): the during/post boundary. Defaults to today. - pre_days (optional, integer): Baseline days before window_start for lift measurement (1-90, default 14). - post_days (optional, integer): Days after window_end for the post window (0-90, default 14; 0 = no post window). - include (optional, string): Set amplifier_dates to date YouTube amplifiers via direct youtube/video calls (undated in fusion). - brand (optional, string): Optional brand the amplifier posts are judged against. Defaults to the hashtag or phrase. - brand_description (optional, string): Optional one-line description of the brand or campaign, used to tell it apart from other things with the same name. - Constraint: one of hashtag, phrase (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/prism/campaign" \ --data-urlencode "hashtag=summergiveaway" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /prism/ai-visibility AI Share-of-Voice / GEO monitoring: prompt set x reruns to per-brand appearance-% per AI engine plus a cited-domain ranking. (oneOf: topic|prompts) Credit cost: 2-1605 (metered): 2 credits per probe, where a probe is one prompt run once on one engine, so the charge is `2 x prompts x runs x engines`. On the defaults (both engines, 8 runs) a 5-prompt audit is 160 credits. `include=web_baseline` adds a flat 5. The 1,605 ceiling is the 20-prompt, 20-run, two-engine worst case with the baseline on - `preset=quick` holds far less, and the hold settles down to the probes that actually completed Parameters: - brand (required): The brand whose appearance-% is measured (required). Matched against each answer plus its aliases. - prompts (optional, string): The category prompts to probe, as a JSON array or a pipe-delimited list (1-20). One of prompts or topic is required. - topic (optional, string): A topic probed as a single prompt in v1 (one of prompts or topic is required). - competitors (optional, string): CSV of up to 5 competitors also measured for appearance-% from the same answers. - engines (optional, string): CSV subset of perplexity,grok (default both): the grounded-answer engines probed. - runs (optional, integer): Reruns per (prompt, engine) to measure variance (1-20, default 8). - preset (optional, enum: quick | standard | deep): quick|standard|deep: sets runs and caps prompts for a flat probe budget. - include (optional, string): Set web_baseline to cross-join AI-cited domains against your web top domains. - brand_domains (optional, string): CSV of your own domains so the citation ranking can flag the ones you already rank on. - Constraint: one of topic, prompts (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/prism/ai-visibility" \ --data-urlencode "brand=socialcrawl" \ --data-urlencode "topic=social media data api" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /prism/crisis-postmortem Crisis post-mortem: a who-said-what-first timeline across web, Reddit, Hacker News, and social, with an origin, peak, propagation sequence, and a grounded narrative. Credit cost: 35 (override; tier advanced) Parameters: - brand (required): The brand or entity the crisis is about (required). - window_start (optional, string): Start of the crisis window (YYYY-MM-DD): the earliest point on the timeline. Optional; defaults to 30 days ago. - window_end (optional, string): End of the crisis window (YYYY-MM-DD). Defaults to today. - crisis_terms (optional, string): Optional CSV of up to 5 terms (e.g. recall,defect) that scope the legs to the actual incident. - include (optional, string): narrative (default on) adds the grounded LLM propagation narrative; pass an empty value for the raw timeline only. ``` curl -G "https://www.socialcrawl.dev/v1/prism/crisis-postmortem" \ --data-urlencode "brand=acme" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /prism/crisis-radar Stateless crisis breach check: a z-score on daily mention volume and negative share, with on-breach confirmation and a severity grade. Credit cost: 15-45 (metered): 15 credits for the baseline breach check. `confirm=true` escalates to the full 45-credit investigation. Run the baseline first and escalate only when it reports a breach: that is the whole point of the two-step shape Parameters: - brand (required): The brand to watch. - sensitivity (optional, string): Z-score breach threshold (0.5-6, default 2.0). z>=sensitivity on volume or negative-share fires a breach. - confirm (optional, boolean): true -> on a breach, run the escalation legs and grade severity (+30 credits, charged only when a breach fires). - baseline_days (optional, integer): Rolling-mean window for the z-score (3-30, default 7). - date_to (optional, string): The day being evaluated (YYYY-MM-DD). Defaults to today. - brand_description (optional, string): Optional one-line description of the brand, used to tell it apart from other things with the same name. ``` curl -G "https://www.socialcrawl.dev/v1/prism/crisis-radar" \ --data-urlencode "brand=socialcrawl" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /prism/devtool-pulse Developer-brand health: a devtool's repo dossier + Hacker News reaction + Reddit chatter + dev-blog echo, in one call. Credit cost: 20 (override; tier advanced) Parameters: - query (required): The devtool name to sweep across Hacker News, Reddit, and the dev-blog index (e.g. Bun, Drizzle ORM, tRPC). - repo (optional, string): The repo to dossier: owner/repo or a github.com/{owner}/{repo} URL. Recommended for a precise dossier. - subreddit (optional, string): Optional scope for the Reddit leg (bare name, no r/): switches it to a subreddit search. - include (optional, string): CSV subset of dossier,hn,reddit,blogs (default all). Trims which legs run, not the flat price. - date_from (optional, string): Optional window start (YYYY-MM-DD) applied to the time-bounded legs. - date_to (optional, string): Optional window end (YYYY-MM-DD). ``` curl -G "https://www.socialcrawl.dev/v1/prism/devtool-pulse" \ --data-urlencode "query=Bun" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /prism/leads Ranked feed of public conversations where people seek alternatives to or are switching from a competitor. Credit cost: 50 (override; tier advanced) Parameters: - competitor (required): The competitor/product to mine alternative-seeking conversations for (≤80 chars). - product_category (optional, string): Optional disambiguator appended to the social queries to cut cross-domain noise (e.g. 'project management'). - freshness (optional, string): Recency floor. Nd/Nw/Nm (e.g. 30d) or an ISO date. Default 30d. - include (optional, string): Set `comments` to attach top-thread Reddit comments to the top leads. - limit (optional, integer): Max leads in the fused feed (1-100, default 50). - offer (optional, string): What you sell, in one or two plain sentences (max 300 chars). Used to judge whether each author would plausibly be helped by it (intent.fits_offer). Default: 'alternatives to {competitor}'. ``` curl -G "https://www.socialcrawl.dev/v1/prism/leads" \ --data-urlencode "competitor=notion" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /prism/comment-leads Buyer leads from the comments under the posts a keyword search returns, opening only the posts worth opening. Credit cost: 0-243 (metered): Pay for what is opened: 1 credit a search page, 1 credit a comment page fetched and 1 credit for every started 25 comments labelled. The default request holds 37 and refunds what did not run; judging which posts to open is free Parameters: - query (required): The keyword search to run (max 100 chars). - offer (optional, string): What you sell, in one or two plain sentences (max 300 chars). Used to judge which posts would draw your buyers and whether each commenter would be helped by it. Default: the `query` itself. - platform (optional, enum: tiktok): Platform to search. `tiktok` (default and, for now, only). - posts (optional, integer): How many posts to search (1-90, default 30; 30 posts a search page). - open (optional, integer): The most posts whose comments are fetched (1-20, default 12). - comments_per_post (optional, integer): The most comments read from each opened post (1-200, default 50; 50 comments a page). - include (optional, string): Set `askers` to also return comments that ask a question the offer would answer, marked `kind: 'asker'`. ``` curl -G "https://www.socialcrawl.dev/v1/prism/comment-leads" \ --data-urlencode "query=standing desk setup" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /prism/earned-media A brand's earned-media footprint: news + tech-press + fresh-web clips, deduped and ranked, with an outlet-coverage rollup. Credit cost: 25 (override; tier advanced) Parameters: - brand (required): The brand/company to map earned media for. - competitor (optional, string): Optional competitor for a share-of-coverage gap. - date_from (optional, string): Window start (YYYY-MM-DD). - date_to (optional, string): Window end (YYYY-MM-DD). - min_domain_rank (optional, integer): Drop clips from sites below this domain authority. - include (optional, string): Set `digest` to add an LLM narrative summary. - brand_description (optional, string): Optional one-line description of the brand, used to tell it apart from other things with the same name. ``` curl -G "https://www.socialcrawl.dev/v1/prism/earned-media" \ --data-urlencode "brand=Vercel" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /prism/truthsocial-pulse A Truth Social handle's pulse: profile, recent posts, per-post detail drill, and the news echo, in one call. Credit cost: 20 (override; tier advanced) Parameters: - handle (required): The Truth Social handle (no @). - drill (optional, integer): How many top posts to drill for full detail (0 to skip). - posts (optional, integer): How many recent posts to pull (window cap). - news_query (optional, string): Override the content_analysis news keyword (defaults to the handle/display name). - include (optional, string): CSV subset of posts,news to trim which legs run. - cursor (optional, string): Opaque pagination cursor for the posts leg. ``` curl -G "https://www.socialcrawl.dev/v1/prism/truthsocial-pulse" \ --data-urlencode "handle=realDonaldTrump" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /prism/launch-echo How a launch landed: the Hacker News reaction (top threads + comments), the dev-blog echo, and an optional repo dossier. Credit cost: 20 (override; tier advanced) Parameters: - query (required): The launch/product name to measure reception for. - repo (optional, string): Optional owner/repo or github URL to anchor the dossier. - threads (optional, integer): How many top HN threads to dig comments for. - date_from (optional, string): Window start (YYYY-MM-DD). - date_to (optional, string): Window end (YYYY-MM-DD). - include (optional, string): CSV subset toggling the comments/blogs/dossier legs. ``` curl -G "https://www.socialcrawl.dev/v1/prism/launch-echo" \ --data-urlencode "query=Bun 1.2" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /prism/audience-overlap How much two TikTok creators' commenter audiences overlap. Jaccard, shared-fan count, and a confidence label. Credit cost: 20 (override; tier advanced) Parameters: - handle_a (required): First TikTok creator handle. - handle_b (required): Second TikTok creator handle. - platform (optional, string): Platform (tiktok only in v1; default tiktok). - videos_per_creator (optional, integer): Recent videos sampled per creator (1-10, default 5): caps the commenter pull. - depth (optional, string): Set `deep` to widen the shared-fan enrichment sample. ``` curl -G "https://www.socialcrawl.dev/v1/prism/audience-overlap" \ --data-urlencode "handle_a=mkbhd" \ --data-urlencode "handle_b=mrwhosetheboss" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /prism/reputation A brand's cross-source reputation. Trustpilot + app stores + Google Business + web sentiment, blended into one weighted score with themed pros/cons. Credit cost: 30 (override; tier premium) Parameters: - brand (required): The brand/business/domain to assess. - sources (optional, string): CSV subset of trustpilot,google_play,app_store,google,tripadvisor,web (default the core set). - country (optional, string): Marketplace or locale, as a location name or numeric code (look one up with `GET /v1/content_analysis/locations`). Defaults to United States. - depth (optional, integer): Reviews per source (Trustpilot clamped ≤20). - place (optional, string): Set to enable the place-based legs (Google Business + TripAdvisor). - axis (optional, enum: company | product | both): Which reputation axis to headline: company, product, or both (default). - app_store_id (optional, string): App Store id to anchor that axis directly (skips resolver). - google_play_id (optional, string): Google Play id to anchor that axis directly (skips resolver). - include (optional, string): CSV subset of `themes`, `response_rate`, `languages` (default: all three). Naming a subset TRIMS the report to those blocks; an unknown member or an empty value is a 400. ``` curl -G "https://www.socialcrawl.dev/v1/prism/reputation" \ --data-urlencode "brand=Notion" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /prism/employer-brand A company's employer brand: what people say about working there across Reddit, the web, YouTube, Naver, and the company's own LinkedIn voice. Credit cost: 30 (override; tier premium) Parameters: - company (required): The employer/company name. - linkedin_url (optional, string): The company's LinkedIn URL: enables the LinkedIn voice axis + the posting-vs-reality gap. - surfaces (optional, string): CSV subset of the surfaces to include. - phrases (optional, string): Optional extra Reddit search phrases. - timeframe (optional, string): Reddit timeframe window. - date_from (optional, string): Window start (YYYY-MM-DD). - date_to (optional, string): Window end (YYYY-MM-DD). ``` curl -G "https://www.socialcrawl.dev/v1/prism/employer-brand" \ --data-urlencode "company=Stripe" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /prism/audience-questions The real questions a topic's audience asks: harvested from Reddit + YouTube threads (Naver KnowledgeiN for Korean topics) and clustered by intent (who/what/why/how/vs). Credit cost: 30 (override; tier premium) Parameters: - topic (required): The topic/keyword to mine audience questions for. - platforms (optional, string): CSV subset of reddit,youtube,web,naver. Default reddit,youtube; a topic written in Hangul defaults to naver,youtube (naver = Naver KnowledgeiN questions). - max_questions (optional, integer): Cap on questions returned. - threads_per_source (optional, integer): How many top threads to dig per source. - timeframe (optional, string): Recency window. - include (optional, string): Set `web` to add the universal-search leg. ``` curl -G "https://www.socialcrawl.dev/v1/prism/audience-questions" \ --data-urlencode "topic=kubernetes operators" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /prism/product-reviews A product's reviews across Amazon + Google Shopping + Trustpilot, folded into a cross-marketplace rating + themed pros/cons report. (oneOf: query|asin|gid) Credit cost: 30 (override; tier premium) Parameters: - query (optional, string): Product name to auto-resolve across marketplaces. - asin (optional, string): Amazon ASIN to anchor the Amazon axis directly. - gid (optional, string): Google product id (gid) to anchor the Google Shopping axis (NOT the catalogid). - sources (optional, string): CSV subset of amazon,google_shopping,trustpilot (default amazon,google_shopping; add trustpilot to include it). - country (optional, string): Marketplace country, as a location name or numeric code (look one up with `GET /v1/content_analysis/locations`). - depth (optional, integer): Reviews per source (Trustpilot clamped ≤20). - competitors (optional, string): Optional competitor products for a comparison (validated; deeper sweep is a fast-follow). - include (optional, string): CSV subset of `topics`, `timeline`, `sellers`, `retailer_matrix`, `social_echo` (default: all five). Naming a subset TRIMS which blocks are computed; an unknown member or an empty value is a 400. `sellers` also needs `amazon` to be in scope. - Constraint: one of query, asin, gid (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/prism/product-reviews" \ --data-urlencode "query=AirPods Pro 3" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /prism/apps-lookup One app across Google Play + the App Store: resolved, title-matched, and compared into a cross-store rating + listing report. (oneOf: title|google_play_id|app_store_id) Credit cost: 30 (override; tier premium) Parameters: - title (optional, string): The app title to resolve across both stores. - google_play_id (optional, string): Google Play app id to anchor that store directly. - app_store_id (optional, string): App Store app id to anchor that store directly. - stores (optional, string): CSV subset of google_play,app_store (default both). - country (optional, string): Store country. - language (optional, string): Store language. - match_threshold (optional, string): Title-similarity threshold for the cross-store match guard (0-1, default 0.6). - include (optional, string): CSV subset of `merged`, `similar`, `developer` (default: all three). Naming a subset TRIMS which blocks are computed; an unknown member or an empty value is a 400. - Constraint: one of title, google_play_id, app_store_id (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/prism/apps-lookup" \ --data-urlencode "title=Notion" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /prism/org-radar A GitHub org's footprint: its top repos each expanded into a full dossier (releases, issue load, top request/complaint), rolled up. Credit cost: 6-51 (metered): 1 credit for the org read plus 5 per repository expanded into a dossier. The default of 5 repos is 26 credits, a single repo is 6, and the 10-repo maximum is 51. Set `repos` to bound it before calling Parameters: - org (required): The GitHub org login or a github.com/{org} URL. - repos (optional, integer): How many top repos to dossier (1-10, default 5): drives the metered price. - sort (optional, enum: stars | updated | pushed): Repo ranking: stars (default), updated, or pushed. - include (optional, string): CSV subset of `dossiers`, `rollup` (default: both). Naming a subset TRIMS which blocks are computed; an unknown member or an empty value is a 400. Dropping `dossiers` also drops the per-repo credit (`1 + 5 x repos` becomes `1`). ``` curl -G "https://www.socialcrawl.dev/v1/prism/org-radar" \ --data-urlencode "org=vercel" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /prism/creator-vet Vet a creator before partnering: engagement quality, commenter authenticity, posting cadence, and controversy signals, optionally across platforms. Credit cost: 50-75 (metered): 50 credits for the standard vet. `include=cross_platform` raises it to 75 for the cross-platform identity check on top Parameters: - handle (required): The creator handle to vet. - platform (optional, string): Primary platform (tiktok/youtube/instagram). - depth (optional, string): Set `deep` to widen the post + commenter sample. - include (optional, string): Set `cross_platform` to add the universal cross-platform presence leg (+25cr). ``` curl -G "https://www.socialcrawl.dev/v1/prism/creator-vet" \ --data-urlencode "handle=mkbhd" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /prism/adverse-screen Screen a person's public social profiles for adverse posts, with every flag citing its post, for human review. (oneOf: linkedin|twitter|facebook|instagram|threads|tiktok) Credit cost: 25-75 (metered): 25 credits for one profile, plus 10 for each further profile (4 profiles cost 55). Profiles that could not be screened are refunded their share, and a call that screened nothing keeps 5 Parameters: - linkedin (optional, string): LinkedIn profile URL (linkedin.com/in/...) or the profile slug. - twitter (optional, string): X (Twitter) handle or profile URL. - facebook (optional, string): Facebook profile or page URL, or its username. - instagram (optional, string): Instagram handle or profile URL. - threads (optional, string): Threads handle or profile URL. - tiktok (optional, string): TikTok handle or profile URL. - name (optional, string): The person's full name. When set, each profile is checked against it before screening. - employer (optional, string): Optional current employer, used only for the identity check. - city (optional, string): Optional city or country, used only for the identity check. - categories (optional, string): Comma-separated subset of fraud_or_financial_crime, violence_or_threats, hate_or_harassment, drugs, sanctions_or_terror_links, sexual_misconduct, reputational_other. Default: all. An unknown member is a 400. - since (optional, string): Only screen posts published on or after this date (YYYY-MM-DD). - limit (optional, integer): Most recent posts screened per profile, 1 to 100 (default 50). One timeline page is read per profile. - screen_unconfirmed (optional, string): Set true to also screen profiles whose identity is possible or could not be judged. Profiles judged to be someone else are never screened. - Constraint: one of linkedin, twitter, facebook, instagram, threads, tiktok (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/prism/adverse-screen" \ --data-urlencode "twitter=elonmusk" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /prism/find-accounts Turn a person's or a company's name into ranked candidate accounts on Instagram, TikTok, X and LinkedIn (Threads and YouTube on request), with a match level and a match confidence per platform, for human review. Credit cost: 2-29 (metered): The searches at their own prices (1 credit each for Instagram, TikTok and X, 10 for LinkedIn) plus 1 credit per platform judged: 6 credits for a person by default, 17 for a company (LinkedIn included). Threads or YouTube, when named, is 1 credit for its search plus 1 for its judging. With profile_check=true each profile lookup is added at its own price (1 credit on Instagram, TikTok and Threads, 5 for a company page on LinkedIn). For a company with `website`, a LinkedIn search that fails is answered by `linkedin/company/by-domain` at its 5 credits instead of the search's 10, and nothing when it fails too. A search that fails, a platform that could not be judged and a lookup that was not made or failed are refunded Parameters: - name (required): The person's or the company's name, 2 to 120 characters. - type (optional, enum: person | company): `person` (default) or `company`. Sets the default platforms and the LinkedIn search used. - platforms (optional, string): Comma-separated subset of linkedin, instagram, tiktok, twitter, threads, youtube. Default: instagram, tiktok, twitter for a person; linkedin, instagram, tiktok, twitter for a company. Threads and YouTube are searched only when named. An unknown member is a 400. - employer (optional, string): Optional current employer of the person, used only as context for the judgment (type=person only). - location (optional, string): Optional city or country, used only as context for the judgment. - website (optional, string): Optional official website or domain (e.g. ogilvy.com). A platform-verified account linking to it is taken as the match without judging. - candidates (optional, integer): Accounts judged per platform, 1 to 5 (default 5), taken from the top of each search. With `website`, an account whose own website is that domain is always included. - profile_check (optional, boolean): Optional, default false. When true, the leading candidate on Instagram, TikTok, Threads and (for a company) LinkedIn is looked up on its profile before the check, so the domain check reads its bio and website: 1 credit a lookup (5 for a LinkedIn company page), refunded when it fails. ``` curl -G "https://www.socialcrawl.dev/v1/prism/find-accounts" \ --data-urlencode "name=Ogilvy" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /prism/mentions Public posts and comments that mention a handle or contain a link, from the first page of each search that can find them. (oneOf: handle|url|name) Credit cost: 1-9 (metered): 1 credit for each X or Reddit page, 5 for an Instagram tag page, 1 for a web page. A default call holds 3. A page with no verified row is refunded. Facebook, Threads and TikTok are not searched. Parameters: - handle (optional, string): Account handle, with or without @. Checked in code: an @handle (u/handle on Reddit) or a link to its profile or one of its posts. Not combined with name. - url (optional, string): A link or a link prefix, such as example.com/news. Checked in code. Not combined with name. - name (optional, string): A display name, 2 to 120 characters, passed on its own. Each row is judged. A failed judgment keeps the rows. - platforms (optional, string): Comma-separated subset of twitter, reddit, facebook, threads, instagram, tiktok, web. Default twitter, reddit, facebook. Instagram needs a handle. Web needs a url. Facebook, Threads and TikTok are accepted and not searched. - since (optional, string): Only keep rows published on or after this date, YYYY-MM-DD. On X it is also part of the search. - include_self (optional, boolean): When true, keep rows whose author is the handle. Default false. - Constraint: one of handle, url, name (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/prism/mentions" \ --data-urlencode "handle=nasa" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /prism/brief-check Check a creator post against a campaign brief: talking points, forbidden claims, and disclosure. Credit cost: 15 credits per post (provisional). Caption and transcript are pulled; each talking point is scored and quoted. A missing transcript is reported, never treated as a pass. Parameters: - url (required): Absolute http(s) URL of one creator post or video. - points (required): CSV of required talking points, for example 'Use the code SAVE20, Taste test on camera'. - forbidden (optional, string): CSV of claims the creator must not make, for example 'Cures disease'. - require_disclosure (optional, enum: 1): Set 1 when a paid-partnership disclosure such as #ad or 광고 is required to pass. - brand (optional, string): Optional brand name used when judging talking points about a product. ``` curl -G "https://www.socialcrawl.dev/v1/prism/brief-check" \ --data-urlencode "url=https://www.tiktok.com/@creator/video/1" \ --data-urlencode "points=Use the code SAVE20, Show the app on camera" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /prism/format-lift Which hook works for this creator, measured against their own posts. Credit cost: 4 credits (provisional). One page of the creator's recent posts is read, and each post is compared with that creator's own median. A bucket with fewer than eight posts lists the count and leaves the median empty. Parameters: - platform (required): youtube, tiktok, or instagram. - handle (required): The creator's handle, without @. ``` curl -G "https://www.socialcrawl.dev/v1/prism/format-lift" \ --data-urlencode "platform=youtube" \ --data-urlencode "handle=joshuaweissman" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /prism/earliness How early a term appeared on each platform, before Trends and before ads. Credit cost: 25 credits (provisional). One date-sorted page per platform, Google Trends over five years, and one page of the TikTok and Meta ad libraries. A lane that does not answer is listed and is not a first-seen date. Parameters: - term (required): The phrase, sound title, hashtag, or trend name to date. - region (optional, string): Optional country code passed to the lanes that accept one, such as US. ``` curl -G "https://www.socialcrawl.dev/v1/prism/earliness" \ --data-urlencode "term=swerve app" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /prism/trend-board What is rising in one country today, with no keyword in: breakout TikTok posts, sounds, hashtags, and Google searches. Credit cost: 30 credits flat (provisional). One call reads TikTok's local feed, TikTok's hashtag board, Google Trending Now, up to 5 author pages and up to 3 sounds. A source that does not answer is listed under unavailable and its section is empty. Only a call where the feed, the hashtag board and Google Trending Now all fail is refunded. Parameters: - country_code (required): ISO 3166-1 alpha-2 country code, such as DE, US or KR. Any country Google Trends Trending Now covers. - period (optional, enum: 7 | 30): 7 (default) or 30. The window of TikTok's hashtag board. Google Trending Now is always read over its longest window, 7 days. - limit (optional, integer): Rows per section, 1 to 10. Default 10. ``` curl -G "https://www.socialcrawl.dev/v1/prism/trend-board" \ --data-urlencode "country_code=DE" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /prism/audience-language Language mix of the people commenting on a creator's recent posts, beside the follower sample on TikTok. Credit cost: 2-26 (metered): Floor 2 credits (one YouTube post: the channel list plus one comment page). Each further comment page is 1 credit, or 5 on Instagram, up to 5 posts. TikTok also opens the audience sample (5) and one follower page (1). Unopened pages are refunded. Five Instagram posts is 26 credits. Parameters: - platform (required): tiktok, instagram or youtube. - handle (required): Creator handle, with or without @. - posts (optional, integer): How many recent posts to open comments for. 1 to 5. Default 5. ``` curl -G "https://www.socialcrawl.dev/v1/prism/audience-language" \ --data-urlencode "platform=tiktok" \ --data-urlencode "handle=charlidamelio" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /prism/korea-gap What the world is talking about that Korea isn't (and vice versa): the global vs Korean (Naver) conversation gap for a brand/topic. Credit cost: 15-40 (metered): 15 credits for the web-only read. The default `include` carries the social leg, which makes the call 40; pass an `include` without `social` to stay at 15 Parameters: - query (required): The brand/topic to compare across the global and Korean conversations. - include (optional, string): CSV subset of `social` (the cross-platform everywhere leg) and `quotes` (the LLM translation pass), default both. An unknown member is a 400. Note the price runs the OTHER way from `video-intel` and `creator-vet`: DROPPING `social` moves the call from 40 credits to the 15-credit web-only variant. - date_from (optional, string): Window start (YYYY-MM-DD). - date_to (optional, string): Window end (YYYY-MM-DD). - display (optional, integer): Naver results per corpus. ``` curl -G "https://www.socialcrawl.dev/v1/prism/korea-gap" \ --data-urlencode "query=Stanley cup" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /prism/share-of-voice Engagement-weighted Share of Voice across 2-5 brands, with web+social split, emotion overlay, and ESOV. Credit cost: 20-200 (metered): 40 credits per brand with the social leg, which is the default, or 20 per brand for the web-only read. Up to 5 brands per call, so the 200-credit ceiling is 5 brands with social and dropping `social` from `include` halves it. Price the call as `brands x per-brand` before you send it - this is the most expensive composite on the API Parameters: - brands (required): 2-5 competitor brand names (CSV). - category_code (optional, string): Numeric category-taxonomy code for true-share-of-category (look one up with `GET /v1/content_analysis/categories`). - market_shares (optional, string): JSON map of real market share per brand (fraction or %), e.g. {"notion":0.4,"coda":0.25,"airtable":0.35}, to compute ESOV. - include (optional, string): CSV toggles (default `emotions,social`). Drop `social` for the cheaper web-only variant; drop `emotions` to skip the sentiment leg. The emotion overlay is third-party NLP passed through unmodified and is not scoped to your date window; `methodology.sentiment_provenance` states that on every response that carries it. - page_type (optional, enum: ecommerce | news | blogs | message-boards | organization): Optional surface filter forwarded to the content_analysis legs. - date_from (optional, string): Window start (YYYY-MM-DD). Defaults to 90 days ago. - date_to (optional, string): Window end (YYYY-MM-DD). Defaults to today. - brand_descriptions (optional, string): Optional JSON map of a one-line description per brand, e.g. {"notion":"the workspace app"}, used to tell a brand apart from its namesakes. ``` curl -G "https://www.socialcrawl.dev/v1/prism/share-of-voice" \ --data-urlencode "brands=notion,coda,airtable" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /prism/review-integrity Cross-source review integrity verdict (statistical, deterministic). (oneOf: query|asin|gid) Credit cost: 30 (override; tier premium) Parameters: - query (optional, string): Product or brand name to evaluate. - asin (optional, string): Amazon ASIN to anchor the Amazon axis directly (skips product-search). - gid (optional, string): Google product id (gid) to anchor the Google Shopping axis (NOT the catalogid product.id). - sources (optional, string): CSV subset of amazon,google_shopping,trustpilot,web,forums (default all). - country (optional, string): Marketplace country, as a location name or numeric code (look one up with `GET /v1/content_analysis/locations`). Defaults to United States. - Constraint: one of query, asin, gid (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/prism/review-integrity" \ --data-urlencode "query=AirPods Pro 3" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /prism/answers Multi-engine AI consensus: one question → Perplexity + Grok + Tavily answers verbatim, merged citations, and an agreement matrix. Credit cost: 15 (override; tier premium) Parameters: - query (required): The question, forwarded verbatim to every engine. - engines (optional, string): CSV subset of perplexity,grok,tavily (default all three). - include (optional, string): CSV of optional grounding legs: `polymarket` adds market probabilities (trimmed). ``` curl -G "https://www.socialcrawl.dev/v1/prism/answers" \ --data-urlencode "query=will the fed cut rates in september" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /prism/video-intel One video URL → detail + stats + transcript + top comments + commenter sample, across YouTube/TikTok/Rumble/Instagram. Credit cost: 5-15 (metered): 5 credits for the video detail, stats and comments. `include=transcript` adds 10, which is exactly what the standalone transcript endpoint charges, so the bundle never costs more than running the two calls yourself Parameters: - url (required): Absolute http(s) URL of a YouTube, TikTok, Rumble, or Instagram video. - comments (optional, string): How many top comments to fetch (0-50, default 20). `0` skips the comments leg. - include (optional, string): CSV of optional costed legs: `transcript` (adds the dedicated transcript leg, +10cr, refunded when null) and/or `commenter_profiles` (≤3 commenter mini-profiles, TikTok/Instagram in v1). - app_demand (optional, boolean): Optional. 1 reads the caption first, then the comments, and returns data.app_demand: late when the caption already names an app, otherwise the count of comments that want an app they do not have, the count that name one, and that ratio. The ratio is withheld unless the comments support it. A low-confidence comment is not counted. No extra credits. - claim (optional, string): Optional sentence. When set, data.sense_check says whether the comments support it, contradict it, or say nothing. A confidence under the cut, or one high-confidence contradiction, withholds the sentence. No extra credits. ``` curl -G "https://www.socialcrawl.dev/v1/prism/video-intel" \ --data-urlencode "url=https://www.youtube.com/watch?v=dQw4w9WgXcQ" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /prism/voice One person's public posts across X, Threads, Bluesky, and Truth Social, time-merged. Credit cost: 5 (override; tier advanced) Parameters: - handle (required): The handle to look up across all four microblogs (a single leading @ is stripped). - platforms (optional, string): CSV subset of twitter,threads,bluesky,truthsocial (default all four). - cursor (optional, string): Opaque composite cursor from a prior response's `next_cursor`. X/Twitter pages; Truth Social pages on `next_max_id`; a mangled token is a 400 at 0 credits. - include (optional, string): CSV subset of posts_by_platform,merged_timeline,computed to trim the payload (posts_by_platform is always returned). ``` curl -G "https://www.socialcrawl.dev/v1/prism/voice" \ --data-urlencode "handle=nasa" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /prism/app-reviews Cross-store app review intelligence (Google Play + App Store): translated, clustered, sentiment-scored. (oneOf: google_play_id|app_store_id|query) Credit cost: 10-15 (metered): 10 credits for one store, 15 for both. A bare `query` resolves both stores and costs 15; naming a single store in `stores`, or passing only one of `google_play_id` / `app_store_id`, costs 10 Parameters: - google_play_id (optional, string): Google Play package name (e.g. com.spotify.music). - app_store_id (optional, string): App Store numeric app id (e.g. 324684580). - query (optional, string): App name to auto-resolve the top hit on each requested store (when no id is given). - country (optional, string): Storefront country, as a location name or numeric code (look one up with `GET /v1/app_store/locations` or `GET /v1/google_play/locations`). Defaults to United States. - language (optional, string): Language code (e.g. en). - depth (optional, integer): Reviews per store (default 150 Google / 50 Apple, max 600). The App Store returns ~50 at depth 40. - stores (optional, string): CSV subset of google_play,app_store. Defaults to whichever ids/query resolve. - include (optional, string): CSV of topics,sentiment_timeline,feature_requests,responses (default all). Omitting topics+feature_requests skips the LLM step. - Constraint: one of google_play_id, app_store_id, query (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/prism/app-reviews" \ --data-urlencode "query=spotify" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /prism/creator-card One handle, unified author cards across TikTok, Instagram, YouTube, X (and more). Credit cost: 5-8 (metered): 5 credits covering any 4 platforms, plus 1 credit per platform beyond 4. The default selection is 4 platforms, so the 7-platform maximum is 8 credits Parameters: - handle (required): The handle to look up across every requested platform (a single leading @ is stripped). - platforms (optional, string): CSV subset of tiktok,instagram,youtube,twitter,threads,bluesky,truthsocial (default the first four). Unknown platforms are ignored. - include (optional, string): CSV subset of cards,totals to trim the payload (cards is always returned). - verify (optional, boolean): Send `true` to add an `identity` block that says whether each found profile is the same person or organisation as the anchor profile, not only that the handle exists there. Each platform gets a verdict with `level` (`same`, `possible`, `different`, or `uncertain` when the answer is not confident), a `score`, a `confidence` and the `evidence` behind it (`cross_link`, `bio_consistent`, `fan_or_parody`, `website_match`). A `null` verdict means that profile was not judged, not that it is different. `identity.verified_on` lists the anchor plus every `same` platform, and with it `totals.combined_followers` adds only those platforms while `totals.unverified_followers` holds the followers of the found profiles not judged `same`. Public profile text only, no image comparison. Same price; adds well under a second. - anchor (optional, enum: tiktok | instagram | youtube | twitter | threads | bluesky | truthsocial): With `verify=true`, the platform whose profile you already trust; every other found profile is compared against it. Omit it and the anchor is the platform-verified profile with the largest audience. If the chosen platform has no card in the response the anchor is picked automatically and `_warnings` says so. ``` curl -G "https://www.socialcrawl.dev/v1/prism/creator-card" \ --data-urlencode "handle=mrbeast" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /prism/handle-audit Should you pull this handle? One call scores a handle across platforms, ranks the best ones, and projects the data volume + credit cost to pull it. Contract overview: Decide whether to pull a handle before paying for a full collection. Score model 2.0.0 returns nullable top-level and per-platform scores, explicit evidence and component coverage, surface estimates, and `legs[]` diagnostics. Unknown numeric values are `null`, not zero. Output is deterministic only for the same (`score_version`, `evidence_fingerprint`) and returned evidence; a later live collection may produce different evidence and a different score. Stale-if-error evidence is eligible for at most 24 hours, and future-dated evidence more than 300 seconds ahead is rejected. 5 credits for any selection of up to 4 supported platforms; +1 credit per selected platform beyond 4. Every successful HTTP 200 receives the full request-shaped charge with no partial refund. A non-200 execution failure receives a full refund when every requested platform is a definitive miss or cannot return usable profile evidence. Scores are advisory estimates from public evidence, not calibrated probabilities. Credit cost: 5-8 (metered): 5 credits for any selection of up to 4 supported platforms; +1 credit per selected platform beyond 4 Cache: An exact normalized request is cached for 30 minutes; an exact cache hit costs 0 credits. Contract: Supported platforms are tiktok, instagram, youtube, twitter, threads, bluesky, truthsocial. Default platforms are tiktok, instagram, youtube, twitter. Optional platforms are threads, bluesky, truthsocial. Reddit is deferred and not supported. Contract: Any unknown platform rejects the whole request with HTTP 400 before billing. Contract: The identity may be a bare handle or canonical supported profile URL. A YouTube channel ID is YouTube-only: omit `platforms` to audit YouTube or set `platforms=youtube`; other canonical profile URLs do not narrow the default fanout. Contract: YouTube sampled-post collection sends includeExtras=true. Contract: Profile evidence states are live, stale, not_found, and unavailable. Posts evidence states are sampled, observed_empty, not_supported, not_found, and unavailable. Stale platforms are also listed in `stale_platforms`. Unknown numeric values are null, not zero, and decision-grade post evidence is required for scoring. Contract: When a platform's profile evidence is not_found, its `not_found_reason` says why: account_gone (removed, renamed, or never existed), account_private (the account exists but is not publicly readable), or handle_unresolved (no source stated a cause), the same vocabulary the profile endpoints publish as `error.details.reason`. `not_found_reason` is null on every platform whose profile was found or not observed. It is reported only and never changes the score or verdict. Contract: `observed_empty` is decision-grade evidence of an empty posting history; `days_since_last_post`, `media_fraction`, `avg_text_length`, and the `content_richness` score stay null because no posts were observed. Contract: Explicitly empty text is observed as length 0 and explicitly empty media is observed as false; unknown text or media stays null. Content-richness uses known-only inputs and re-normalizes across the known inputs instead of treating unknowns as zero. Contract: `legs[]` is emitted in stable plan order. A genuinely pending leg abandoned at the global deadline is synthesized as 504 / UPSTREAM_TIMEOUT / 0 credits; an early rejected dispatch is 502 / UPSTREAM_ERROR / 0 credits, including when compatible stale evidence recovers the response. A terminal profile failure emits no speculative posts leg. Contract: Scores are reproducible only when `score_version` and `evidence_fingerprint` are unchanged; score and evidence fields can be null when decision-grade evidence is unavailable. Contract: Collection has a 6.4-second default budget, a 6.5-second hard cap, and a 7-second public API target. These are configured targets, not measured v2 performance. Contract: Process-local monotonic last-known-good ordering prevents older writers within one process; global atomic CAS remains future work. Contract: With `relevant_to`, up to 25 of the posts already sampled (live evidence only, taken in turn across platforms) are judged against your subject by the same published relevance rule as `relevance=filter`. `domain_relevance.score` is the share kept, 0 to 100, with the posts judged listed in `judged_posts`. Contract: `score_with_domain_relevance` is 0.7 x `score` + 0.3 x `domain_relevance.score`, and `verdict_with_domain_relevance` applies the same thresholds to it. `score`, `verdict`, the five base components, their weights and `evidence_fingerprint` never read the subject. Contract: `domain_relevance` is null with a `_warnings` entry when fewer than 3 posts could be judged (`domain_relevance_insufficient_sample`) or the judging step did not answer (`domain_relevance_unavailable`). It is a judged estimate to review, not a measurement. Without `relevant_to` none of these fields is present. Parameters: - handle (required): The identity to audit. Accepts a bare handle, a leading @, a supported profile URL, or a YouTube channel ID. A YouTube channel ID is YouTube-only: omit `platforms` or set `platforms=youtube`. - platforms (optional, string): CSV subset of tiktok,instagram,youtube,twitter,threads,bluesky,truthsocial (default: tiktok,instagram,youtube,twitter). Names are case-insensitive and deduplicated. Unknown platforms are rejected. At most 8 CSV members before deduplication. - sample (optional, integer): Integer from 1 to 25: recent posts sampled per found platform for engagement and activity metrics (default 10). - relevant_to (optional, string): Optional. Your subject in up to 200 characters, for example relevant_to=video games. Name ONE subject: `A and B` is read as posts about both. Adds domain_relevance (the share of sampled posts about it) and score_with_domain_relevance next to the unchanged score. No extra charge. ``` curl -G "https://www.socialcrawl.dev/v1/prism/handle-audit" \ --data-urlencode "handle=mrbeast" \ -H "x-api-key: YOUR_API_KEY" ``` ### POST /prism/post-stats Up to 100 mixed-platform post URLs → current engagement per URL, failed URLs refunded. Credit cost: 1-500 (metered): 1 credit per successful URL on most platforms, 2 on Instagram and 5 on LinkedIn, charged at each URL's own platform rate. Up to 100 URLs per call: the call holds the summed worst case up front (a 100-URL Instagram batch holds 200) and refunds every dead, errored and unsupported URL, so you pay for exactly the rows that returned counts Parameters: - urls (required): JSON array of 1-100 absolute http(s) post URLs (mixed platforms allowed). - include (optional, string): CSV subset of views,likes,comments,shares,saves,reposts to trim each row's engagement block. ``` curl -X POST "https://www.socialcrawl.dev/v1/prism/post-stats" \ -H "x-api-key: YOUR_API_KEY" \ -H "content-type: application/json" \ -d '{"urls":["https://www.youtube.com/watch?v=dQw4w9WgXcQ"]}' ``` ### POST /prism/comment-lookup Re-check up to 25 known comments in one call: per-item results, failed items refunded. Credit cost: 2-100 (metered): 2 credits per found TikTok item and 5 per found Instagram item, raised by `deep_scan`. Up to 25 items per call: the whole batch holds at most 100 credits up front and refunds every not_found, errored and deferred item Parameters: - items (required): JSON array of 1-25 lookup items. Each item is `{ "comment_url": "…" }` or `{ "platform": "tiktok"|"instagram", "post_url": "…", "comment_id": "…" }`, optionally with `parent_comment_id`, `position_hint`, and `deep_scan`. ``` curl -X POST "https://www.socialcrawl.dev/v1/prism/comment-lookup" \ -H "x-api-key: YOUR_API_KEY" \ -H "content-type: application/json" \ -d '{"items":[{"comment_url":"https://www.tiktok.com/@mrbeast/video/7654638524729216287?comment_id=7654640784985211670"},{"platform":"instagram","post_url":"https://www.instagram.com/p/CnpPou9hWqq/","comment_id":"18007013966365752"}]}' ``` ### POST /prism/profiles Up to 50 (platform, handle) pairs → one canonical Author per row, failed handles refunded. Credit cost: 1-126375 (metered): 1 credit per successful row on most platforms and 5 on LinkedIn, charged at each handle's own platform tier. Up to 50 items per call: the call holds the summed worst case up front and refunds every not_found, unsupported, errored and deferred row. With include: "posts" (up to 25 items) a row also costs its post list page, 1 credit (5 on LinkedIn), charged only when that page came back with posts. With posts_limit the call walks the list and charges each page that came back with posts; it holds the most pages the limit can take (10 at most per handle) and refunds the unused ones, and a dry run shows that hold. With include: "comments" each selected post's comments cost what that platform's comment endpoint charges per page (1 credit on most platforms, 5 on Instagram, LinkedIn and Reddit), charged only for pages that came back with comments; the call holds comments_top_posts times the pages comments_per_post can take and refunds the rest Parameters: - items (required): JSON array of 1-50 items (1-25 with include: "posts"). Each item is `{ "platform": "tiktok", "handle": "@scout2015", "custom_id"?: "…", "stop_at_id"?: "…" }`. `handle` accepts an @handle, a bare handle, or a pasted profile URL. `stop_at_id` (only with include: "posts") is the id or URL of the newest post you already hold for that handle: its page stops just before it. - include (optional, string): Optional. A comma-separated set of "posts" and "about". "posts" adds the handle's first page of posts to every ok row (`posts`), for the price of that page (1 credit), charged only when the page came back with posts. Caps the batch at 25 items. "about" reads an Instagram handle through its About panel (same price) and adds `country` to a row when the account declares one (an Instagram country, a Facebook page address, a TikTok account region, or a YouTube channel country), as an ISO code with its `source`; beside "posts" it also adds `activity` and `stats`, computed from the posts held. "comments" implies "posts" and reads the comments of each handle's top posts (see comments_top_posts), priced per page at that platform's comment endpoint price. - since (optional, string): Optional, with include: "posts". Only posts published on or after this date (YYYY-MM-DD or an ISO 8601 timestamp); each page stops at the first older post and `posts.stopped_at` is `since`. - posts_limit (optional, integer): Optional, with include: "posts". An integer from 1 to 100: walk each handle's post list until that many posts are held (each page that came back with posts is charged, up to 10 pages per handle). Without it, one page. - comments_top_posts (optional, integer): Optional, with include: "comments". An integer from 1 to 50 (default 3): how many of each handle's walked posts, those with the most comments, get their comments read. - comments_per_post (optional, integer): Optional, with include: "comments". An integer from 1 to 200 (default 20): the most comments read per selected post, walked page by page on that platform's comment endpoint. - comments_select_by (optional, enum: comments | views): Optional, with include: "comments". "comments" (default) picks the posts with the most comments, then posts without a comment count by views; "views" picks the most viewed posts. ``` curl -X POST "https://www.socialcrawl.dev/v1/prism/profiles" \ -H "x-api-key: YOUR_API_KEY" \ -H "content-type: application/json" \ -d '{"items":[{"platform":"tiktok","handle":"@scout2015"},{"platform":"linkedin","handle":"williamhgates","custom_id":"vet-1"}]}' ``` ### POST /prism/post-comments Up to 25 post URLs across platforms → each post's comments in one call, priced per page like the comment endpoints. Credit cost: 1-2500 (metered): Each post costs what its own comment endpoint charges per page (1 credit on TikTok, YouTube, Facebook, X and Threads; 5 on Instagram, LinkedIn and Reddit), for the pages that came back with comments. The call holds every post at the pages per_post can take and refunds the rest; unsupported, not found, failed and deferred posts cost nothing. With since, a post with no new comment costs nothing Parameters: - items (required): JSON array of 1-25 post URLs. Each item is a URL string or `{ "url": "https://…", "custom_id"?: "…" }` (custom_id up to 128 characters, echoed on its row). `urls` is accepted as an alias. - per_post (optional, integer): Optional. An integer from 1 to 200 (default 20): the most comments read per post, walked page by page on that platform's comment endpoint. - since (optional, string): Optional. Only comments published on or after this date (YYYY-MM-DD, midnight UTC) or ISO 8601 timestamp. A post whose comments are all older costs nothing. ``` curl -X POST "https://www.socialcrawl.dev/v1/prism/post-comments" \ -H "x-api-key: YOUR_API_KEY" \ -H "content-type: application/json" \ -d '{"items":["https://www.tiktok.com/@mrbeast/video/7654638524729216287","https://www.youtube.com/watch?v=dQw4w9WgXcQ"]}' ``` ### POST /prism/discover/inputs Store a long author list or exclude list once and reuse it by id. Credit cost: Free. Parameters: - kind (required): "authors" or "exclude". - authors (optional, string): With kind authors: JSON array of 1 to 10,000 handles (each at most 100 characters): bare handles, @handles, profile URLs or platform:handle account keys. - exclude (optional, string): With kind exclude: JSON array of 1 to 50,000 evidence ids, post URLs or account keys (each at most 200 characters), in a body under 4,000,000 bytes. ``` curl -X POST "https://www.socialcrawl.dev/v1/prism/discover/inputs" \ -H "x-api-key: YOUR_API_KEY" \ -H "content-type: application/json" \ -d '{"kind":"authors"}' ``` ### POST /prism/jobs Run a batch or any GET endpoint as a background job: up to 5,000 profiles or post URLs, or 1,000 calls to one GET. Credit cost: The same price per row as the batch endpoint the job runs: prism/profiles (1 credit per profile on most platforms, 5 on LinkedIn, plus its posts pages and comment pages as on prism/profiles) or prism/post-stats (its per-URL price). The call holds the whole job's worst case up front, answered in credits_used on the 202, and refunds every row that is not ok when the job completes. A job of a GET endpoint holds nothing (credits_used 0 on the 202, `billing: per_call`): each item is billed as that GET when it runs, a cached, failed or not-found item costs what the GET would, and `credits_max` on the 202 is the most the job can cost. Reading the job is free. Parameters: - endpoint (required): The batch endpoint each chunk runs, "prism/profiles" or "prism/post-stats", or the path of any GET endpoint, such as "youtube/channel/about" (a leading /v1/ is accepted). - items (required): JSON array of 1 to 5,000 items in that batch endpoint's item shape, or 1 to 1,000 objects of a GET endpoint's query parameters. Rows come back with `index`, their position in this array. - include (optional, string): Optional, prism/profiles only. A comma-separated set of "posts", "about" and "comments", as on prism/profiles: "posts" adds each handle's posts to its row and "comments" (it implies posts) the comments of its top posts. With posts or comments the chunks hold 25 items. - since (optional, string): Optional, prism/profiles with include: "posts" only. Only posts published on or after this date (YYYY-MM-DD or an ISO 8601 timestamp). - posts_limit (optional, integer): Optional, prism/profiles with include: "posts" only. An integer from 1 to 100: walk each handle's post list until that many posts are held, as on prism/profiles. - comments_top_posts (optional, integer): Optional, prism/profiles with include: "comments" only. An integer from 1 to 50 (default 3): how many of each handle's posts, those with the most comments, get their comments read. - comments_per_post (optional, integer): Optional, prism/profiles with include: "comments" only. An integer from 1 to 200 (default 20): the most comments read per selected post. - comments_select_by (optional, enum: comments | views): Optional, prism/profiles with include: "comments" only. "comments" (default) or "views": how the posts whose comments are read are picked. - webhook (optional, string): Optional. `{ "url": "https://…" }`, a public https URL, called once when the job completes. The signing secret is returned once, on the 202. ``` curl -X POST "https://www.socialcrawl.dev/v1/prism/jobs" \ -H "x-api-key: YOUR_API_KEY" \ -H "content-type: application/json" \ -d '{"endpoint":"prism/profiles","items":[{"platform":"tiktok","handle":"@scout2015"},{"platform":"instagram","handle":"nasa"}]}' ``` ### GET /prism/jobs List the API key's background jobs, newest first. Credit cost: 0 (override; tier standard) Parameters: - cursor (optional, string): The previous response's next_cursor. ``` curl "https://www.socialcrawl.dev/v1/prism/jobs" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /prism/jobs/{job_id} Status and results of one background job. Credit cost: 0 (override; tier standard) Parameters: - job_id (required): The job id (bj_…) returned by POST /v1/prism/jobs as data.job_id. - cursor (optional, string): The previous response's next_cursor (a chunk position). ``` curl "https://www.socialcrawl.dev/v1/prism/jobs/bj_3kP9x2LmQ7" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /prism/investigate A bounded read-only walk over SocialCrawl endpoints that turns a research goal into a trail of searches, profiles and comments. Credit cost: Holds `budget` credits (default 40, max 200) up front and refunds what the walk does not spend. Each underlying page is charged at its own price. Deciding the next step is free. Parameters: - goal (required): What you want to find, in one or two sentences (max 500 chars). - budget (optional, integer): Credits held up front for the walk (1-200, default 40). Unused credits are refunded. - max_steps (optional, integer): Most steps the walk may take (1-12, default 8). ``` curl -G "https://www.socialcrawl.dev/v1/prism/investigate" \ --data-urlencode "goal=Find Korean skincare creators on TikTok whose commenters ask where to buy" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /prism/discover Search many platforms for keywords or a description, keep a date window, judge every row and return the posts, in one call or as a background run. (oneOf: keywords|query|brands|brand|hashtags|contains|seeds|authors|author_list_id|mentions_of|brand_handles|cursor|continue|view|mode) Credit cost: 0-10000000 (metered): Each platform search is billed at that platform's own search price, only when it returns rows; a search that fails, finds nothing or is served from cache costs 0. Judging against your keywords is free; judging against a different context costs 1 credit per started 15 rows, and each label costs 1 credit per started 25 rows. max_credits (default 50 on this call) caps the whole run, judging, joins and page bodies included, and a request expected to cost more is refused free before anything is spent. A search still running when the call answers bills when it finishes, after the answer: credits_used leaves it out and data.run.credits.pending shows the most it can add. Parameters: - keywords (optional, string): Comma-separated searches (up to 6 run in the call; more continue in the background). Each runs once on each platform. - query (optional, string): One search phrase, never split on commas. - context (optional, string): What kept rows must be about, 3 to 2,000 characters. A persona or a question goes here, never in keywords. - brands (optional, string): Comma-separated brands; each is its own group, searched by its name and labelled with the mention judgment. - brand (optional, string): One brand (an alias for a one-item brands). - platforms (optional, string): Comma-separated platforms, in order: tiktok, instagram, youtube, reddit, twitter, threads, facebook, linkedin, pinterest, bluesky, hackernews, forums, quora, github, news, web, telegram, naver, xiaohongshu, douyin, ebay, amazon, aliexpress, gumtree, tiktokshop, walmart. - preset (optional, string): A named platform set instead of platforms: consumer (default), b2b, travel, apps, news, commerce, creators, korea. - period (optional, string): Nd (1 to 365), yesterday, this_week, last_week, this_month, last_month, last_quarter, this_year or ytd. - since (optional, string): First day kept, YYYY-MM-DD (inclusive). Not combined with period. - until (optional, string): Last day kept, YYYY-MM-DD (inclusive). Not combined with period. - from (optional, string): Alias for since. - to (optional, string): Alias for until. - tz (optional, string): IANA time zone of the window's calendar (default UTC). - view (optional, string): posts (default), accounts, communities or items. - limit (optional, string): Kept rows returned per group (default 50, at most 100 in one call). - max_credits (optional, string): The most the run may spend (default 50 on this call). The run pauses at it and never overspends. - depth (optional, string): quick (at most 3 fast platforms, 15 s), standard (default) or deep (background). - dry_run (optional, string): true answers the plan of searches and the estimate of credits and seconds, free, and starts nothing. - country (optional, string): ISO 3166 codes: keep rows whose row or author country is one of these; also picks the news edition. - exclude_country (optional, string): ISO 3166 codes: drop rows from these countries. - unknown_country (optional, string): keep (default) or drop rows whose country is unknown, when country is sent. A geo.country guessed with confidence below 0.3 (from language or currency, say) counts as unknown, so a kept row can show such a guess for another country. - near (optional, string): The place the listings, places and web searches look in: a place name or lat,lng. It filters no other rows. - languages (optional, string): ISO 639 codes: keep rows in these languages and plan searches in them. Malay and Indonesian count as one language (ms keeps id rows and id keeps ms rows); rows with no detected language are kept. - min_views (optional, string): Drop rows with fewer views. - min_likes (optional, string): Drop rows with fewer likes. - min_comments (optional, string): Drop rows with fewer comments. - min_followers (optional, string): Drop rows whose author has fewer followers. - max_followers (optional, string): Drop rows whose author has more followers. - verified (optional, string): true keeps only rows by verified authors. - media_type (optional, string): Keep only rows showing these media: video, image, carousel or text. - active_within_days (optional, string): Keep authors who posted within this many days. - link_domains (optional, string): Keep only rows linking to these domains. - exclude_terms (optional, string): Drop rows containing any of these terms. - contains (optional, string): Comma-separated strings or URL prefixes a kept row must contain; without keywords they are searched. - undated (optional, string): keep (default; counted apart) or drop rows with no date. - estimated_dates (optional, string): keep (default), undated or drop rows whose date the platform did not publish and the lane estimated. - outside_window (optional, string): drop (default) or keep rows dated outside the window, marked in_window false. Needs a window. - min_text_length (optional, string): Drop rows whose text has fewer characters (0 to 200), before judging. - sites (optional, string): Domains searched on the web source, up to 20, or auto to find the sites that hold the topic; web is added when you send no platforms or preset, and an explicit platform set must include web. Named sites run next to web's own whole-web keyword search; send search_in.web=sites to search only inside them. - relevance (optional, string): filter (default: drop off-topic rows), score (keep every row with its score) or off. - relevance_threshold (optional, string): The relevance score a kept row needs, 0 to 1 (default 0.5); on the POST a group's own relevance_threshold sets it for that group's rows. - label (optional, string): Judgments to add to each row, each billed: mention (needs brands), intent, niche, sponsored_model, stance (judgments.stance { label, confidence } toward the group's brand, else its keywords, else its context; uncertain apart in summary.stance), or none. With view=accounts, fit, role and account_kind judge accounts (the default). - role (optional, string): Accounts only: keep accounts of these roles (seller, reseller, buyer, warner, reviewer, promoter, other); adds the role judgment. - intent (optional, string): Keep only rows with these intents: switching, complaint, recommendation, question, purchase_intent or selling (implies label=intent). - account_kind (optional, string): Accounts only: keep accounts of these kinds (individual_creator, brand_or_business, media_or_publisher, fan_or_repost_page, cannot_tell); adds the account_kind judgment. - targets_country (optional, string): Accounts only: keep accounts that target these countries (ISO 3166); adds its judgment. - sponsored (optional, string): keep (default), exclude or only rows flagged or disclosed as sponsored. - questions (optional, string): Up to 5 questions of your own, as JSON keyed by the answer's id, judged on every account (and every row with view=posts); a question about a sensitive trait of a person is a free 400. - limit_per_keyword (optional, string): Kept rows per keyword (counts in summary.by_keyword). - include (optional, string): Extra data: author_country, body (full text of the top kept articles, pages and threads), raw (each platform's own row), instagram_views, dropped (dropped rows in results.jsonl and state=dropped reads), commenters (with comments=inline), interest (data.interest: how much each keyword was searched over time, per group); with view=accounts, authors, contacts (opt-in) and audience (a TikTok creator's follower aggregates). - body_limit (optional, string): With include=body: rows whose full text is read (default 10; at most 50 on the GET and 5,000 on the POST). - comments (optional, string): off (default), inline (reads the comments under each group's top posts into a comments block on the row, billed in by_stage.comments) or bulk (data.comments_job lists those posts as ready prism/comments calls). - comments.top_posts (optional, string): With comments=inline: posts read per group, 1 to 50 (default 3), taken in turn from each platform and ranked by comment count. - comments.per_post (optional, string): With comments=inline: the most comments read from each post, 1 to 200 (default 20). - comments.select_by (optional, string): comments (default: rank posts by comment count, by views where a platform reports none) or views (rank every post by views). - comments.min_length (optional, string): With comments=inline: drop replies shorter than this many characters (fetched counts them, items holds the kept ones). - comments.labels (optional, string): With comments=inline on view=posts: comment labels (sentiment, spam, question, purchase_intent, complaint, toxic, low_quality, injection), billed at the comment endpoint's label price. - posts_per_account (optional, string): Accounts only: posts read per looked-up account, 1 to 100 (default 12); one call reads at most 12. Sent, every looked-up account's posts are read and returned in its posts array; left out, only when activity is needed. - sort (optional, string): relevance (default), recent, engagement, views or views_per_follower; accounts fit, followers, engagement or recent. - sample (optional, string): representative spreads rows across authors and platforms (with max_per_author, max_per_platform). - max_per_author (optional, string): With sample=representative: the most rows per author. - max_per_platform (optional, string): With sample=representative: the most rows per platform. - format (optional, string): evidence (default), compact or text. - fields (optional, string): Keep only these keys per row (a dotted name keeps one leaf). - author_identity (optional, string): full (default), omit or hash the author on every row. - translate_to (optional, string): Adds text_translated in this language (1 credit per started 1,000 characters). - continue (optional, string): The token in data.next.continue_url: buys the next platform pages of the searches that reported more. - since_watermark (optional, string): The watermark from an earlier run's data.next.watermark: only rows newer than it. - seeds (optional, string): Comma-separated seeds to resolve to their own accounts instead of a topic (implies mode=resolve and view=accounts). Each entry is a domain or URL (notion.so: a company with that domain, named from it), Name:domain (BMW Group:bmwgroup.com), or a name alone (Trevor Noah, searched as a person). A name and a domain written any other way is a free 400 whose did_you_mean is the Name:domain form. - authors (optional, string): Comma-separated handles: keep only rows by these authors (up to 100 here; store longer lists with POST /v1/prism/discover/inputs). - author_list_id (optional, string): The id from POST /v1/prism/discover/inputs, in place of a long authors list. - authors_match (optional, string): filter (default): search and keep the rows by the listed authors; walk: read each listed author's own posts in the window instead, and keep the ones about the subject. - brand_handles (optional, string): A brand's own accounts: a handle or profile URL, optionally after a platform (instagram:zomato) and, with several brands, after the brand (zomato:instagram:zomato). Adds the account's own posts (owned_post) and, on Instagram, its tagged posts. - mentions_of (optional, string): Accounts whose mentions you want (@handle): posts and replies that mention them on X, Reddit and Instagram, by the @handle (u/handle on Reddit) or by a link to the account's profile or one of its posts. - hashtags (optional, string): Comma-separated hashtags searched on each platform's hashtag lane. - search_in (optional, string): Lane kinds per platform: posts (default), hashtags; Reddit and X comments; Naver corpora blog, cafe, kin, news, webkr; web search or sites, discover_sites and walk; a brand's owned posts, Instagram tagged posts, ads and reviews; listings, places and communities; Telegram messages (the search inside the channels its channel search finds). search_in. sets one platform. - review_sites (optional, string): With search_in=reviews: the review sites searched (default trustpilot, google, tripadvisor, app_store, google_play; also yelp, amazon, g2). - mode (optional, string): search (the ordinary keyword run), resolve (seeds resolved to their accounts; seeds imply it) or board (each platform's trend boards for country; view=items with country and no subject implies it). - expand (optional, string): 0 (default) to 3 extra searches planned per keyword per language; true counts as 1 and false as 0. - expand_from (optional, string): With expand above 0: keywords (default: the extra searches are planned from your keywords) or suggestions (also from each keyword's YouTube and TikTok search suggestions, 1 credit each). - sites_auto_max (optional, string): With sites=auto: how many of the sites found are searched, 1 to 20 (default 5). - walk_urls (optional, string): With search_in.web=walk: pages walked per site and keyword, 1 to 500 (default 50). - pages_per_platform (optional, string): Pages per search, 1 (default) to 10; above 3 the run continues in the background. - retention_days (optional, string): Days the run and its rows stay readable, 1 to 30 (default 7). - deadline_s (optional, string): Seconds this call may take, 10 to 96 (default 40, quick 15). - async (optional, string): auto (default: over budget continues in the background), true (always background) or false (422 NEEDS_ASYNC over budget). - fresh (optional, string): true bypasses cached search pages, at full price. - new_run (optional, string): true starts a new run even when an identical one exists. - exclude (optional, string): Evidence ids, post URLs or account keys already held: never returned again. - exclude_list_id (optional, string): The id of an exclude list stored with POST /v1/prism/discover/inputs (kind exclude, up to 50,000 entries): its rows are never returned, with exclude. - cursor (optional, string): pagination.next_cursor of an earlier answer: the next page of that run's stored rows, free. - page_size (optional, string): GET only: stored rows per page with cursor, 1 to 100 (default 50). - Constraint: one of keywords, query, brands, brand, hashtags, contains, seeds, authors, author_list_id, mentions_of, brand_handles, cursor, continue, view, mode (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/prism/discover" \ --data-urlencode "query=electric cars" \ -H "x-api-key: YOUR_API_KEY" ``` ### POST /prism/discover Start a discover run from a JSON body, for groups and lists too long for a URL; it runs in the background and answers 202 with the run. (oneOf: keywords|query|brands|brand|groups|hashtags|contains|seeds|authors|author_list_id|mentions_of|brand_handles|continue|view|mode) Credit cost: 0-10000000 (metered): Nothing is held when the run starts. Each platform search is billed at that platform's own search price when it runs, only when it returns rows, and judging against a context costs 1 credit per started 15 rows and each label costs 1 credit per started 25 rows. max_credits is required and caps the whole run: the run pauses at it and never overspends. Reading the run is free. Parameters: - max_credits (required): Required unless dry_run is true or schedule carries max_credits_per_run: the most the run may spend. The run pauses at it and never overspends; a request expected to cost more is refused free. - keywords (optional, string): JSON array of searches, each run once on each platform. - query (optional, string): One search phrase, never split on commas. - context (optional, string): What kept rows must be about, 3 to 2,000 characters. - brands (optional, string): JSON array of brands; each is its own group, labelled with the mention judgment. - brand (optional, string): One brand (an alias for a one-item brands). - platforms (optional, string): JSON array of platforms, in order: tiktok, instagram, youtube, reddit, twitter, threads, facebook, linkedin, pinterest, bluesky, hackernews, forums, quora, github, news, web, telegram, naver, xiaohongshu, douyin, ebay, amazon, aliexpress, gumtree, tiktokshop, walmart. - preset (optional, string): A named platform set instead of platforms. - period (optional, string): Nd (1 to 365), yesterday, this_week, last_week, this_month, last_month, last_quarter, this_year or ytd. - since (optional, string): First day kept, YYYY-MM-DD (inclusive). - until (optional, string): Last day kept, YYYY-MM-DD (inclusive). - from (optional, string): Alias for since. - to (optional, string): Alias for until. - tz (optional, string): IANA time zone of the window's calendar (default UTC). - view (optional, string): posts (default), accounts, communities or items. - limit (optional, string): Kept rows returned per group (default 500 on a background run). - depth (optional, string): quick (at most 3 fast platforms, 15 s, no expansion), standard (default) or deep (background). - dry_run (optional, string): true answers the plan and the estimate, free, and starts nothing. - country (optional, string): ISO 3166 codes: keep rows whose row or author country is one of these; also picks the news edition. - exclude_country (optional, string): ISO 3166 codes: drop rows from these countries. - unknown_country (optional, string): keep (default) or drop rows whose country is unknown, when country is sent. A geo.country guessed with confidence below 0.3 (from language or currency, say) counts as unknown, so a kept row can show such a guess for another country. - near (optional, string): The place the listings, places and web searches look in: a place name or lat,lng. It filters no other rows. - languages (optional, string): ISO 639 codes: keep rows in these languages and plan searches in them. Malay and Indonesian count as one language (ms keeps id rows and id keeps ms rows); rows with no detected language are kept. - min_views (optional, string): Drop rows with fewer views. - min_likes (optional, string): Drop rows with fewer likes. - min_comments (optional, string): Drop rows with fewer comments. - min_followers (optional, string): Drop rows whose author has fewer followers. - max_followers (optional, string): Drop rows whose author has more followers. - verified (optional, string): true keeps only rows by verified authors. - media_type (optional, string): Keep only rows showing these media: video, image, carousel or text. - active_within_days (optional, string): Keep authors who posted within this many days. - link_domains (optional, string): Keep only rows linking to these domains. - exclude_terms (optional, string): Drop rows containing any of these terms. - contains (optional, string): Keep only rows containing one of these strings or URL prefixes (link_hits on the row); without keywords they are searched. - undated (optional, string): keep (default; counted apart) or drop rows with no date. - estimated_dates (optional, string): keep (default), undated or drop rows whose date the platform did not publish and the lane estimated. - outside_window (optional, string): drop (default) or keep rows dated outside the window, marked in_window false. Needs a window. - min_text_length (optional, string): Drop rows whose text has fewer characters (0 to 200), before judging. - sites (optional, string): Domains searched on the web source, up to 20, or auto to find the sites that hold the topic; web is added when you send no platforms or preset, and an explicit platform set must include web. Named sites run next to web's own whole-web keyword search; send search_in.web=sites to search only inside them. - relevance (optional, string): filter (default: drop off-topic rows), score (keep every row with its score) or off. - relevance_threshold (optional, string): The relevance score a kept row needs, 0 to 1 (default 0.5); on the POST a group's own relevance_threshold sets it for that group's rows. - label (optional, string): Judgments to add to each row, each billed: mention (needs brands), intent, niche, sponsored_model, stance (judgments.stance { label, confidence } toward the group's brand, else its keywords, else its context; uncertain apart in summary.stance), or none. With view=accounts, fit, role and account_kind judge accounts (the default). - role (optional, string): Accounts only: keep accounts of these roles (seller, reseller, buyer, warner, reviewer, promoter, other); adds the role judgment. - intent (optional, string): Keep only rows with these intents: switching, complaint, recommendation, question, purchase_intent or selling (implies label=intent). - account_kind (optional, string): Accounts only: keep accounts of these kinds (individual_creator, brand_or_business, media_or_publisher, fan_or_repost_page, cannot_tell); adds the account_kind judgment. - targets_country (optional, string): Accounts only: keep accounts that target these countries (ISO 3166); adds its judgment. - sponsored (optional, string): keep (default), exclude or only rows flagged or disclosed as sponsored. - questions (optional, string): Up to 5 questions of your own, as JSON keyed by the answer's id, judged on every account (and every row with view=posts); a question about a sensitive trait of a person is a free 400. - limit_per_keyword (optional, string): Kept rows per keyword (counts in summary.by_keyword). - include (optional, string): Extra data: author_country, body (full text of the top kept articles, pages and threads), raw (each platform's own row), instagram_views, dropped (dropped rows in results.jsonl and state=dropped reads), commenters (with comments=inline), interest (data.interest: how much each keyword was searched over time, per group); with view=accounts, authors, contacts (opt-in) and audience (a TikTok creator's follower aggregates). - body_limit (optional, string): With include=body: rows whose full text is read (default 10; at most 50 on the GET and 5,000 on the POST). - comments (optional, string): off (default), inline (reads the comments under each group's top posts into a comments block on the row, billed in by_stage.comments) or bulk (data.comments_job lists those posts as ready prism/comments calls). - comments.top_posts (optional, string): With comments=inline: posts read per group, 1 to 50 (default 3), taken in turn from each platform and ranked by comment count. - comments.per_post (optional, string): With comments=inline: the most comments read from each post, 1 to 200 (default 20). - comments.select_by (optional, string): comments (default: rank posts by comment count, by views where a platform reports none) or views (rank every post by views). - comments.min_length (optional, string): With comments=inline: drop replies shorter than this many characters (fetched counts them, items holds the kept ones). - comments.labels (optional, string): With comments=inline on view=posts: comment labels (sentiment, spam, question, purchase_intent, complaint, toxic, low_quality, injection), billed at the comment endpoint's label price. - posts_per_account (optional, string): Accounts only: posts read per looked-up account, 1 to 100 (default 12); one call reads at most 12. Sent, every looked-up account's posts are read and returned in its posts array; left out, only when activity is needed. - sort (optional, string): relevance (default), recent, engagement, views or views_per_follower; accounts fit, followers, engagement or recent. - sample (optional, string): representative spreads rows across authors and platforms (with max_per_author, max_per_platform). - max_per_author (optional, string): With sample=representative: the most rows per author. - max_per_platform (optional, string): With sample=representative: the most rows per platform. - format (optional, string): evidence (default; every field), compact (about 100 tokens a row) or text (one line a row). - fields (optional, string): Keep only these keys per row (a dotted name keeps one leaf). - author_identity (optional, string): full (default), omit or hash the author on every row. - translate_to (optional, string): Adds text_translated in this language (1 credit per started 1,000 characters). - continue (optional, string): The token in data.next.continue_url: buys the next platform pages of the searches that reported more. - since_watermark (optional, string): The watermark from an earlier run's data.next.watermark: only rows newer than it. - groups (optional, string): JSON array of groups, each { "id", "keywords", "platforms"?, "country"?, "languages"?, "context"?, "tags"? }; rows report their group and the run route filters by it. - seeds (optional, string): Names to resolve to their own accounts, instead of a topic (implies mode=resolve and view=accounts). On the GET a comma-separated list whose entries are a domain or URL (notion.so: a company with that domain, named from it), Name:domain (BMW Group:bmwgroup.com) or a name alone (Trevor Noah, searched as a person); any other way of writing a name with a domain is a free 400 whose did_you_mean is the Name:domain form. On the POST the same text entries, or objects with name and optional domain, country and platforms. - authors (optional, string): JSON array of handles: keep only rows by these authors. - author_list_id (optional, string): The id from POST /v1/prism/discover/inputs, in place of a long authors list. - authors_match (optional, string): filter (default): search and keep the rows by the listed authors; walk: read each listed author's own posts in the window instead, and keep the ones about the subject. - brand_handles (optional, string): A brand's own accounts: a handle or profile URL, optionally after a platform (instagram:zomato) and, with several brands, after the brand (zomato:instagram:zomato). Adds the account's own posts (owned_post) and, on Instagram, its tagged posts. - mentions_of (optional, string): Accounts whose mentions you want (@handle): posts and replies that mention them on X, Reddit and Instagram, by the @handle (u/handle on Reddit) or by a link to the account's profile or one of its posts. - hashtags (optional, string): Hashtags searched on each platform's hashtag lane. - search_in (optional, string): Lane kinds per platform: posts (default), hashtags; Reddit and X comments; Naver corpora blog, cafe, kin, news, webkr; web search or sites, discover_sites and walk; a brand's owned posts, Instagram tagged posts, ads and reviews; listings, places and communities; Telegram messages (the search inside the channels its channel search finds). search_in. sets one platform. - review_sites (optional, string): With search_in=reviews: the review sites searched (default trustpilot, google, tripadvisor, app_store, google_play; also yelp, amazon, g2). - mode (optional, string): search (the ordinary keyword run), resolve (seeds resolved to their accounts; seeds imply it) or board (each platform's trend boards for country; view=items with country and no subject implies it). - expand (optional, string): 0 (default) to 3 extra searches planned per keyword per language; true counts as 1 and false as 0. - expand_from (optional, string): With expand above 0: keywords (default: the extra searches are planned from your keywords) or suggestions (also from each keyword's YouTube and TikTok search suggestions, 1 credit each). - sites_auto_max (optional, string): With sites=auto: how many of the sites found are searched, 1 to 20 (default 5). - walk_urls (optional, string): With search_in.web=walk: pages walked per site and keyword, 1 to 500 (default 50). - pages_per_platform (optional, string): Pages per search, 1 (default) to 10; above 3 the run continues in the background. - schedule (optional, string): POST only: { every (1h, 6h, 12h, 1d, 7d or 30d), max_credits_per_run? (defaults to max_credits; one of them is required), max_credits_per_month?, name? (1 to 100 characters), purpose? (business, brand, creator or organisation; required with authors or author_list_id), start_at? (ISO 8601 with its zone, up to 90 days ahead; omit to run now) } runs this request again on its own, each run from the previous run's watermark. Not combined with async=false, since_watermark or continue. Creating it is free; each run bills like a background run, capped at max_credits_per_run. Manage it at /v1/prism/discover/schedules. - retention_days (optional, string): Days the run and its rows stay readable, 1 to 30 (default 7). - async (optional, string): Has no effect on the POST, which always runs in the background; async=false with schedule is a free 400. - fresh (optional, string): true bypasses cached search pages, at full price. - new_run (optional, string): true starts a new run even when an identical one exists. - exclude (optional, string): Evidence ids, post URLs or account keys already held: never returned again. - exclude_list_id (optional, string): The id of an exclude list stored with POST /v1/prism/discover/inputs (kind exclude, up to 50,000 entries): its rows are never returned, with exclude. - Constraint: one of keywords, query, brands, brand, groups, hashtags, contains, seeds, authors, author_list_id, mentions_of, brand_handles, continue, view, mode (at least one required) ``` curl -X POST "https://www.socialcrawl.dev/v1/prism/discover" \ -H "x-api-key: YOUR_API_KEY" \ -H "content-type: application/json" \ -d '{"max_credits":200,"groups":[{"id":"client_a","keywords":["electric cars"],"tags":{"market":"uk"}},{"id":"client_b","keywords":["heat pumps"]}]}' ``` ### GET /prism/creators Find creators (influencers) who post about a niche on TikTok, Instagram, YouTube, X and LinkedIn, or measure and rank creators you already have, in one call or as a background run. (oneOf: keywords|query|prompt|campaign|campaign_url|similar_to|handles|author_list_id) Credit cost: 0-10000000 (metered): Each platform search is billed at that platform's own search price, only when it returns rows; a search that fails, finds nothing or is served from cache costs 0. A creator's profile lookup costs 1 credit (LinkedIn 5), a posts walk the post page's price, and judging 1 credit per started 5 creators per pass. Compiling a prompt, the metrics, the score, the filters and paging are free. max_credits (default 200 on this call) caps the whole run, and a request expected to cost more is refused free before anything is spent. Parameters: - keywords (optional, string): Comma-separated topic words, in any language; each runs once per platform. - query (optional, string): The same as keywords (merged into it). - prompt (optional, string): One sentence, 3 to 1,000 characters, compiled into the other fields; fields you send win, and the result is echoed in data.request.compiled. - campaign (optional, string): What you are promoting and who it is for, 3 to 1,000 characters; compiled into 4 to 8 content topics, a judging context and competitors (data.request.compiled.campaign). - campaign_url (optional, string): The product's App Store, Google Play or web page, read once per run at the read's own price; a subject on its own. - context (optional, string): What the creators' own content should be, 3 to 2,000 characters; every candidate is judged against it (default: accounts whose own posts are about the keywords). - brief (optional, string): The same as context. - similar_to (optional, string): 1 to 5 seed creators (platform:handle, an account key or a profile URL) to find more like. Each seed's last posts are read once for up to 3 topic searches on every chosen platform; a TikTok or Instagram seed also reads its platform's similar list (YouTube and LinkedIn: data.platforms.

.similar is topic_only). Lookalikes are judged against the seed's niche unless context is sent; the seed itself is never returned. Without platforms, the seeds' platforms. - handles (optional, string): Comma-separated creators you already have (platform:handle, an account key or a profile URL), up to 25 here; send more in POST /v1/prism/creators. Needs purpose. - author_list_id (optional, string): A stored list from POST /v1/prism/discover/inputs (up to 10,000 handles), in place of handles; every entry is measured, and one that names no creator is said in _warnings. Needs purpose. - purpose (optional, string): What the measured accounts are, required with handles or author_list_id: business, brand, creator or organisation. - platforms (optional, string): Comma-separated platforms: tiktok, instagram, youtube, twitter, linkedin, threads, facebook (x is read as twitter). Default tiktok, instagram, youtube. - country (optional, string): Up to 5 ISO 3166-1 alpha-2 codes (UK is read as GB). A planned market plans localised searches; any other code filters only, as data.market says. - min_followers (optional, string): Fewest followers (subscribers on YouTube), inclusive: one number, or a per-platform map such as youtube:10000,tiktok:50000,default:5000. - max_followers (optional, string): Most followers, inclusive: one number or a per-platform map. - min_subscribers (optional, string): The same as min_followers. - max_subscribers (optional, string): The same as max_followers. - limit (optional, string): Creators to deliver (default 20). Up to 100 answer in the call; more continue in the background. - max_credits (optional, string): The most the run may spend (default 200 on the GET; required on the POST except with dry_run). It pauses there and never overspends. - depth (optional, string): quick (profile-level creators, no posts walk, at most three platforms), standard (default, walks posts_per_creator posts of each delivered creator) or deep (background, 24 posts). - dry_run (optional, string): true answers the normalised and compiled request, the plan of searches and the estimate, free, and starts nothing. - languages (optional, string): Creators who post in these languages; searches are written per market language. - exclude_country (optional, string): Up to 10 ISO codes never delivered, by the creator's country after its lookup; a country the row would show from a hint (its captions or bio) counts too. - unknown_country (optional, string): With country: drop (default) a creator whose country stays unknown after its lookup, or keep it. - min_median_views (optional, string): Median views over the measured posts (the typical post), one number or a per-platform map. - min_avg_views (optional, string): Mean views over the measured posts, one number or a per-platform map. - min_views_to_followers (optional, string): Median views over followers, a size-independent floor, one number or a per-platform map. - min_engagement_rate (optional, string): Floor on the engagement rate, a fraction (0.03 for 3%), on the platform's basis unless engagement_basis is sent. - engagement_basis (optional, string): views or followers: the basis min_engagement_rate reads. - min_posts_per_week (optional, string): Fewest eligible posts per week over the walk. - video_format (optional, string): YouTube only: any (default), long or short, the videos the view metrics read. - unmeasurable (optional, string): keep (default) or drop a creator on a platform that publishes no value for a filtered metric; kept creators are flagged in why.unapplied_filters. - active_within_days (optional, string): Last non-pinned post within this many days. - verified (optional, string): true keeps verified accounts only; false keeps unverified only. - account_kind (optional, string): individual_creator, brand_or_business, media_or_publisher or fan_or_repost_page; a creator whose kind cannot be told is dropped unless unknown_kind=keep. - unknown_kind (optional, string): With account_kind: drop (default) or keep a creator whose kind cannot be told. - niches (optional, string): Content niche ids (sc-niche-v1); an unknown id is a free 400 with did_you_mean. - has_contact (optional, string): any: keep creators who show a public email or phone (a bio link alone is not a contact); email: keep creators who show a public email, reading a YouTube channel's About panel in the background at 25 credits per email found. Either implies include=contacts. - exclude (optional, string): Account keys never returned. - exclude_list_id (optional, string): A stored list of account keys never returned. - exclude_sponsors (optional, string): Up to 20 brand names: a creator whose measured posts disclose a sponsorship naming one is dropped; [] turns a campaign's competitors off. - sort (optional, string): score (default), fit, followers, median_views, median_shares, median_saves, posts_per_week, engagement or recent. - include (optional, string): posts (the measured posts, compact), contacts (the contact values) or linked (each delivered creator's own accounts on other platforms, confirmed by an identity check, with follower totals over those only). audience: each delivered TikTok creator's follower countries with their sampled counts, and its followers' genders and ages where TikTok publishes them, a sample of followers as published with its n, in account.audience and never in the creator's own fields (5 credits a TikTok creator read). authenticity: each delivered TikTok, Instagram or YouTube creator's comment-based and brand-safety signals, read on its walked posts, each with its sample size and never a verdict, in account.authenticity (the reads at their own prices, at most 36 credits a creator, Instagram 48, plus judging). - posts_per_creator (optional, string): Eligible (non-pinned, non-repost) posts measured per creator: 12 on standard (1 to 30), 24 on deep. - label (optional, string): Judgments: default fit,account_kind,niche; none turns judging off. - relevance (optional, string): filter (default in search mode), score or off. - relevance_threshold (optional, string): The fit a kept creator needs, 0 to 1 (default 0.5). - format (optional, string): evidence (default), compact or text. - fields (optional, string): The keys each creator row keeps. - author_identity (optional, string): full (default), omit or hash, for display-safe payloads. - audience_country (optional, string): TikTok only: up to 5 floors on the share of a creator's sampled followers in a country, as TikTok publishes it (a sample of followers as published), for example GB:0.3; every floor must hold, and a country TikTok does not list counts as 0. Each creator that passed every other filter is read once, at 5 credits, and only those that hold are delivered. Without platforms only TikTok is searched; with another platform it is not_released (none publishes an audience). Gender and age are never filters. - retention_days (optional, string): Days the run and its creators stay readable, 1 to 30 (default 7). - deadline_s (optional, string): GET only: seconds this call may wait, 10 to 96 (default 40, quick 15); a run expected to take longer answers 202 at once. - async (optional, string): auto (default: a run over the call's budget, or expected to take longer than deadline_s, answers 202 at once and continues in the background), true (always background) or false (waits up to deadline_s and answers what it finished with a resume_url; 422 NEEDS_ASYNC over the budget). - new_run (optional, string): true starts a new run even when an identical one exists. - fresh (optional, string): true bypasses cached searches, at their full price. - Constraint: one of keywords, query, prompt, campaign, campaign_url, similar_to, handles, author_list_id (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/prism/creators" \ --data-urlencode "keywords=home espresso,latte art" \ -H "x-api-key: YOUR_API_KEY" ``` ### POST /prism/creators Start a creator search from a JSON body, for handle lists and exclusions too long for a URL; it runs in the background and answers 202 with the run. (oneOf: keywords|query|prompt|campaign|campaign_url|similar_to|handles|author_list_id) Credit cost: 0-10000000 (metered): Nothing is held when the run starts. Each platform search is billed at that platform's own search price when it runs, only when it returns rows; a profile lookup costs 1 credit (LinkedIn 5), a posts walk the post page's price, and judging 1 credit per started 5 creators per pass. max_credits is required and caps the whole run: the run pauses at it and never overspends. Reading the run is free. Parameters: - max_credits (required): Required unless dry_run is true: the most the run may spend. The run pauses at it and never overspends; a request expected to cost more is refused free. - keywords (optional, string): JSON array of topic words, in any language; each runs once per platform. - query (optional, string): The same as keywords (merged into it). - prompt (optional, string): One sentence, 3 to 1,000 characters, compiled into the other fields; fields you send win, and the result is echoed in data.request.compiled. - campaign (optional, string): What you are promoting and who it is for, 3 to 1,000 characters; compiled into 4 to 8 content topics, a judging context and competitors (data.request.compiled.campaign). - campaign_url (optional, string): The product's App Store, Google Play or web page, read once per run at the read's own price; a subject on its own. - context (optional, string): What the creators' own content should be, 3 to 2,000 characters; every candidate is judged against it (default: accounts whose own posts are about the keywords). - brief (optional, string): The same as context. - similar_to (optional, string): 1 to 5 seed creators (platform:handle, an account key or a profile URL) to find more like. Each seed's last posts are read once for up to 3 topic searches on every chosen platform; a TikTok or Instagram seed also reads its platform's similar list (YouTube and LinkedIn: data.platforms.

.similar is topic_only). Lookalikes are judged against the seed's niche unless context is sent; the seed itself is never returned. Without platforms, the seeds' platforms. - handles (optional, string): JSON array of creators you already have (platform:handle, an account key or a profile URL), up to 250. Needs purpose. - author_list_id (optional, string): A stored list from POST /v1/prism/discover/inputs (up to 10,000 handles), in place of handles; every entry is measured, and one that names no creator is said in _warnings. Needs purpose. - purpose (optional, string): What the measured accounts are, required with handles or author_list_id: business, brand, creator or organisation. - platforms (optional, string): JSON array of platforms: tiktok, instagram, youtube, twitter, linkedin, threads, facebook (x is read as twitter). Default tiktok, instagram, youtube. - country (optional, string): Up to 5 ISO 3166-1 alpha-2 codes (UK is read as GB). A planned market plans localised searches; any other code filters only, as data.market says. - min_followers (optional, string): Fewest followers (subscribers on YouTube), inclusive: one number, or a per-platform map such as youtube:10000,tiktok:50000,default:5000. - max_followers (optional, string): Most followers, inclusive: one number or a per-platform map. - min_subscribers (optional, string): The same as min_followers. - max_subscribers (optional, string): The same as max_followers. - limit (optional, string): Creators to deliver (default 20; in handles mode the number of handles). The GET answers at most 100 in the call; a background run delivers up to 1,000, and an author_list_id run up to its 10,000 handles. - depth (optional, string): quick (profile-level creators, no posts walk, at most three platforms), standard (default, walks posts_per_creator posts of each delivered creator) or deep (background, 24 posts). - dry_run (optional, string): true answers the normalised and compiled request, the plan of searches and the estimate, free, and starts nothing. - languages (optional, string): Creators who post in these languages; searches are written per market language. - exclude_country (optional, string): Up to 10 ISO codes never delivered, by the creator's country after its lookup; a country the row would show from a hint (its captions or bio) counts too. - unknown_country (optional, string): With country: drop (default) a creator whose country stays unknown after its lookup, or keep it. - min_median_views (optional, string): Median views over the measured posts (the typical post), one number or a per-platform map. - min_avg_views (optional, string): Mean views over the measured posts, one number or a per-platform map. - min_views_to_followers (optional, string): Median views over followers, a size-independent floor, one number or a per-platform map. - min_engagement_rate (optional, string): Floor on the engagement rate, a fraction (0.03 for 3%), on the platform's basis unless engagement_basis is sent. - engagement_basis (optional, string): views or followers: the basis min_engagement_rate reads. - min_posts_per_week (optional, string): Fewest eligible posts per week over the walk. - video_format (optional, string): YouTube only: any (default), long or short, the videos the view metrics read. - unmeasurable (optional, string): keep (default) or drop a creator on a platform that publishes no value for a filtered metric; kept creators are flagged in why.unapplied_filters. - active_within_days (optional, string): Last non-pinned post within this many days. - verified (optional, string): true keeps verified accounts only; false keeps unverified only. - account_kind (optional, string): individual_creator, brand_or_business, media_or_publisher or fan_or_repost_page; a creator whose kind cannot be told is dropped unless unknown_kind=keep. - unknown_kind (optional, string): With account_kind: drop (default) or keep a creator whose kind cannot be told. - niches (optional, string): Content niche ids (sc-niche-v1); an unknown id is a free 400 with did_you_mean. - has_contact (optional, string): any: keep creators who show a public email or phone (a bio link alone is not a contact); email: keep creators who show a public email, reading a YouTube channel's About panel in the background at 25 credits per email found. Either implies include=contacts. - exclude (optional, string): Account keys never returned. - exclude_list_id (optional, string): A stored list of account keys never returned. - exclude_sponsors (optional, string): Up to 20 brand names: a creator whose measured posts disclose a sponsorship naming one is dropped; [] turns a campaign's competitors off. - sort (optional, string): score (default), fit, followers, median_views, median_shares, median_saves, posts_per_week, engagement or recent. - include (optional, string): posts (the measured posts, compact), contacts (the contact values) or linked (each delivered creator's own accounts on other platforms, confirmed by an identity check, with follower totals over those only). audience: each delivered TikTok creator's follower countries with their sampled counts, and its followers' genders and ages where TikTok publishes them, a sample of followers as published with its n, in account.audience and never in the creator's own fields (5 credits a TikTok creator read). authenticity: each delivered TikTok, Instagram or YouTube creator's comment-based and brand-safety signals, read on its walked posts, each with its sample size and never a verdict, in account.authenticity (the reads at their own prices, at most 36 credits a creator, Instagram 48, plus judging). - posts_per_creator (optional, string): Eligible (non-pinned, non-repost) posts measured per creator: 12 on standard (1 to 30), 24 on deep. - label (optional, string): Judgments: default fit,account_kind,niche; none turns judging off. - relevance (optional, string): filter (default in search mode), score or off. - relevance_threshold (optional, string): The fit a kept creator needs, 0 to 1 (default 0.5). - format (optional, string): evidence (default), compact or text. - fields (optional, string): The keys each creator row keeps. - author_identity (optional, string): full (default), omit or hash, for display-safe payloads. - audience_country (optional, string): TikTok only: up to 5 floors on the share of a creator's sampled followers in a country, as TikTok publishes it (a sample of followers as published), for example GB:0.3; every floor must hold, and a country TikTok does not list counts as 0. Each creator that passed every other filter is read once, at 5 credits, and only those that hold are delivered. Without platforms only TikTok is searched; with another platform it is not_released (none publishes an audience). Gender and age are never filters. - retention_days (optional, string): Days the run and its creators stay readable, 1 to 30 (default 7). - async (optional, string): auto (default: a run over the call's budget, or expected to take longer than deadline_s, answers 202 at once and continues in the background), true (always background) or false (waits up to deadline_s and answers what it finished with a resume_url; 422 NEEDS_ASYNC over the budget). - new_run (optional, string): true starts a new run even when an identical one exists. - fresh (optional, string): true bypasses cached searches, at their full price. - schedule (optional, string): POST only: re-run this search on a cadence and report only new creators, { "every": "1d", "purpose": "creator", "max_credits_per_run": 200 } (every 1h, 6h, 12h, 1d, 7d or 30d; purpose creator required; not with handles or author_list_id). The first run is a baseline with no event; each later run delivers only creators no earlier run of the schedule delivered and announces them as discover.new_accounts. Each run bills at the price of its calls, with no premium. - Constraint: one of keywords, query, prompt, campaign, campaign_url, similar_to, handles, author_list_id (at least one required) ``` curl -X POST "https://www.socialcrawl.dev/v1/prism/creators" \ -H "x-api-key: YOUR_API_KEY" \ -H "content-type: application/json" \ -d '{"max_credits":200,"keywords":["home espresso","latte art"]}' ``` --- ## Producthunt ### GET /producthunt/launches List current Product Hunt launches Credit cost: 1 (standard) Parameters: - topic (optional, string): Product Hunt topic slug, as in producthunt.com/topics/, for example artificial-intelligence or developer-tools. A display name such as Developer Tools is converted to its slug. An unrecognised topic returns no launches, with a note in data._warnings, and costs nothing. - query (optional, string): Case-insensitive text filter applied to the product name and tagline of the current launches. It filters this feed; it does not search Product Hunt's archive. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. ``` curl "https://www.socialcrawl.dev/v1/producthunt/launches" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## Quora ### GET /quora/search Search Quora questions Credit cost: 5 (advanced) Parameters: - query (required): Search keywords, for example learn programming. - time (optional, enum: all | hour | day | week | month | year): Recency window: all, hour, day, week, month, or year. Default all. - limit (optional, integer): Maximum rows to return, 1 to 10. Default 10. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. ``` curl -G "https://www.socialcrawl.dev/v1/quora/search" \ --data-urlencode "query=learn programming" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /quora/post Get a Quora question Credit cost: 5 (advanced) Parameters: - url (required): Full URL of the Quora question, for example https://www.quora.com/What-is-the-best-alternative-to-SendGrid-Email-API. ``` curl -G "https://www.socialcrawl.dev/v1/quora/post" \ --data-urlencode "url=https://www.quora.com/What-is-the-best-alternative-to-SendGrid-Email-API" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /quora/answers Search Quora answers Credit cost: 5 (advanced) Parameters: - query (required): Search keywords, for example learn programming. - time (optional, enum: all | hour | day | week | month | year): Recency window: all, hour, day, week, month, or year. Default all. - limit (optional, integer): Maximum rows to return, 1 to 10. Default 10. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. ``` curl -G "https://www.socialcrawl.dev/v1/quora/answers" \ --data-urlencode "query=learn programming" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /quora/posts Search Quora space posts Credit cost: 5 (advanced) Parameters: - query (required): Search keywords. - time (optional, enum: all | hour | day | week | month | year): Recency window: all, hour, day, week, month, or year. Default all. - limit (optional, integer): Maximum rows to return, 1 to 10. Default 10. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. ``` curl -G "https://www.socialcrawl.dev/v1/quora/posts" \ --data-urlencode "query=learn programming" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /quora/profiles Search Quora profiles Credit cost: 5 (advanced) Parameters: - query (required): Search keywords, for example a topic or a person's name. - time (optional, enum: all | hour | day | week | month | year): Recency window: all, hour, day, week, month, or year. Default all. - limit (optional, integer): Maximum rows to return, 1 to 10. Default 10. ``` curl -G "https://www.socialcrawl.dev/v1/quora/profiles" \ --data-urlencode "query=learn programming" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /quora/spaces Search Quora Spaces Credit cost: 5 (advanced) Parameters: - query (required): Search keywords. - time (optional, enum: all | hour | day | week | month | year): Recency window: all, hour, day, week, month, or year. Default all. - limit (optional, integer): Maximum rows to return, 1 to 10. Default 10. ``` curl -G "https://www.socialcrawl.dev/v1/quora/spaces" \ --data-urlencode "query=learn programming" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /quora/topics Search Quora topics Credit cost: 5 (advanced) Parameters: - query (required): Search keywords. - time (optional, enum: all | hour | day | week | month | year): Recency window: all, hour, day, week, month, or year. Default all. - limit (optional, integer): Maximum rows to return, 1 to 10. Default 10. ``` curl -G "https://www.socialcrawl.dev/v1/quora/topics" \ --data-urlencode "query=learn programming" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## Reddit ### GET /reddit/subreddit List Reddit subreddit posts Credit cost: 1-5 (metered): 1 credit per page. The default labels (sponsored, intent, niche) are free. label=mention, quality, ai_style, ai_written, injection, or label=intent with offer=, holds 4 extra credits and refunds down to 1 credit per started 25 posts that were newly judged: posts already labelled are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - subreddit (required): Subreddit name without the r/ prefix - timeframe (optional, enum: all | day | week | month | year): Timeframe to get posts from. Applied with sort=top (auto-selected when you omit sort). - sort (optional, enum: best | hot | new | top | rising): Sort order - after (optional, string): After to get more posts. Get 'after' from previous response. - trim (optional, boolean): Set to true for a trimmed down version of the response. This also skips the enrichment pass, so `author.avatar_url`, `content.media_urls`, `flags.spoiler`, `post.url` and the `ext.*` fields are omitted along with the rest of the trimmed leaves. - label (optional, string): Optional CSV of SocialCrawl labels to add to every post. Without this param every page already carries sponsored, intent and niche, free; label= adds the labels you name to them (the defaults keep running). sponsored, intent and niche are free when asked for too; mention, quality, ai_style, intent with offer=, and injection add 1 credit per started 25 newly judged posts. judgments=off (or label=none) turns the default labels off. sponsored: is the post a paid or gifted promotion (p, 0 to 1), did it carry a disclosure marker such as #ad or 광고 (disclosed), is it likely paid with no marker (undisclosed), and which of the accounts it mentions does it promote (brand, or null). These are signals to review, never a finding. mention (needs brand=): is the post about that brand rather than something that shares its name (about_brand, 0 to 1), how it feels about the brand on five levels (sentiment_level 0 to 4 and sentiment_score 0 to 1, null when the post is not about the brand), is it sarcastic, which aspect it talks about (taste_or_quality, price_or_value, availability_or_delivery, health_or_safety, advertising_or_campaign, customer_service, none), and did the author buy or use it (first_hand). intent: what the author is mainly doing (label: asking_for_recommendation, comparing_options, switching_away, complaining, promoting, news_or_discussion, other, or null when unsure, with confidence), whether they read as a potential buyer rather than a seller (buyer, seller), how pressing the need is (urgency, 0 to 3), and, when you pass offer=, whether your offer would plausibly help them (fits_offer). niche: which of the 33 niches of the published taxonomy sc-niche-v1 the caption belongs to, or personal_no_niche, or other (label, confidence, taxonomy), with label null when the caption is too thin to tell or the pick is unsure. It reads the caption only, not the video. quality: how much checkable detail the caption carries (fact_density 0 to 3), whether it mainly asks for likes, replies, shares, follows or tags (engagement_bait, 0 to 1), whether it is written to provoke anger as a way to get engagement (rage_bait, 0 to 1; about the writing, never the side taken), whether it only repeats someone else's news or view (secondhand, 0 to 1), and what the post is mainly doing (post_aim: inform, opinion, sell, entertain, provoke, other). exclude=engagement_bait drops posts whose engagement_bait is 0.8 or higher. ai_style: writing patterns common in AI-generated text, judged on the text alone. It does not say who wrote the post, and a person who writes in this style is flagged too. Six patterns are judged, each a probability (0 to 1): binary_contrast ("This is not X. It's Y."), faux_insight ("Here's what nobody tells you" and throat-clearing openers), colon_reveal ("The best part: it learns."), profound_kicker (ends on an aphorism or mic-drop line), puffery ("marks a pivotal moment", "highlighting the importance of") and rhetorical_setup ("What if I told you", a question the post answers itself, "Not a X. Not a Y. A Z."). Four are counted in the text: slop_words (words and phrases such as delve, leverage, robust, game changer, at the end of the day), em_dashes, emoji_bullets (lines that start with an emoji or a symbol bullet) and one_line_paragraphs (paragraphs that are one short line). pattern_count is how many patterns are present (a judged pattern at 0.8 or higher, slop_words 1 or more, em_dashes 2 or more, emoji_bullets 2 or more, one_line_paragraphs 5 or more) and markers lists their names, comma-separated. A post with less than about eight words of text is only counted, with every judged pattern 0. The judged patterns read the first 400 and the last 150 characters of a long post, and the counts read the whole post. exclude=ai_style drops posts with 2 or more patterns. ai_written: how likely the post was written by an AI model, from SocialCrawl's own text classifier, reading the opening of the post (about 1,000 characters): p (0 to 1, calibrated), level (likely (p 0.95 or higher), possible (p 0.71 or higher) or unlikely) and model (the model name; a new name means a new scale). A post under 8 words is too short to read: p null, level too_short. It is a probability, not proof: a person can be flagged and an AI-written post can pass, and the shorter the post the less certain it is. exclude=ai_written drops likely posts. injection flags text that addresses an AI system and tries to direct it (flagged, p); it never drops or rewrites a row. A post that could not be judged carries labels: null. Posts already labelled for anyone are free, and so is a cached page. data.labels reports what was judged and billed. - exclude (optional, string): Optional CSV (engagement_bait, ai_style, ai_written). engagement_bait, only with label=quality, drops posts whose engagement_bait probability is 0.8 or higher. ai_style, only with label=ai_style, drops posts that show 2 or more of its writing patterns (pattern_count 2 or higher). ai_written, only with label=ai_written, drops posts whose level is likely (p 0.95 or higher), or whose p reaches your ai_written_threshold; a post too short to read is never dropped. Dropped posts are listed in data.labels.dropped_ids. A post that could not be judged is never dropped. - ai_written_threshold (optional, string): Optional, only with label=ai_written and exclude=ai_written. The p (0 to 1) at which exclude=ai_written drops a post. Default 0.95 (level likely). 0.71 also drops possible posts: more AI posts go, and more posts by people with them. - brand (optional, string): Required by label=mention, ignored otherwise. The brand or product the posts are judged against, up to 300 characters, for example brand=Buldak. Without it label=mention is skipped with the warning label_mention_needs_brand and is not billed. - brand_description (optional, string): Optional, with label=mention. One plain sentence saying what the brand is, for example brand_description=Samyang's spicy instant noodle brand. Use it when the name is also an ordinary word or another company's name. - offer (optional, string): Optional, with label=intent. One or two plain sentences saying what you sell, up to 300 characters, for example offer=A web design agency that builds websites for small businesses. Adds fits_offer to every post; without it fits_offer is null. - judgments (optional, enum: on | off): Optional, on (the default) or off. By default every row gains free SocialCrawl judgments (computed.labels, and computed.relevance on search endpoints), reported in data.labels (mode default) and data.relevance (origin default), each with a status (complete, partial or skipped) and pending: the rows still being judged when the page was sent, which carry null now and are filled on your next call or cached read. Default judgments never add credits, never change an existing field, and never drop or reorder a row. off returns the page exactly as before, with none of those keys. label=none does the same. - dry_run (optional, enum: 1): Optional. When 1, return a cost preview for this labelled or relevance-filtered request without fetching the page or judging any row. data.estimate reports rows_expected, rows_cached, label_credits_min, label_credits_max and base_credits. 0 credits charged. - label_evidence (optional, enum: 1): Optional, only with label=. When 1, every labelled row also carries computed.labels_evidence. = { quote, sentence_index }: the sentence in the row that most clearly shows the label, copied verbatim. Absent or null when no single sentence shows it. - fit (optional, enum: goal): Optional. When goal, keep the rows and fields needed for the goal you pass in goal= (plus any that are uncertain, and the first and last), and replace the rest with a stub. data.held_back lists the held ids and a recall id that re-reads the full page from cache at no extra charge. Without this param the page is unchanged. - goal (optional, string): Required by fit=goal. What you are trying to do, in your own words, up to 300 characters. - fit_tokens (optional, integer): Optional, only with fit=goal. Soft cap on how much of the page to keep, in tokens. Uncertain blocks and the first and last block are kept even if they exceed it. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off the page, and the page that reaches one ends the walk: `next_cursor` is not returned and `pagination.stopped_at` is `since`. Sending it sets the newest-first sort shown in this endpoint's sort parameter (any other sort value is a free 400). The page still costs what a page costs, so you pay for the pages you walk and no more. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/reddit/subreddit" \ --data-urlencode "subreddit=technology" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /reddit/subreddit/details Get Reddit subreddit details (oneOf: subreddit|url) Credit cost: 1 (standard) Parameters: - subreddit (optional, string): Subreddit name without the r/ prefix, in any casing (e.g. `AskReddit` or `askreddit`). - url (optional, string): Subreddit URL - Constraint: one of subreddit, url (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/reddit/subreddit/details" \ --data-urlencode "subreddit=AskReddit" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /reddit/search Search Reddit posts Credit cost: 1-48 (metered): 1 credit for the search page. include_body=true reserves up to 25 extra credits and refunds every one it does not spend. Rows now arrive with their bodies, and a link post has no body to fetch, so a row is only charged for when a body actually comes back: on most pages nothing is spent and the page costs 1 credit. With limit above 25: 1 credit per 25 posts returned, never less than 1. The call holds limit / 25 credits (rounded up, at most 40) and refunds the rest once the posts are counted; posts left out by since or until are not counted. Relevance against your query (by default, or with relevance=) is free. Adding relevant_to= holds 4 extra credits and refunds down to 1 credit per started 25 rows that were newly judged: rows already judged for the same topic are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. The default labels (sponsored, intent, niche) are free. label=mention, quality, ai_style, ai_written, injection, or label=intent with offer=, holds 4 extra credits and refunds down to 1 credit per started 25 posts that were newly judged: posts already labelled are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. With `seen`, a page's price falls with its share of rows you already received under that id; credits for joined data (`include=`) are never discounted. Parameters: - query (required): Search words or a phrase. Words match any, not all: vision pro also returns posts that mention only one of them. Wrap a phrase in double quotes to match it exactly (query="vision pro"). Reddit operators such as subreddit:name work too. When fewer than half the rows contain every word of an unquoted query, the response says so in _warnings. - sort (optional, enum: relevance | new | top | comment_count): Sort by - timeframe (optional, enum: all | day | week | month | year): Only return posts from this window. Applied on sort=relevance, sort=top and sort=comment_count. It has no effect with sort=new, which is already ordered newest-first and returns recent posts regardless. - after (optional, string): Used to paginate to next page - trim (optional, boolean): Set to true for a trimmed down version of the response. On this endpoint it also skips the second source that widens the page, so a trimmed page is both thinner and usually shorter. Measured 09/09/2026: default 29 rows, `trim=true` 25 rows. It does not drop `author.avatar_url` or `ext.*` — those leaves stay when the first source already carried them. Use it to skip the widening pass, not to strip fields. - include_body (optional, boolean): Set to true to include each post's body (selftext) on the search page. Rarely needed now: bodies arrive on the rows themselves. 1 extra credit per row that actually comes back with a body, unused credits refunded. Link posts have no body, so they are looked up and not charged for. - limit (optional, integer): How many posts to return in one call, 26 to 1,000. Above 25 the call reads Reddit's own search index once instead of returning one page: up to this many posts for the query, sort and timeframe, newest first with sort=new, at 1 credit per 25 posts returned (limit / 25 is held up front and the rest refunded). Reddit stops one query at about 1,000 posts, so this answer has no next page. At 25 or below the ordinary page is returned exactly as without it, and a request that carries after is a next page, so limit does not apply to it. - relevance (optional, enum: score | filter): Optional. Without this param every row already carries computed.relevance against your query, free ({ p, sense, depth, spam }: is this row about what your query means, or a different thing that shares its words?), and nothing is dropped or reordered. score asks for it explicitly and waits for every row; filter also drops the rows that are not about your query, and lists their ids in data.relevance.dropped_ids. Your query is the topic; nothing to configure. A row that could not be judged is never dropped and carries relevance: null. Pagination is unchanged, so a filtered page can hold fewer rows. Free with your query as the topic; with relevant_to it adds 1 credit per started 25 newly judged rows (rows already judged for the same topic are free, and so is a cached page). - relevance_threshold (optional, string): Optional, only with relevance. The probability (0 to 1) a row must reach to be kept by relevance=filter. Default 0.5. Lower keeps more rows, higher keeps fewer. - relevant_to (optional, string): Optional, only with relevance. Up to 200 characters describing what you mean, used as the topic instead of the query. Use it when the query is ambiguous, for example query=cleopatra with relevant_to=Cleopatra, the IGT slot game. - judgments (optional, enum: on | off): Optional, on (the default) or off. By default every row gains free SocialCrawl judgments (computed.labels, and computed.relevance on search endpoints), reported in data.labels (mode default) and data.relevance (origin default), each with a status (complete, partial or skipped) and pending: the rows still being judged when the page was sent, which carry null now and are filled on your next call or cached read. Default judgments never add credits, never change an existing field, and never drop or reorder a row. off returns the page exactly as before, with none of those keys. label=none does the same. - dry_run (optional, enum: 1): Optional. When 1, return a cost preview for this labelled or relevance-filtered request without fetching the page or judging any row. data.estimate reports rows_expected, rows_cached, label_credits_min, label_credits_max and base_credits. 0 credits charged. - fit (optional, enum: goal): Optional. When goal, keep the rows and fields needed for the goal you pass in goal= (plus any that are uncertain, and the first and last), and replace the rest with a stub. data.held_back lists the held ids and a recall id that re-reads the full page from cache at no extra charge. Without this param the page is unchanged. - goal (optional, string): Required by fit=goal. What you are trying to do, in your own words, up to 300 characters. - fit_tokens (optional, integer): Optional, only with fit=goal. Soft cap on how much of the page to keep, in tokens. Uncertain blocks and the first and last block are kept even if they exceed it. - label (optional, string): Optional CSV of SocialCrawl labels to add to every post. Without this param every page already carries sponsored, intent and niche, free; label= adds the labels you name to them (the defaults keep running). sponsored, intent and niche are free when asked for too; mention, quality, ai_style, intent with offer=, and injection add 1 credit per started 25 newly judged posts. judgments=off (or label=none) turns the default labels off. sponsored: is the post a paid or gifted promotion (p, 0 to 1), did it carry a disclosure marker such as #ad or 광고 (disclosed), is it likely paid with no marker (undisclosed), and which of the accounts it mentions does it promote (brand, or null). These are signals to review, never a finding. mention (needs brand=): is the post about that brand rather than something that shares its name (about_brand, 0 to 1), how it feels about the brand on five levels (sentiment_level 0 to 4 and sentiment_score 0 to 1, null when the post is not about the brand), is it sarcastic, which aspect it talks about (taste_or_quality, price_or_value, availability_or_delivery, health_or_safety, advertising_or_campaign, customer_service, none), and did the author buy or use it (first_hand). intent: what the author is mainly doing (label: asking_for_recommendation, comparing_options, switching_away, complaining, promoting, news_or_discussion, other, or null when unsure, with confidence), whether they read as a potential buyer rather than a seller (buyer, seller), how pressing the need is (urgency, 0 to 3), and, when you pass offer=, whether your offer would plausibly help them (fits_offer). niche: which of the 33 niches of the published taxonomy sc-niche-v1 the caption belongs to, or personal_no_niche, or other (label, confidence, taxonomy), with label null when the caption is too thin to tell or the pick is unsure. It reads the caption only, not the video. quality: how much checkable detail the caption carries (fact_density 0 to 3), whether it mainly asks for likes, replies, shares, follows or tags (engagement_bait, 0 to 1), whether it is written to provoke anger as a way to get engagement (rage_bait, 0 to 1; about the writing, never the side taken), whether it only repeats someone else's news or view (secondhand, 0 to 1), and what the post is mainly doing (post_aim: inform, opinion, sell, entertain, provoke, other). exclude=engagement_bait drops posts whose engagement_bait is 0.8 or higher. ai_style: writing patterns common in AI-generated text, judged on the text alone. It does not say who wrote the post, and a person who writes in this style is flagged too. Six patterns are judged, each a probability (0 to 1): binary_contrast ("This is not X. It's Y."), faux_insight ("Here's what nobody tells you" and throat-clearing openers), colon_reveal ("The best part: it learns."), profound_kicker (ends on an aphorism or mic-drop line), puffery ("marks a pivotal moment", "highlighting the importance of") and rhetorical_setup ("What if I told you", a question the post answers itself, "Not a X. Not a Y. A Z."). Four are counted in the text: slop_words (words and phrases such as delve, leverage, robust, game changer, at the end of the day), em_dashes, emoji_bullets (lines that start with an emoji or a symbol bullet) and one_line_paragraphs (paragraphs that are one short line). pattern_count is how many patterns are present (a judged pattern at 0.8 or higher, slop_words 1 or more, em_dashes 2 or more, emoji_bullets 2 or more, one_line_paragraphs 5 or more) and markers lists their names, comma-separated. A post with less than about eight words of text is only counted, with every judged pattern 0. The judged patterns read the first 400 and the last 150 characters of a long post, and the counts read the whole post. exclude=ai_style drops posts with 2 or more patterns. ai_written: how likely the post was written by an AI model, from SocialCrawl's own text classifier, reading the opening of the post (about 1,000 characters): p (0 to 1, calibrated), level (likely (p 0.95 or higher), possible (p 0.71 or higher) or unlikely) and model (the model name; a new name means a new scale). A post under 8 words is too short to read: p null, level too_short. It is a probability, not proof: a person can be flagged and an AI-written post can pass, and the shorter the post the less certain it is. exclude=ai_written drops likely posts. injection flags text that addresses an AI system and tries to direct it (flagged, p); it never drops or rewrites a row. A post that could not be judged carries labels: null. Posts already labelled for anyone are free, and so is a cached page. data.labels reports what was judged and billed. - exclude (optional, string): Optional CSV (engagement_bait, ai_style, ai_written). engagement_bait, only with label=quality, drops posts whose engagement_bait probability is 0.8 or higher. ai_style, only with label=ai_style, drops posts that show 2 or more of its writing patterns (pattern_count 2 or higher). ai_written, only with label=ai_written, drops posts whose level is likely (p 0.95 or higher), or whose p reaches your ai_written_threshold; a post too short to read is never dropped. Dropped posts are listed in data.labels.dropped_ids. A post that could not be judged is never dropped. - ai_written_threshold (optional, string): Optional, only with label=ai_written and exclude=ai_written. The p (0 to 1) at which exclude=ai_written drops a post. Default 0.95 (level likely). 0.71 also drops possible posts: more AI posts go, and more posts by people with them. - brand (optional, string): Required by label=mention, ignored otherwise. The brand or product the posts are judged against, up to 300 characters, for example brand=Buldak. Without it label=mention is skipped with the warning label_mention_needs_brand and is not billed. - brand_description (optional, string): Optional, with label=mention. One plain sentence saying what the brand is, for example brand_description=Samyang's spicy instant noodle brand. Use it when the name is also an ordinary word or another company's name. - offer (optional, string): Optional, with label=intent. One or two plain sentences saying what you sell, up to 300 characters, for example offer=A web design agency that builds websites for small businesses. Adds fits_offer to every post; without it fits_offer is null. - label_evidence (optional, enum: 1): Optional, only with label=. When 1, every labelled row also carries computed.labels_evidence. = { quote, sentence_index }: the sentence in the row that most clearly shows the label, copied verbatim. Absent or null when no single sentence shows it. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off the page, and the page that reaches one ends the walk: `next_cursor` is not returned and `pagination.stopped_at` is `since`. Sending it sets the newest-first sort shown in this endpoint's sort parameter (any other sort value is a free 400). The page still costs what a page costs, so you pay for the pages you walk and no more. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_age_days (optional, integer): Keep only rows published within this many days (`post.published_at`), 1 to 3650. A row with no date is discarded. Filtering runs on our side after each page is fetched, so every page walked is billed as usual; `data.walk.discarded` counts what was removed and why. Each page is its own entry in `GET /v1/credits/transactions`, under the page's request id listed in `data.walk.request_ids`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them (after any filter). Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows (after any filter), then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. - seen (optional, string): An id you choose (1 to 64 letters, digits, `.`, `_` or `-`). Rows your account already received under the same `seen` id are removed from the page, and the page price falls with the share of repeats: page credits x new rows / rows on the page, rounded up, so a page of nothing but repeats is free. Only the page's own price is discounted: credits for joined data (`include=`) are charged only for rows you have not received, and are never discounted. The memory lasts 24 hours from the last call that used the id, holds row ids only, and is private to your account. Use one id across a set of related searches; `data.walk.repeats` counts what was removed. ``` curl -G "https://www.socialcrawl.dev/v1/reddit/search" \ --data-urlencode "query=\"claude code\"" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /reddit/post Get a Reddit post Credit cost: 1 (standard) Parameters: - url (required): Full URL of the Reddit post ``` curl -G "https://www.socialcrawl.dev/v1/reddit/post" \ --data-urlencode "url=https://www.reddit.com/r/NoteTaking/comments/1o9s55r/i_tried_all_popular_ai_notetaking_apps_so_you/" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /reddit/post/comments List Reddit post comments Credit cost: 5-9 (metered): 5 credits per page, with the default labels (sentiment, question, purchase_intent, complaint) included free. label=spam, toxic or low_quality holds 4 extra credits and refunds down to 1 credit per started 25 comments that were newly judged: comments already labelled are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - url (required): Full URL of the Reddit post to fetch comments for - cursor (optional, string): Cursor to get more comments, or replies. - trim (optional, boolean): Accepted, but not honoured on this endpoint: the source that serves it does not offer a trimmed shape, so the response is the same either way. Kept because removing an accepted parameter would break existing calls. - label (optional, string): Optional CSV of SocialCrawl labels to add to every comment: sentiment, spam, question, purchase_intent, complaint, toxic, low_quality, injection. Without this param every page already carries sentiment, question, purchase_intent and complaint, free; label= adds the labels you name to them (the defaults keep running). Each comment gains computed.labels with probabilities (0 to 1), so you choose the cut-off; a comment that could not be judged carries labels: null. sentiment, question, purchase_intent and complaint are free when asked for too; spam, toxic, low_quality and injection add 1 credit per started 25 newly judged comments (comments already labelled for anyone are free, and so is a cached page). injection flags text that addresses an AI system and tries to direct it (flagged, p); it never drops or rewrites a row. judgments=off (or label=none) turns the default labels off. data.labels reports what was judged and billed. - exclude (optional, string): Optional CSV (spam, low_quality), only with the same preset in label. Drops comments whose probability is 0.8 or higher and lists their ids in data.labels.dropped_ids. A comment that could not be judged is never dropped. - judgments (optional, enum: on | off): Optional, on (the default) or off. By default every row gains free SocialCrawl judgments (computed.labels, and computed.relevance on search endpoints), reported in data.labels (mode default) and data.relevance (origin default), each with a status (complete, partial or skipped) and pending: the rows still being judged when the page was sent, which carry null now and are filled on your next call or cached read. Default judgments never add credits, never change an existing field, and never drop or reorder a row. off returns the page exactly as before, with none of those keys. label=none does the same. - dry_run (optional, enum: 1): Optional. When 1, return a cost preview for this labelled or relevance-filtered request without fetching the page or judging any row. data.estimate reports rows_expected, rows_cached, label_credits_min, label_credits_max and base_credits. 0 credits charged. - label_evidence (optional, enum: 1): Optional, only with label=. When 1, every labelled row also carries computed.labels_evidence. = { quote, sentence_index }: the sentence in the row that most clearly shows the label, copied verbatim. Absent or null when no single sentence shows it. - fit (optional, enum: goal): Optional. When goal, keep the rows and fields needed for the goal you pass in goal= (plus any that are uncertain, and the first and last), and replace the rest with a stub. data.held_back lists the held ids and a recall id that re-reads the full page from cache at no extra charge. Without this param the page is unchanged. - goal (optional, string): Required by fit=goal. What you are trying to do, in your own words, up to 300 characters. - fit_tokens (optional, integer): Optional, only with fit=goal. Soft cap on how much of the page to keep, in tokens. Uncertain blocks and the first and last block are kept even if they exceed it. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/reddit/post/comments" \ --data-urlencode "url=https://www.reddit.com/r/NoteTaking/comments/1o9s55r/i_tried_all_popular_ai_notetaking_apps_so_you/" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /reddit/subreddit/search Search within a subreddit Credit cost: 1-26 (metered): 1 credit for the search page. With include_body=true, 1 extra credit per post that comes back with a body, up to 25 per page, unused credits refunded. Link posts have no body and are not charged for. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - subreddit (required): Subreddit name (e.g. 'Fitness', not 'r/Fitness' or a full URL) - query (optional, string): Search query to find matching content - sort (optional, enum: relevance | hot | top | new | comments): Sort order. For posts/media: relevance, hot, top, new, comments. For comments: relevance, top, new - timeframe (optional, enum: all | year | month | week | day | hour): Only return posts from this window. Applied on sort=relevance, sort=top and sort=comments. It has no effect with sort=new, which is already ordered newest-first and returns recent posts regardless. - cursor (optional, string): Cursor to get more results. Get 'cursor' from previous response. - include_body (optional, boolean): Set to true to include each post's body (selftext) on the search page. 1 extra credit per post that comes back with a body, unused credits refunded. Link posts have no body, so they are looked up and not charged for. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/reddit/subreddit/search" \ --data-urlencode "subreddit=technology" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /reddit/profile Get a Reddit user profile Credit cost: 1 (standard) Parameters: - handle (required): Reddit username without the u/ prefix. Case-insensitive. ``` curl -G "https://www.socialcrawl.dev/v1/reddit/profile" \ --data-urlencode "handle=spez" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /reddit/profile/posts List a Reddit user's posts Credit cost: 1 (standard) Parameters: - handle (required): Reddit username without the u/ prefix. Case-insensitive. - sort (optional, enum: hot | new | top): Sort order for the account's submissions. `top` defaults to the all-time window; pass `timeframe` to narrow it. - timeframe (optional, enum: all | year | month | week | day | hour): Only return posts from this window. Applied on `sort=top`. Omitted `sort=top` is treated as `timeframe=all` so the documented call is not an empty page. - cursor (optional, string): Cursor to get more results. Get 'cursor' from the previous response. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off the page, and the page that reaches one ends the walk: `next_cursor` is not returned and `pagination.stopped_at` is `since`. Sending it sets the newest-first sort shown in this endpoint's sort parameter (any other sort value is a free 400). The page still costs what a page costs, so you pay for the pages you walk and no more. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/reddit/profile/posts" \ --data-urlencode "handle=spez" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /reddit/profile/comments Read a Reddit account's own comment history, newest first, deeper than any search index reaches. Metered: 2 credits per comment returned, so try /v1/reddit/search/comments?query=author:name first Credit cost: 2-200 (metered): 2 credits per comment returned. A limit of 25 holds 50 credits and settles down to the number of comments that actually came back; an account with no comments costs nothing. Parameters: - handle (required): Reddit username without the u/ prefix. Case-insensitive. - limit (optional, integer): How many comments to return, 1-100, default 25. This is a depth control, not a page size: a higher limit reaches further back into the account's history at the same speed. - before (optional, string): Only return comments older than this date (YYYY-MM-DD). Use it to walk a long history in windows instead of raising limit. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. ``` curl -G "https://www.socialcrawl.dev/v1/reddit/profile/comments" \ --data-urlencode "handle=spez" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /reddit/search/comments Search Reddit comments Credit cost: 1 (standard) Parameters: - query (required): Search phrase. Reddit's operators work here: `author:username` for one account's comments, `subreddit:name terms` to scope to a community, and quotes for an exact phrase. - sort (optional, enum: relevance | top | new): Sort order for the comment hits. - cursor (optional, string): Cursor to get more results. Get 'cursor' from the previous response. - switch_from (optional, string): Optional brand. data.switch_receipts lists comments where the author says they personally left that brand, with the verbatim text, the reason, where they went, and a link. There is no percentage. A comment that is not a switch, or that is not confident, is left out. No extra credits. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off the page, and the page that reaches one ends the walk: `next_cursor` is not returned and `pagination.stopped_at` is `since`. Sending it sets the newest-first sort shown in this endpoint's sort parameter (any other sort value is a free 400). The page still costs what a page costs, so you pay for the pages you walk and no more. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/reddit/search/comments" \ --data-urlencode "query=best project management software" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /reddit/subreddits/search Find subreddits by topic Credit cost: 1-26 (metered): 1 credit for the page. include=details holds 1 credit per row, at most 25 (or `limit`), and keeps only the rows filled from a fresh lookup, so a page is at most 26 credits; rows served from cache are free, unfilled rows are refunded, and a repeat within the cache window costs 0. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - query (required): Topic or keyword to find communities for. - cursor (optional, string): Cursor to get more results. Get 'cursor' from the previous response. - include (optional, enum: details): Set to `details` (one token only) to fill `author.joined_at`, `author.ext.weekly_active_users`, `author.ext.weekly_contributions`, `author.ext.rules_text` and `author.ext.language` on every row in this one call (a row the details lookup's main source cannot answer gets only the creation date and the language, and is still billed). Holds 1 credit per row (at most 25, or `limit`) and keeps only the rows filled from a fresh lookup; rows already in cache and rows that could not be filled are refunded, so the most a call can cost is 1 + rows. Adds 3 to 9 seconds on a fresh page (never more than 12) and nothing when the rows are already cached; read `data.hydration` for the rows, the credits held and kept, and the time. - limit (optional, integer): Take the top N communities of the page (1 to 25). With `include=details` it also caps the extra credits at N, so `limit=5&include=details` holds 6 credits, not 26. It is not a page size: the cursor still advances past the whole page, so rows beyond N are not returned by the next page. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/reddit/subreddits/search" \ --data-urlencode "query=machine learning" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /reddit/search/media Search Reddit image and video posts Credit cost: 1 (standard) Parameters: - query (required): Search phrase. Reddit's operators work here: `subreddit:name terms` scopes the sweep to one community, and quotes force an exact phrase. - sort (optional, enum: relevance | new | top | comment_count): Sort by - timeframe (optional, enum: all | day | week | month | year): Only return posts from this window. Applied on sort=relevance, sort=top and sort=comment_count. It has no effect with sort=new, which is already ordered newest-first. - cursor (optional, string): Cursor to get more results. Get 'cursor' from the previous response. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/reddit/search/media" \ --data-urlencode "query=mechanical keyboard build" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /reddit/post/transcript Get a Reddit video post transcript Credit cost: 10 (premium) Parameters: - url (required): Reddit post URL or direct v.redd.it video URL. - language (optional, string): 2-letter language code. Defaults to `en`. ``` curl -G "https://www.socialcrawl.dev/v1/reddit/post/transcript" \ --data-urlencode "url=https://www.reddit.com/r/youseeingthisshit/comments/1oiu9xm/football_nostalgiasaints_punter_head_coach_cant/" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /reddit/omni-search Reddit VoC sweep: one keyword → threads across all of Reddit with subreddit attribution and top comments inline. Credit cost: 5-9 (metered): 1 credit for the search page plus 1 per thread SUCCESSFULLY expanded, with a floor of 5. The call holds `1 + threads` up front (the 8-thread maximum holds 9) and refunds every thread that failed to expand, so the floor is what most sweeps cost. Running the same sweep by hand - one reddit/search plus eight reddit/post/comments calls - costs the same 9 at best and gives you no subreddit rollup Parameters: - query (required): Keyword or phrase to sweep across Reddit. - threads (optional, integer): How many top threads to expand comments for (1-8, default 8). - sort (optional, enum: relevance | new | top | comment_count): Search sort order (relevance | new | top | comment_count). - timeframe (optional, enum: all | day | week | month | year): Time window for the search (all | day | week | month | year). - subreddit (optional, string): Scope the sweep to one subreddit (bare name, no r/ prefix). - cursor (optional, string): Opaque cursor from a prior response's next_cursor to page deeper. - include (optional, string): CSV subset of subreddits,comments (default both). The comments block is returned as `threads[].top_comments`. ``` curl -G "https://www.socialcrawl.dev/v1/reddit/omni-search" \ --data-urlencode "query=best mechanical keyboard" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## Rumble ### GET /rumble/search Search Rumble videos Credit cost: 1 (standard) Parameters: - query (required): Search query. - cursor (optional, string): Cursor from the previous response: the next page number (e.g. `2`). - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/rumble/search" \ --data-urlencode "query=funny cats" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /rumble/channel/videos List videos for a Rumble channel (oneOf: handle|url) Credit cost: 1 (standard) Parameters: - handle (optional, string): Rumble channel handle. - url (optional, string): Rumble channel URL. - cursor (optional, string): Cursor from the previous response: the next page number (e.g. `2`). - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. - Constraint: one of handle, url (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/rumble/channel/videos" \ --data-urlencode "handle=CuteCats223" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /rumble/video Get a Rumble video Credit cost: 1 (standard) Parameters: - url (required): Rumble video URL. ``` curl -G "https://www.socialcrawl.dev/v1/rumble/video" \ --data-urlencode "url=https://rumble.com/v79xhhm-discovery-example.html" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /rumble/video/transcript Get a Rumble video transcript Credit cost: 10 (premium) Parameters: - url (required): Rumble video URL. ``` curl -G "https://www.socialcrawl.dev/v1/rumble/video/transcript" \ --data-urlencode "url=https://rumble.com/v784xoi-president-donald-j.-trump-and-secwar-pete-hegseth-hold-a-press-conference-a.html" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /rumble/video/comments List top-level comments on a Rumble video Credit cost: 1 (standard) Parameters: - url (required): Rumble video URL. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. ``` curl -G "https://www.socialcrawl.dev/v1/rumble/video/comments" \ --data-urlencode "url=https://rumble.com/v792vns-example.html" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## Search ### GET /search/everywhere Universal social search across 15 platforms Credit cost: 20 (override; tier standard) Parameters: - query (required): Search query (1-512 chars) - lookback_days (optional, integer): Recency window in days (minimum 1, default 30). This is a ranking signal, not a hard filter: it sets the freshness curve, so a much older item can still rank when nothing recent matches, and it sets the date window sent to `facebook`. When `from_date` is sent too, `from_date` applies. On the stream, windows over 90 days add a `warning` chunk. For hard bounds use `since`, `until`, `from_date` and `to_date`. Check each row's `date_confidence` and `date_source` before filtering on its date: `low` means the date is an estimate (a rounded YouTube label, a KnowledgeiN estimate). - from_date (optional, string): Lower date bound, YYYY-MM-DD (from the start of that UTC day) or an ISO 8601 timestamp; when `lookback_days` is sent too, `from_date` applies. A hard bound on `items`: rows dated before it are dropped, rows with no date are kept, and `data.since` reports `{ since, until, dropped, kept_undated }`. Sent with `since`, the later of the two applies and `data._warnings` says so. An invalid date is a free 400. - to_date (optional, string): Upper date bound, YYYY-MM-DD (to the end of that UTC day, inclusive) or an ISO 8601 timestamp; defaults to today when from_date is set alone. A hard bound on `items`: rows dated after it are dropped, rows with no date are kept. Sent with `until`, the earlier of the two applies and `data._warnings` says so. An invalid date is a free 400. - since (optional, string): Only rows published at or after this time (ISO 8601 timestamp or date), for polling: pass the previous run's time. A hard filter on `items`, unlike lookback_days; rows with no date are kept and `data.since` reports `{ since, until, dropped, kept_undated }`. Billed as usual. - until (optional, string): Only rows published at or before this time: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. A hard filter on `items`, like since; rows with no date are kept and counted in `data.since`. Billed as usual. An invalid date is a free 400. - period (optional, string): A window that holds, by name: `Nd` (1 to 3650 days, ending now), `yesterday`, `this_week`, `last_week`, `this_month`, `last_month`, `last_quarter`, `this_year` or `ytd`, with calendar periods read in `tz`. Each platform is sent its own date filter, and every row is checked against the window again, so rows outside it are dropped whatever the platform did; `data.request.window` echoes the resolved window and `data.platforms.

.window` counts the rows dropped. `since`, `until`, `from_date`, `to_date` and `lookback_days` win over `period` when sent with it, and `data._warnings` says so. Billed as usual. - tz (optional, string): IANA time zone name (default UTC, for example Asia/Seoul) that `period` and bare `since`, `until`, `from_date` and `to_date` dates are read in. An unknown zone is a free 400. - undated (optional, enum: keep | drop): What a row with no date does inside a window: `keep` (default) keeps it and counts it in `data.request.window.undated_kept`, `drop` removes it and counts it in `undated_dropped`. Applies only when a window is set. - limit (optional, integer): How many ranked results `items` returns, 1 to 40 (default 40). Above 40 is clamped to 40 with a `data._warnings` line. It trims the answer only: the search, its price and its cache entry are the same whatever the limit, so `limit=10` then `limit=20` on the same query is one billed search. - sources (optional, string): Optional CSV allowlist of sources (mutually exclusive with exclude). Valid names: reddit, twitter, twitter-ai-search, youtube, tiktok, instagram, hackernews, polymarket, github, threads, pinterest, perplexity, web, linkedin, rumble, tiktok-hashtag, instagram-hashtag, youtube-hashtag, naver, facebook. `naver` (Naver blog, cafe and KnowledgeiN) is in the default set only when the query contains Hangul. `facebook` (public post search) runs only when you name it here. `x` is another name for `twitter` (X posts) and `tavily` for `web`; `twitter-ai-search` here means `twitter` plus include=synthesis. Platform shorthands expand to their full group: youtube, instagram, tiktok also include their -hashtag lane. Unknown names return a 400. - platforms (optional, string): Another name for sources, with the same values. When both are sent and differ, sources applies and `data._warnings` says so. - exclude (optional, string): Optional CSV blocklist of sources (mutually exclusive with sources). Same valid names and platform shorthands as sources: excluding youtube/instagram/tiktok also excludes the platform's -hashtag lane. Unknown names return a 400. - include_transcripts (optional, boolean): Fetch spoken-word transcripts for the top 3 video results (default false), so a video whose title never mentions your query but whose narration does can still be found and quoted. Applies to the YouTube, Rumble, TikTok and Instagram lanes; YouTube and Rumble transcripts carry timestamped segments, TikTok and Instagram are plain text. Transcripts land on `data.items[i].source_items[0].metadata.transcript` and stream as `transcript_enriched` chunks. Included in the flat 20 credits. - relevance (optional, enum: filter): Optional. filter drops the ranked results our relevance judgment already scored as off-topic for your query (unrelated, or clearly about a different thing that shares its words) and lists their ids in data.relevance.dropped_ids, with counts. A result that could not be judged is never dropped; when none could be judged nothing is dropped and data._warnings says relevance_unavailable. items_by_source is not filtered. Omit it to keep every ranked result, ordered as before; each result still carries computed.relevance by default (see judgments). Included in the flat 20 credits. - include (optional, string): Optional CSV of extra blocks and opt-in sources. synthesis adds the X summary (twitter-ai-search) and returns it in data.synthesis.x with its citations. facebook adds Facebook public post search to the default sources (it is not in the default fan-out, and `sources=` would replace the defaults instead). stance is now on by default and accepted for compatibility: data.items[i].computed.stance ({ label: positive, negative, neutral, no_opinion or null when unsure, probabilities, confidence }) says what each post, with any replies shown, says about your query, and data.stance_split counts it per platform and overall with the ids behind every count. Posts judged under the confidence floor count as uncertain, never as a stance. If the judgment is unavailable the search still returns, stance is null and data._warnings says stance_unavailable (or stance_skipped when judging was skipped). Included in the flat 20 credits. - judgments (optional, enum: on | off): Optional. on (default) or off. On, every result also carries data.items[i].computed.stance (see include) and data.items[i].computed.relevance ({ score 0 to 1, on_topic, entity }), with data.relevance counting the off-topic results and listing their ids. Nothing is dropped or reordered and the price does not change. off returns the response without these blocks. - switch_from (optional, string): Optional brand. data.switch_receipts lists results where the author says they personally left that brand, with the verbatim text, the reason, where they went, and the link. There is no percentage. Rows that are not a switch stay in items. Included in the flat 20 credits. ``` curl -G "https://www.socialcrawl.dev/v1/search/everywhere" \ --data-urlencode "query=kanye west" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /search/forums Fused forum search across Reddit, Hacker News, and Naver 지식iN/카페, with top comments inline on hero threads by default. Credit cost: 10 (override; tier standard) Parameters: - query (required): Search query (2-256 chars), forwarded to every forum search. - sources (optional, string): Optional CSV allowlist of forum sources (reddit, hackernews, naver_kin, naver_cafe). Mutually exclusive with exclude. - exclude (optional, string): Optional CSV blocklist of forum sources. Mutually exclusive with sources. - comments (optional, string): Comment enrichment toggle (on|off, default on). 'off' returns thread-only. - timeframe (optional, string): Recency window passed to Reddit; HN filtered client-side (all|day|week|month|year, default all). - lookback_days (optional, integer): Alt recency window in days (1-365); HN filtered client-side. - relevance (optional, enum: score | filter): Optional. Without it every thread already carries items[].computed.relevance ({ p, sense, on_topic }: is the thread about what your query means, or a different thing that shares its words?), free, in the fused order (see judgments). score also reorders threads by relevance blended with their fused rank. filter also drops the threads that are not on topic, lists them in data.relevance.dropped_ids, and counts stance_split, question_share and top_communities over the threads it kept. A thread that could not be judged, or is too short to judge, is never dropped and carries relevance: null. Omit it to keep every thread in the fused order. Included in the flat 10 credits. - relevance_threshold (optional, string): Optional, only with relevance. The probability (0 to 1) a thread must reach to count as on topic. Default 0.5. Lower keeps more threads, higher keeps fewer. - judgments (optional, enum: on | off): Optional. on (default) or off. On, every thread also carries items[].computed.relevance ({ p, sense, on_topic }) from the same stance judgment, with data.relevance (mode default) counting it; the fused order is kept and nothing is dropped (relevance=score or filter reorders and drops). off returns the response without it. Included in the flat 10 credits. ``` curl -G "https://www.socialcrawl.dev/v1/search/forums" \ --data-urlencode "query=airpods pro 3 battery" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /search/creators Fused creator discovery across TikTok, Threads, and Instagram, ranked by relevance, followers, and verification. YouTube, X, and Facebook people are opt-in via sources=. Credit cost: 10-24 (metered): 10 credits flat, with a coverage-based partial refund. A search no source has a creator for answers 200 with an empty `creators` and `empty_reason: no_matches`, at 0 credits. Adding brief= adds 2 credits, refunded when the judging step could not answer for every judged creator. With country=, the searches and lookups it runs are billed on top, each at its own endpoint's price: 1 credit for the TikTok search page, 1 for the Google page and 1 per Instagram profile looked up (at most 10, and free for a profile looked up recently), so at most 12 more. data.legs lists each one with its credits, and the 10 are refunded when no creator is found. A cached page is free. Parameters: - query (required): Niche or topic (2-256 chars), forwarded to every profile search. - sources (optional, string): Optional CSV allowlist of creator sources (tiktok, threads, instagram, youtube, twitter, facebook). Mutually exclusive with exclude. Omit to keep the default TikTok + Threads + Instagram walk. With country, only tiktok and instagram (the default), and any other source is a free 400. - exclude (optional, string): Optional CSV blocklist of creator sources. Mutually exclusive with sources. - min_followers (optional, integer): Drop fused creators whose follower count is below this integer floor. - verified_only (optional, boolean): When true, keep only verified creators after fusion. - sort (optional, enum: relevance | followers | verification): Which axis dominates rank: relevance (default), followers, or verification. - brief (optional, string): Optional. What you want to find, in your own words (3-300 chars), for example brief=Korean creators who review skincare products. The first 40 creators are judged from their name, handle and bio: does the account post about your brief (fits_brief, 0 to 1), and what kind of account is it (individual_creator, brand_or_business, media_or_publisher, fan_or_repost_page, cannot_tell; null when unsure). With sort=relevance (the default) they are re-ranked so creators who fit and are individual people lead; other sorts keep their order and only gain the labels. These are signals to review, not a finding. Adds 2 credits. - relevance (optional, enum: score | filter): Optional, only with brief. score (default) ranks and labels; filter also drops judged creators that do not fit the brief and lists them in data.brief.dropped_ids. A creator that could not be judged is never dropped. - judgments (optional, enum: on | off): Optional. on (default) or off. On, the first 40 creators also carry account ({ account_kind: individual_creator, brand_or_business, media_or_publisher, fan_or_repost_page, cannot_tell or null when unsure, account_kind_confidence, named_person }), judged from their name, handle and bio, and data.account_kinds reports how many were judged. The order is unchanged and it is free. off returns the response without it. - country (optional, enum: CL | AR | MX | CO | PE | EC | UY | ES | BR): Optional. Two-letter market code (CL, AR, MX, CO, PE, EC, UY, ES, BR) to find the creators who post about your query in that market, instead of searching account names. Runs on TikTok and Instagram: the market's name is added to your query, TikTok creators are kept when the video's own region is the market and its text is about your query, and Instagram creators when they declare that country on their About this account panel. Each creator carries country and evidence, and data.country reports the query sent and what was kept and dropped per source. The searches and lookups it runs are billed on top at their own price, at most 12 credits. Any other code is a free 400. ``` curl -G "https://www.socialcrawl.dev/v1/search/creators" \ --data-urlencode "query=skincare routine" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /search/multi One query, each platform's own search results, in one call at each platform's own price. Credit cost: 0-30 (metered): Metered, usually 1-14 credits: each platform is billed at its own endpoint's price, per platform, exactly as a direct call to that endpoint is billed (TikTok, Instagram, YouTube, Reddit, Threads, X and Facebook 1 credit a page, Telegram 2, LinkedIn 5). A platform whose page found nothing or failed costs nothing, and a platform page served from cache is free. A Threads phrase that Threads does not recognise is expanded into narrower searches, as on /v1/threads/search, and costs 1 credit per search that returned posts. With expand=context every platform runs your phrase plus expand_queries planned searches (3 by default, 3 at most), each billed as its own page, so the range above is multiplied by 1 plus expand_queries and the estimate quotes it that way. The searches inside a platform judge their rows for free; relevant_to= or label= is billed once on the request, for the rows of the merged page. data.sources..credits shows each platform's charge and credits_used the total. Relevance against your query (by default, or with relevance=) is free. Adding relevant_to= holds 8 extra credits and refunds down to 1 credit per started 25 rows that were newly judged: rows already judged for the same topic are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. The default labels (sponsored, intent, niche) are free. label=mention, quality, ai_style, ai_written, injection, or label=intent with offer=, holds 8 extra credits and refunds down to 1 credit per started 25 posts that were newly judged: posts already labelled are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. Parameters: - query (required): Keyword or phrase, sent unchanged to every platform (1-500 characters). Each platform matches it the way its own search endpoint does: Reddit matches any of the words unless you wrap a phrase in double quotes, and a phrase Threads does not recognise is expanded into narrower searches. - platforms (optional, string): Comma-separated platforms to search: tiktok, instagram, youtube, reddit, threads, twitter, facebook, linkedin, telegram. Default tiktok,instagram,youtube,reddit,threads. Each one costs its own endpoint's page price, and only when its page came back with rows. - since (optional, string): Keep only posts published on or after this date on every platform: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. A row with no date is kept. A platform page trimmed to nothing still costs its page, as it would on that platform's own endpoint. When you do not send a platform's own date filter, the narrowest one that reaches this date is sent for you on TikTok, YouTube and Reddit (an exact date on Threads, Facebook and X); data.window says which. - until (optional, string): Keep only posts published on or before this date on every platform: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. A row with no date is kept. Sent natively as Threads and Facebook end_date and the X until: operator; the other platforms have no upper bound of their own, so their newer rows are dropped from the page and still cost the page. Use since and until together for a calendar month. - expand (optional, enum: context): Optional. context also searches up to three short phrases written from your query and, when you send it, relevant_to: the words the people who wrote the posts actually use. Native search reads a query literally, so a long phrase such as fake byd spare part sellers scam misses a seller who writes BYD spare parts Sharjah. Your own query always runs as well. Every row carries matched_query, the search that found it; data.expansion lists the searches with the rows each one added and its credits; data.sources..by_query has each search's own cursor. Each extra search is billed on each platform exactly like your query (1 credit a page with rows, LinkedIn 5), and writing the searches is free. If they cannot be written in time, one search is made from the product words in your query (data._warnings says expand_fallback), or, when there are none to drop, only your query runs (expand_unavailable). Each search on each platform is one in-flight request on your API key, so one expanded call on six platforms holds up to 24 of the key's 50 at once; a search the limit refuses costs nothing and data._warnings says searches_limited. - expand_queries (optional, integer): Optional, only with expand=context. How many extra searches to run, 1 to 3 (default 3). Each one can cost up to one more page per platform. - tiktok.date_posted (optional, enum: yesterday | this-week | this-month | last-3-months | last-6-months | all-time): TikTok only. Sent to /v1/tiktok/search as `date_posted`, exactly as that endpoint takes it. One of: yesterday, this-week, this-month, last-3-months, last-6-months, all-time. - tiktok.sort_by (optional, enum: relevance | most-liked | date-posted): TikTok only. Sent to /v1/tiktok/search as `sort_by`, exactly as that endpoint takes it. One of: relevance, most-liked, date-posted. - tiktok.region (optional, string): TikTok only. Sent to /v1/tiktok/search as `region`, exactly as that endpoint takes it. - instagram.date_posted (optional, enum: last-week | last-month | last-year): Instagram only. Sent to /v1/instagram/search/reels as `date_posted`, exactly as that endpoint takes it. One of: last-week, last-month, last-year. - instagram.include (optional, enum: creator): Instagram only. Sent to /v1/instagram/search/reels as `include`, exactly as that endpoint takes it. One of: creator. - youtube.uploadDate (optional, enum: today | this_week | this_month | this_year): YouTube only. Sent to /v1/youtube/search as `uploadDate`, exactly as that endpoint takes it. One of: today, this_week, this_month, this_year. - youtube.sortBy (optional, enum: relevance | popular): YouTube only. Sent to /v1/youtube/search as `sortBy`, exactly as that endpoint takes it. One of: relevance, popular. - youtube.type (optional, enum: videos | shorts): YouTube only. Sent to /v1/youtube/search as `type`, exactly as that endpoint takes it. One of: videos, shorts. - youtube.duration (optional, enum: under_3_min | between_3_and_20_min | over_20_min): YouTube only. Sent to /v1/youtube/search as `duration`, exactly as that endpoint takes it. One of: under_3_min, between_3_and_20_min, over_20_min. - youtube.region (optional, string): YouTube only. Sent to /v1/youtube/search as `region`, exactly as that endpoint takes it. - reddit.sort (optional, enum: relevance | new | top | comment_count): Reddit only. Sent to /v1/reddit/search as `sort`, exactly as that endpoint takes it. One of: relevance, new, top, comment_count. - reddit.timeframe (optional, enum: all | day | week | month | year): Reddit only. Sent to /v1/reddit/search as `timeframe`, exactly as that endpoint takes it. One of: all, day, week, month, year. - threads.start_date (optional, string): Threads only. Sent to /v1/threads/search as `start_date`, exactly as that endpoint takes it. - threads.end_date (optional, string): Threads only. Sent to /v1/threads/search as `end_date`, exactly as that endpoint takes it. - twitter.sort (optional, enum: latest | top): X only. Sent to /v1/twitter/search/tweets as `sort`, exactly as that endpoint takes it. One of: latest, top. - facebook.start_date (optional, string): Facebook only. Sent to /v1/facebook/search/posts as `start_date`, exactly as that endpoint takes it. - facebook.end_date (optional, string): Facebook only. Sent to /v1/facebook/search/posts as `end_date`, exactly as that endpoint takes it. - facebook.recent_posts (optional, boolean): Facebook only. Sent to /v1/facebook/search/posts as `recent_posts`, exactly as that endpoint takes it. - facebook.location_uid (optional, string): Facebook only. Sent to /v1/facebook/search/posts as `location_uid`, exactly as that endpoint takes it. - linkedin.sort_by (optional, enum: date_posted | relevance): LinkedIn only. Sent to /v1/linkedin/search/posts as `sort_by`, exactly as that endpoint takes it. One of: date_posted, relevance. - linkedin.date_posted (optional, enum: past_24h | past_week | past_month): LinkedIn only. Sent to /v1/linkedin/search/posts as `date_posted`, exactly as that endpoint takes it. One of: past_24h, past_week, past_month. - linkedin.content_type (optional, enum: videos | photos | jobs | live_videos | documents | collaborative_articles): LinkedIn only. Sent to /v1/linkedin/search/posts as `content_type`, exactly as that endpoint takes it. One of: videos, photos, jobs, live_videos, documents, collaborative_articles. - telegram.date_posted (optional, enum: last-hour | last-day | last-week | last-month | last-year): Telegram only. Sent to /v1/telegram/search as `date_posted`, exactly as that endpoint takes it. One of: last-hour, last-day, last-week, last-month, last-year. - telegram.region (optional, string): Telegram only. Sent to /v1/telegram/search as `region`, exactly as that endpoint takes it. - relevance (optional, enum: score | filter): Optional. Without this param every row already carries computed.relevance against your query, free ({ p, sense, depth, spam }: is this row about what your query means, or a different thing that shares its words?), and nothing is dropped or reordered. score asks for it explicitly and waits for every row; filter also drops the rows that are not about your query, and lists their ids in data.relevance.dropped_ids. Your query is the topic; nothing to configure. A row that could not be judged is never dropped and carries relevance: null. Pagination is unchanged, so a filtered page can hold fewer rows. Free with your query as the topic; with relevant_to it adds 1 credit per started 25 newly judged rows (rows already judged for the same topic are free, and so is a cached page). - relevance_threshold (optional, string): Optional, only with relevance. The probability (0 to 1) a row must reach to be kept by relevance=filter. Default 0.5. Lower keeps more rows, higher keeps fewer. - relevant_to (optional, string): Optional, only with relevance. Up to 200 characters describing what you mean, used as the topic instead of the query. Use it when the query is ambiguous, for example query=cleopatra with relevant_to=Cleopatra, the IGT slot game. - judgments (optional, enum: on | off): Optional, on (the default) or off. By default every row gains free SocialCrawl judgments (computed.labels, and computed.relevance on search endpoints), reported in data.labels (mode default) and data.relevance (origin default), each with a status (complete, partial or skipped) and pending: the rows still being judged when the page was sent, which carry null now and are filled on your next call or cached read. Default judgments never add credits, never change an existing field, and never drop or reorder a row. off returns the page exactly as before, with none of those keys. label=none does the same. - dry_run (optional, enum: 1): Optional. When 1, return a cost preview for this labelled or relevance-filtered request without fetching the page or judging any row. data.estimate reports rows_expected, rows_cached, label_credits_min, label_credits_max and base_credits. 0 credits charged. - fit (optional, enum: goal): Optional. When goal, keep the rows and fields needed for the goal you pass in goal= (plus any that are uncertain, and the first and last), and replace the rest with a stub. data.held_back lists the held ids and a recall id that re-reads the full page from cache at no extra charge. Without this param the page is unchanged. - goal (optional, string): Required by fit=goal. What you are trying to do, in your own words, up to 300 characters. - fit_tokens (optional, integer): Optional, only with fit=goal. Soft cap on how much of the page to keep, in tokens. Uncertain blocks and the first and last block are kept even if they exceed it. - label (optional, string): Optional CSV of SocialCrawl labels to add to every post. Without this param every page already carries sponsored, intent and niche, free; label= adds the labels you name to them (the defaults keep running). sponsored, intent and niche are free when asked for too; mention, quality, ai_style, intent with offer=, and injection add 1 credit per started 25 newly judged posts. judgments=off (or label=none) turns the default labels off. sponsored: is the post a paid or gifted promotion (p, 0 to 1), did it carry a disclosure marker such as #ad or 광고 (disclosed), is it likely paid with no marker (undisclosed), and which of the accounts it mentions does it promote (brand, or null). These are signals to review, never a finding. mention (needs brand=): is the post about that brand rather than something that shares its name (about_brand, 0 to 1), how it feels about the brand on five levels (sentiment_level 0 to 4 and sentiment_score 0 to 1, null when the post is not about the brand), is it sarcastic, which aspect it talks about (taste_or_quality, price_or_value, availability_or_delivery, health_or_safety, advertising_or_campaign, customer_service, none), and did the author buy or use it (first_hand). intent: what the author is mainly doing (label: asking_for_recommendation, comparing_options, switching_away, complaining, promoting, news_or_discussion, other, or null when unsure, with confidence), whether they read as a potential buyer rather than a seller (buyer, seller), how pressing the need is (urgency, 0 to 3), and, when you pass offer=, whether your offer would plausibly help them (fits_offer). niche: which of the 33 niches of the published taxonomy sc-niche-v1 the caption belongs to, or personal_no_niche, or other (label, confidence, taxonomy), with label null when the caption is too thin to tell or the pick is unsure. It reads the caption only, not the video. quality: how much checkable detail the caption carries (fact_density 0 to 3), whether it mainly asks for likes, replies, shares, follows or tags (engagement_bait, 0 to 1), whether it is written to provoke anger as a way to get engagement (rage_bait, 0 to 1; about the writing, never the side taken), whether it only repeats someone else's news or view (secondhand, 0 to 1), and what the post is mainly doing (post_aim: inform, opinion, sell, entertain, provoke, other). exclude=engagement_bait drops posts whose engagement_bait is 0.8 or higher. ai_style: writing patterns common in AI-generated text, judged on the text alone. It does not say who wrote the post, and a person who writes in this style is flagged too. Six patterns are judged, each a probability (0 to 1): binary_contrast ("This is not X. It's Y."), faux_insight ("Here's what nobody tells you" and throat-clearing openers), colon_reveal ("The best part: it learns."), profound_kicker (ends on an aphorism or mic-drop line), puffery ("marks a pivotal moment", "highlighting the importance of") and rhetorical_setup ("What if I told you", a question the post answers itself, "Not a X. Not a Y. A Z."). Four are counted in the text: slop_words (words and phrases such as delve, leverage, robust, game changer, at the end of the day), em_dashes, emoji_bullets (lines that start with an emoji or a symbol bullet) and one_line_paragraphs (paragraphs that are one short line). pattern_count is how many patterns are present (a judged pattern at 0.8 or higher, slop_words 1 or more, em_dashes 2 or more, emoji_bullets 2 or more, one_line_paragraphs 5 or more) and markers lists their names, comma-separated. A post with less than about eight words of text is only counted, with every judged pattern 0. The judged patterns read the first 400 and the last 150 characters of a long post, and the counts read the whole post. exclude=ai_style drops posts with 2 or more patterns. ai_written: how likely the post was written by an AI model, from SocialCrawl's own text classifier, reading the opening of the post (about 1,000 characters): p (0 to 1, calibrated), level (likely (p 0.95 or higher), possible (p 0.71 or higher) or unlikely) and model (the model name; a new name means a new scale). A post under 8 words is too short to read: p null, level too_short. It is a probability, not proof: a person can be flagged and an AI-written post can pass, and the shorter the post the less certain it is. exclude=ai_written drops likely posts. injection flags text that addresses an AI system and tries to direct it (flagged, p); it never drops or rewrites a row. A post that could not be judged carries labels: null. Posts already labelled for anyone are free, and so is a cached page. data.labels reports what was judged and billed. - exclude (optional, string): Optional CSV (engagement_bait, ai_style, ai_written). engagement_bait, only with label=quality, drops posts whose engagement_bait probability is 0.8 or higher. ai_style, only with label=ai_style, drops posts that show 2 or more of its writing patterns (pattern_count 2 or higher). ai_written, only with label=ai_written, drops posts whose level is likely (p 0.95 or higher), or whose p reaches your ai_written_threshold; a post too short to read is never dropped. Dropped posts are listed in data.labels.dropped_ids. A post that could not be judged is never dropped. - ai_written_threshold (optional, string): Optional, only with label=ai_written and exclude=ai_written. The p (0 to 1) at which exclude=ai_written drops a post. Default 0.95 (level likely). 0.71 also drops possible posts: more AI posts go, and more posts by people with them. - brand (optional, string): Required by label=mention, ignored otherwise. The brand or product the posts are judged against, up to 300 characters, for example brand=Buldak. Without it label=mention is skipped with the warning label_mention_needs_brand and is not billed. - brand_description (optional, string): Optional, with label=mention. One plain sentence saying what the brand is, for example brand_description=Samyang's spicy instant noodle brand. Use it when the name is also an ordinary word or another company's name. - offer (optional, string): Optional, with label=intent. One or two plain sentences saying what you sell, up to 300 characters, for example offer=A web design agency that builds websites for small businesses. Adds fits_offer to every post; without it fits_offer is null. - label_evidence (optional, enum: 1): Optional, only with label=. When 1, every labelled row also carries computed.labels_evidence. = { quote, sentence_index }: the sentence in the row that most clearly shows the label, copied verbatim. Absent or null when no single sentence shows it. ``` curl -G "https://www.socialcrawl.dev/v1/search/multi" \ --data-urlencode "query=matcha latte" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /search/news Planned multi-country news search: one query, localized and fanned out across two independent news indexes in a single call. Credit cost: 2-62 (metered): 2 credits + 1 credit per google leg that returns at least one article (upfront hold 2 + min(5 x countries, max_legs, 12), maximum 14 on the default engine). Adding the bing engine adds 1 credit per 5 articles it returns, because that engine is priced per article rather than per call; the absolute ceiling with both engines is 62. The hold always settles down to the actual charge, and empty, skipped or failed legs bill 0. Parameters: - query (required): News topic or question (1-500 chars). Boolean AND/OR/NOT and quoted phrases are supported; Google advanced operators (site:, intitle:, before:) are rejected. - countries (optional, string): CSV of ISO 3166-1 alpha-2 country codes to search (1-12 of the 50 supported editions, default US). Each country searches its news edition in its default language; duplicates collapse. - engines (optional, string): CSV of news indexes to search: google (default) and bing. Asking for both runs the same plan on each and merges the results, which roughly doubles distinct articles and adds a snippet to most rows. The bing engine serves 49 of the 50 editions (not CN) and is priced per article; `publisher` and `from`/`to` are google-only. - sort (optional, enum: relevance | date): Ordering of the merged articles: relevance (default, each engine's own ranking) or date (newest first, undated articles last). - time_range (optional, enum: day | week | month | year): Recency window (day | week | month | year, default day). Ignored when from/to is present. - from (optional, string): Lower publish-date bound, YYYY-MM-DD or Unix seconds. Pins every leg to the primary news source. - to (optional, string): Upper publish-date bound, YYYY-MM-DD or Unix seconds. Pins every leg to the primary news source. - publisher (optional, string): Bare publisher domain filter (e.g. bbc.com; no scheme or path). Pins every leg to the primary news source. - depth (optional, integer): Articles requested per leg (10-100 in steps of 10, default 10). The google engine is priced per call, so depth is free there; the bing engine is priced per article and caps its own legs at 20. - max_legs (optional, integer): Hard cap on billable legs (1-12, default 12). The upfront hold shrinks with it. - group (optional, enum: stories): Optional. stories groups the articles that report the same event across editions, outlets and languages into data.stories[] ({ id, head_id, member_ids, size, outlets, outlet_count, countries, languages, first_seen, last_seen, judged }), where every id is an article's canonical_url (or url). items[] and its order are unchanged. data.story_grouping reports how many articles were judged and what it cost. An article that could not be judged is its own story with judged: false; if grouping is unavailable, stories is null and data._warnings says stories_unavailable. JSON responses only (ignored on the event stream). Grouping is on by default now (see judgments); group=stories asks for it explicitly with a longer time budget. Free. - judgments (optional, enum: on | off): Optional. on (default) or off. On, JSON responses also carry data.stories and data.story_grouping (see group), grouped under a short time budget; when grouping does not finish in time, stories is null and data._warnings says stories_pending (stories_unavailable when it failed). items[], their order and the price are unchanged. off returns the response without them. ``` curl -G "https://www.socialcrawl.dev/v1/search/news" \ --data-urlencode "query=samsung galaxy launch" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## Sephora ### GET /sephora/product Get a Sephora product by id or URL (oneOf: product_id|url) Credit cost: 5 (advanced) Parameters: - product_id (optional, string): Sephora product id, the P-number at the end of a product URL: sephora.com/product/the-ordinary-deciem-natural-moisturizing-factors-ha-P427414 is product id P427414. Product ids also come back on every row of GET /v1/sephora/search and GET /v1/sephora/category. - url (optional, string): Full Sephora product URL, for example https://www.sephora.com/product/the-ordinary-deciem-natural-moisturizing-factors-ha-P427414. The product id is extracted from the path. - sku (optional, string): Sephora sku id for a size or colour variant, for example 2210581. Comes back on search rows as currentSku.skuId and on product detail as currentSku.skuId. - language (optional, enum: en-US | en-CA | fr-CA): Copy locale: en-US (default), en-CA, or fr-CA. - Constraint: one of product_id, url (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/sephora/product" \ --data-urlencode "product_id=P427414" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /sephora/reviews Get Sephora product reviews (oneOf: product_id|url) Credit cost: 5-9 (metered): 5 credits per page. The default labels (sentiment, issue) are free. label=reports, incentivized or injection holds 4 extra credits and refunds down to 1 credit per started 25 reviews that were newly judged (the first 100 reviews of a page are judged): reviews already labelled are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - product_id (optional, string): Sephora product id, the P-number at the end of a product URL: sephora.com/product/the-ordinary-deciem-natural-moisturizing-factors-ha-P427414 is product id P427414. Product ids also come back on every row of GET /v1/sephora/search and GET /v1/sephora/category. - url (optional, string): Full Sephora product URL, for example https://www.sephora.com/product/the-ordinary-deciem-natural-moisturizing-factors-ha-P427414. The product id is extracted from the path. - page (optional, integer): Page number, starting at 1. Prefer the universal cursor parameter. - limit (optional, integer): Reviews per page, 1 to 100. Default 10. - sort_by (optional, enum: most_helpful | highest_rating | lowest_rating | oldest | newest): Review ordering: most_helpful, highest_rating, lowest_rating, oldest, newest. - rating (optional, integer): Keep only reviews with this star value, 1 to 5. - label (optional, string): Optional CSV of SocialCrawl labels to add to every review. Without this param every page already carries sentiment and issue, free; label= adds the labels you name to them (the defaults keep running). sentiment and issue are free when asked for too; reports, incentivized and injection add 1 credit per started 25 newly judged reviews. judgments=off (or label=none) turns the default labels off. They read the review's title and text, never its stars. sentiment: how the reviewer feels, on five levels (level 0 to 4, score_0_1, confidence), plus rating_mismatch, true when the words clearly contradict the star rating (a glowing text under 1 star, a furious one under 5), null when the review has no rating. issue: the main problem the review reports (label: product_defect, sizing_or_fit, shipping_or_delivery, customer_service, price_or_value, missing_feature, other, or none, with confidence; label is null when unsure). reports (needs reports=): does this review say that the thing you describe happened (p, 0 to 1). incentivized: does the reviewer say they got the product free, discounted or rewarded for the review (p), and did the text carry a disclosure such as 'in exchange for my honest review', Vine or 체험단 (disclosed). injection flags text that addresses an AI system and tries to direct it (flagged, p); it never drops or rewrites a row. These are signals to read, never a verdict on a review or a reviewer. A review that could not be judged carries labels: null. Reviews already labelled for anyone are free, and so is a cached page. data.labels reports what was judged and billed. - reports (optional, string): Required by label=reports, ignored otherwise. What to look for, in plain words, up to 300 characters, for example reports=the battery drains quickly or reports=배송이 늦음. Every review gains labels.reports.p, the probability that it says so, in any wording or language. Without it label=reports is skipped with the warning label_reports_needs_reports and is not billed. - judgments (optional, enum: on | off): Optional, on (the default) or off. By default every row gains free SocialCrawl judgments (computed.labels, and computed.relevance on search endpoints), reported in data.labels (mode default) and data.relevance (origin default), each with a status (complete, partial or skipped) and pending: the rows still being judged when the page was sent, which carry null now and are filled on your next call or cached read. Default judgments never add credits, never change an existing field, and never drop or reorder a row. off returns the page exactly as before, with none of those keys. label=none does the same. - dry_run (optional, enum: 1): Optional. When 1, return a cost preview for this labelled or relevance-filtered request without fetching the page or judging any row. data.estimate reports rows_expected, rows_cached, label_credits_min, label_credits_max and base_credits. 0 credits charged. - label_evidence (optional, enum: 1): Optional, only with label=. When 1, every labelled row also carries computed.labels_evidence. = { quote, sentence_index }: the sentence in the row that most clearly shows the label, copied verbatim. Absent or null when no single sentence shows it. - fit (optional, enum: goal): Optional. When goal, keep the rows and fields needed for the goal you pass in goal= (plus any that are uncertain, and the first and last), and replace the rest with a stub. data.held_back lists the held ids and a recall id that re-reads the full page from cache at no extra charge. Without this param the page is unchanged. - goal (optional, string): Required by fit=goal. What you are trying to do, in your own words, up to 300 characters. - fit_tokens (optional, integer): Optional, only with fit=goal. Soft cap on how much of the page to keep, in tokens. Uncertain blocks and the first and last block are kept even if they exceed it. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. - Constraint: one of product_id, url (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/sephora/reviews" \ --data-urlencode "product_id=P427414" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /sephora/search Search Sephora products by keyword Credit cost: 5 (advanced) Parameters: - query (required): Search keyword, for example moisturizer or niacinamide. - page (optional, integer): Page number, starting at 1. Prefer the universal cursor parameter. - page_size (optional, integer): Products per page, 1 to 60. Default 60. - sort_by (optional, enum: best_selling | top_rated | price_low | price_high | new): Result ordering: best_selling (default), top_rated, price_low, price_high, or new. On a moisturizer keyword search, new returned the same ids as best_selling, so treat new as unverified. - min_rating (optional, integer): Keep products whose average rating is at least this, 1 to 5. - min_price (optional, integer): Minimum list price in US dollars. - max_price (optional, integer): Maximum list price in US dollars. - is_new (optional, boolean): When true, keep products Sephora flags as new. - category_id (optional, string): Optional category id from GET /v1/sephora/categories to narrow the search. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/sephora/search" \ --data-urlencode "query=moisturizer" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /sephora/search/suggestions Get Sephora search suggestions Credit cost: 1 (standard) Parameters: - query (required): Partial search query to autocomplete, for example moistur. - language (optional, enum: en-US | en-CA | fr-CA): Copy locale: en-US (default), en-CA, or fr-CA. ``` curl -G "https://www.socialcrawl.dev/v1/sephora/search/suggestions" \ --data-urlencode "query=moistur" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /sephora/category Browse Sephora products in a category Credit cost: 5 (advanced) Parameters: - category_id (required): Sephora category id, for example cat1100057 for Luxury Beauty. Ids come from GET /v1/sephora/categories. - page (optional, integer): Page number, starting at 1. Prefer the universal cursor parameter. - page_size (optional, integer): Products per page, 1 to 60. Default 60. - sort_by (optional, enum: best_selling | top_rated | price_low | price_high | new): Result ordering: best_selling (default), top_rated, price_low, price_high, or new. On a moisturizer keyword search, new returned the same ids as best_selling, so treat new as unverified. - min_price (optional, integer): Minimum list price in US dollars. - max_price (optional, integer): Maximum list price in US dollars. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/sephora/category" \ --data-urlencode "category_id=cat1100057" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /sephora/categories List Sephora root categories Credit cost: 1 (standard) ``` curl "https://www.socialcrawl.dev/v1/sephora/categories" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /sephora/category/data Get a Sephora category and its children Credit cost: 1 (standard) Parameters: - id (required): Sephora category id, for example cat1100057. Ids come from GET /v1/sephora/categories. ``` curl -G "https://www.socialcrawl.dev/v1/sephora/category/data" \ --data-urlencode "id=cat1100057" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /sephora/brands List Sephora brands Credit cost: 1 (standard) ``` curl "https://www.socialcrawl.dev/v1/sephora/brands" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /sephora/brand/products List Sephora products for a brand Credit cost: 5 (advanced) Parameters: - id (required): Brand short name from GET /v1/sephora/brands, for example gucci or AAVRANI. - page (optional, integer): Page number, starting at 1. Prefer the universal cursor parameter. - page_size (optional, integer): Products per page, 1 to 60. Default 60. - sort_by (optional, enum: best_selling | top_rated | price_low | price_high | new): Result ordering: best_selling (default), top_rated, price_low, price_high, or new. On a moisturizer keyword search, new returned the same ids as best_selling, so treat new as unverified. - min_price (optional, integer): Minimum list price in US dollars. - max_price (optional, integer): Maximum list price in US dollars. - min_rating (optional, integer): Keep products whose average rating is at least this, 1 to 5. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/sephora/brand/products" \ --data-urlencode "id=gucci" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /sephora/stores Find Sephora stores near a coordinate Credit cost: 1 (standard) Parameters: - location_coordinate (required): Latitude and longitude as lat,lng, for example 37.4418834,-122.1430195. - radius (optional, integer): Search radius in miles, 1 to 100. Default 50. ``` curl -G "https://www.socialcrawl.dev/v1/sephora/stores" \ --data-urlencode "location_coordinate=37.4418834,-122.1430195" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /sephora/availability Get Sephora in-store availability for a sku (oneOf: location_coordinate|latitude) Credit cost: 5 (advanced) Parameters: - sku (required): Sephora sku id, for example 2210581. Comes back on product detail as currentSku.skuId. - latitude (optional, string): Latitude. Use with longitude when you do not pass location_coordinate. - longitude (optional, string): Longitude. Use with latitude when you do not pass location_coordinate. - location_coordinate (optional, string): Latitude and longitude as lat,lng, for example 37.4418834,-122.1430195. - radius (optional, integer): Search radius in miles, 1 to 100. Default 50. - Constraint: one of location_coordinate, latitude (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/sephora/availability" \ --data-urlencode "sku=2210581" \ --data-urlencode "location_coordinate=37.4418834,-122.1430195" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## Snapchat ### GET /snapchat/profile Get Snapchat user profile Credit cost: 1 (standard) Parameters: - handle (required): Snapchat username ``` curl -G "https://www.socialcrawl.dev/v1/snapchat/profile" \ --data-urlencode "handle=djkhaled305" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /snapchat/spotlight/comments List comments on a Snapchat Spotlight Credit cost: 1 (standard) Parameters: - url (required): Full URL of the Snapchat Spotlight snap. - cursor (optional, string): Cursor from the previous response to fetch the next page. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/snapchat/spotlight/comments" \ --data-urlencode "url=https://www.snapchat.com/@lilah4823/spotlight/W7_EDlXWTBiXAEEniNoMPwAAYY2pvZnd0ZXVmAZ8fgG8pAZ8ff-_IAAAAAQ" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## Spotify ### GET /spotify/artist Get a Spotify artist (oneOf: id|url) Credit cost: 1 (standard) Parameters: - id (optional, string): Spotify artist ID. - url (optional, string): Spotify artist URL. - Constraint: one of id, url (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/spotify/artist" \ --data-urlencode "url=https://open.spotify.com/artist/0cGUm45nv7Z6M6qdXYQGTX" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /spotify/track Get a Spotify track (oneOf: id|url) Credit cost: 1 (standard) Parameters: - id (optional, string): Spotify track ID. - url (optional, string): Spotify track URL. - Constraint: one of id, url (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/spotify/track" \ --data-urlencode "url=https://open.spotify.com/track/1ITJflybJsfarsUtiBvkfK" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /spotify/album Get a Spotify album (oneOf: id|url) Credit cost: 1 (standard) Parameters: - id (optional, string): Spotify album ID. - url (optional, string): Spotify album URL. - Constraint: one of id, url (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/spotify/album" \ --data-urlencode "url=https://open.spotify.com/album/0pgrg7phBbnwGJ2HBEl9EG" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /spotify/search Search Spotify Credit cost: 1 (standard) Parameters: - query (required): Search query. ``` curl -G "https://www.socialcrawl.dev/v1/spotify/search" \ --data-urlencode "query=my first million" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /spotify/podcast Get a Spotify podcast (oneOf: id|url) Credit cost: 1 (standard) Parameters: - id (optional, string): Spotify podcast (show) ID. - url (optional, string): Spotify podcast (show) URL. - Constraint: one of id, url (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/spotify/podcast" \ --data-urlencode "url=https://open.spotify.com/show/3mliji9352UAk3XnWElnDV" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /spotify/podcast/episodes List a Spotify podcast's episodes (oneOf: id|url) Credit cost: 1 (standard) Parameters: - id (optional, string): Spotify podcast (show) ID. - url (optional, string): Spotify podcast (show) URL. - cursor (optional, integer): Cursor returned by the previous response. Omit for page 1. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. - Constraint: one of id, url (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/spotify/podcast/episodes" \ --data-urlencode "url=https://open.spotify.com/show/4rOoJ6Egrf8K2IrywzwOMk" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## Target ### GET /target/product Get a Target product by TCIN Credit cost: 5 (advanced) Parameters: - tcin (required): Target catalogue id (TCIN), the numeric id at the end of a target.com product URL: target.com/p//-/A-92148487 is TCIN 92148487. Target rolls a colour or size variant up to its parent product, so the id returned by GET /v1/target/product can differ from the one you sent; the id you asked for is preserved at product.ext.requested_id. ``` curl -G "https://www.socialcrawl.dev/v1/target/product" \ --data-urlencode "tcin=92148487" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /target/reviews Get Target product reviews Credit cost: 5-9 (metered): 5 credits per page. The default labels (sentiment, issue) are free. label=reports, incentivized or injection holds 4 extra credits and refunds down to 1 credit per started 25 reviews that were newly judged (the first 100 reviews of a page are judged): reviews already labelled are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - tcin (required): Target catalogue id (TCIN), the numeric id at the end of a target.com product URL: target.com/p//-/A-92148487 is TCIN 92148487. Target rolls a colour or size variant up to its parent product, so the id returned by GET /v1/target/product can differ from the one you sent; the id you asked for is preserved at product.ext.requested_id. - page (optional, integer): Page number, starting at 1. Prefer the universal cursor parameter. - label (optional, string): Optional CSV of SocialCrawl labels to add to every review. Without this param every page already carries sentiment and issue, free; label= adds the labels you name to them (the defaults keep running). sentiment and issue are free when asked for too; reports, incentivized and injection add 1 credit per started 25 newly judged reviews. judgments=off (or label=none) turns the default labels off. They read the review's title and text, never its stars. sentiment: how the reviewer feels, on five levels (level 0 to 4, score_0_1, confidence), plus rating_mismatch, true when the words clearly contradict the star rating (a glowing text under 1 star, a furious one under 5), null when the review has no rating. issue: the main problem the review reports (label: product_defect, sizing_or_fit, shipping_or_delivery, customer_service, price_or_value, missing_feature, other, or none, with confidence; label is null when unsure). reports (needs reports=): does this review say that the thing you describe happened (p, 0 to 1). incentivized: does the reviewer say they got the product free, discounted or rewarded for the review (p), and did the text carry a disclosure such as 'in exchange for my honest review', Vine or 체험단 (disclosed). injection flags text that addresses an AI system and tries to direct it (flagged, p); it never drops or rewrites a row. These are signals to read, never a verdict on a review or a reviewer. A review that could not be judged carries labels: null. Reviews already labelled for anyone are free, and so is a cached page. data.labels reports what was judged and billed. - reports (optional, string): Required by label=reports, ignored otherwise. What to look for, in plain words, up to 300 characters, for example reports=the battery drains quickly or reports=배송이 늦음. Every review gains labels.reports.p, the probability that it says so, in any wording or language. Without it label=reports is skipped with the warning label_reports_needs_reports and is not billed. - judgments (optional, enum: on | off): Optional, on (the default) or off. By default every row gains free SocialCrawl judgments (computed.labels, and computed.relevance on search endpoints), reported in data.labels (mode default) and data.relevance (origin default), each with a status (complete, partial or skipped) and pending: the rows still being judged when the page was sent, which carry null now and are filled on your next call or cached read. Default judgments never add credits, never change an existing field, and never drop or reorder a row. off returns the page exactly as before, with none of those keys. label=none does the same. - dry_run (optional, enum: 1): Optional. When 1, return a cost preview for this labelled or relevance-filtered request without fetching the page or judging any row. data.estimate reports rows_expected, rows_cached, label_credits_min, label_credits_max and base_credits. 0 credits charged. - label_evidence (optional, enum: 1): Optional, only with label=. When 1, every labelled row also carries computed.labels_evidence. = { quote, sentence_index }: the sentence in the row that most clearly shows the label, copied verbatim. Absent or null when no single sentence shows it. - fit (optional, enum: goal): Optional. When goal, keep the rows and fields needed for the goal you pass in goal= (plus any that are uncertain, and the first and last), and replace the rest with a stub. data.held_back lists the held ids and a recall id that re-reads the full page from cache at no extra charge. Without this param the page is unchanged. - goal (optional, string): Required by fit=goal. What you are trying to do, in your own words, up to 300 characters. - fit_tokens (optional, integer): Optional, only with fit=goal. Soft cap on how much of the page to keep, in tokens. Uncertain blocks and the first and last block are kept even if they exceed it. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/target/reviews" \ --data-urlencode "tcin=92146817" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /target/category Browse Target products in a category Credit cost: 5 (advanced) Parameters: - category_id (required): Target category node id, for example hz89j for Kitchen & Dining. Ids come from GET /v1/target/categories, and also appear in the category URLs on target.com (/c//-/N-). - page (optional, integer): Page number, starting at 1. Prefer the universal cursor parameter. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/target/category" \ --data-urlencode "category_id=hz89j" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /target/categories List the Target category taxonomy Credit cost: 1 (standard) ``` curl "https://www.socialcrawl.dev/v1/target/categories" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /target/stores Find Target stores near a location Credit cost: 1 (standard) Parameters: - query (required): Location to search near: a ZIP code (10001), a city (Austin), or a state. Both ZIP and city forms are verified. ``` curl -G "https://www.socialcrawl.dev/v1/target/stores" \ --data-urlencode "query=10001" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## Tavily ### GET /tavily/search Tavily web search with optional LLM-generated answer Credit cost: 1 (standard) Parameters: - query (required): The search query: natural-language free text. - search_depth (optional, enum: basic | advanced | fast | ultra-fast): Latency-vs-relevance tradeoff. `basic` is the default; `advanced` unlocks `chunks_per_source` and higher-relevance ranking. - topic (optional, enum: general | news | finance): Search category. Defaults to `general`. Use `news` for time-sensitive queries and `finance` for market data. - time_range (optional, enum: day | week | month | year | d | w | m | y): Time window relative to now. Accepts `day` / `week` / `month` / `year` (or shorthand `d` / `w` / `m` / `y`). - max_results (optional, integer): Number of results to return (1-20). Defaults to 5. - chunks_per_source (optional, integer): Max relevant chunks returned per source (1-5). Only honoured when `search_depth=advanced`. Defaults to 3. - include_images (optional, boolean): Include images alongside the result content. - include_image_descriptions (optional, boolean): Include AI-generated descriptions for the returned images. - include_answer (optional, boolean): Include an LLM-generated answer string synthesised from the top sources. - include_raw_content (optional, boolean): Include the raw HTML/text alongside the cleaned content. - include_domains (optional, string): Comma-separated list of domains to restrict results to (e.g. `nytimes.com,reuters.com`). - exclude_domains (optional, string): Comma-separated list of domains to exclude from results. - country (optional, string): ISO 3166-1 alpha-2 country code to bias results toward. - start_date (optional, string): Inclusive lower bound on result publish date (YYYY-MM-DD). - end_date (optional, string): Inclusive upper bound on result publish date (YYYY-MM-DD). ``` curl -G "https://www.socialcrawl.dev/v1/tavily/search" \ --data-urlencode "query=claude opus 4.7 release notes" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /tavily/extract Extract clean content from one or more URLs Credit cost: 1 (standard) Parameters: - urls (required): Comma-separated list of URLs to extract (max 20). - extract_depth (optional, enum: basic | advanced): Extraction strategy. `basic` is the default and faster; `advanced` handles harder pages but takes longer. - format (optional, enum: markdown | text): Output format for the extracted content. `markdown` (default) preserves structure; `text` is plain. - include_images (optional, boolean): Include images extracted from each URL. - include_favicon (optional, boolean): Include the favicon URL for each page. - timeout (optional, integer): Per-URL timeout in seconds (1-60). ``` curl -G "https://www.socialcrawl.dev/v1/tavily/extract" \ --data-urlencode "urls=https://en.wikipedia.org/wiki/Lionel_Messi" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /tavily/map Map a website's sitegraph Credit cost: 1 (standard) Parameters: - url (required): Root URL to begin mapping. - max_depth (optional, integer): Maximum link depth from the root URL. Defaults to 1. - max_breadth (optional, integer): Maximum number of links followed per level (per page). Defaults to 20. - limit (optional, integer): Total number of links the mapper will process before stopping. Defaults to 50. - instructions (optional, string): Natural-language instructions for the mapper (e.g. 'Find all pages related to API documentation'). - select_paths (optional, string): Comma-separated regex patterns: only include URLs whose path matches. - select_domains (optional, string): Comma-separated regex patterns: only include URLs whose domain matches. - exclude_paths (optional, string): Comma-separated regex patterns: exclude URLs whose path matches. - exclude_domains (optional, string): Comma-separated regex patterns: exclude URLs whose domain matches. - allow_external (optional, boolean): Whether to follow / return links to external domains. Defaults to true. - timeout (optional, integer): Maximum time in seconds (10-150). - categories (optional, string): Comma-separated list of category hints to bias mapping toward. ``` curl -G "https://www.socialcrawl.dev/v1/tavily/map" \ --data-urlencode "url=https://docs.tavily.com" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /tavily/crawl Crawl a website with LLM-driven path selection Credit cost: 1 (standard) Parameters: - url (required): Root URL to begin crawling. - max_depth (optional, integer): Maximum link depth from the root URL. Defaults to 1. - max_breadth (optional, integer): Maximum number of links followed per level (per page). Defaults to 20. - limit (optional, integer): Total number of pages the crawler will process before stopping. Defaults to 50. - instructions (optional, string): Natural-language instructions for the crawler (e.g. 'Find all product pages with pricing'). - select_paths (optional, string): Comma-separated regex patterns: only crawl URLs whose path matches. - select_domains (optional, string): Comma-separated regex patterns: only crawl URLs whose domain matches. - exclude_paths (optional, string): Comma-separated regex patterns: skip URLs whose path matches. - exclude_domains (optional, string): Comma-separated regex patterns: skip URLs whose domain matches. - allow_external (optional, boolean): Whether to follow links to external domains. Defaults to true. - extract_depth (optional, enum: basic | advanced): Per-page extraction strategy. `basic` is faster; `advanced` handles harder pages. - format (optional, enum: markdown | text): Output format for extracted content. `markdown` (default) or `text`. - categories (optional, string): Comma-separated list of category hints to bias the crawl toward. ``` curl -G "https://www.socialcrawl.dev/v1/tavily/crawl" \ --data-urlencode "url=https://docs.tavily.com" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## Telegram ### GET /telegram/profile Get Telegram channel profile Credit cost: 1 (standard) Parameters: - handle (required): Public Telegram handle, with or without the leading @, or a t.me channel URL ``` curl -G "https://www.socialcrawl.dev/v1/telegram/profile" \ --data-urlencode "handle=telegram" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /telegram/profile/posts List Telegram channel posts Credit cost: 1 (standard) Parameters: - handle (required): Public Telegram handle, with or without the leading @, or a t.me channel URL - cursor (optional, string): Cursor from the previous page's pagination block. Omit it for the most recent posts. - q (optional, string): Words to search for inside the channel, for example `cheap watches`. Returns one page of the newest messages whose text contains every word (put a phrase in double quotes to require it as written), at the same price as a plain page. Telegram's own search also matches other forms of a word (`watch` for `watches`); those messages are left out, because they do not contain the words. A search page has no cursor, so `q` cannot be combined with `cursor`. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/telegram/profile/posts" \ --data-urlencode "handle=telegram" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /telegram/post Get Telegram post Credit cost: 1 (standard) Parameters: - url (required): Public Telegram post URL, in the form https://t.me/{handle}/{id} ``` curl -G "https://www.socialcrawl.dev/v1/telegram/post" \ --data-urlencode "url=https://t.me/durov/546" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /telegram/search Search Telegram channels by keyword Credit cost: 2-16 (metered): 2 credits for the page. include=details holds 1 credit per channel (at most 10 a page, fewer with limit) and keeps only the channels filled from a fresh lookup, so a page is at most 12 credits; channels served from cache are free, unfilled channels are refunded, and a search that finds no public channel costs 0. Relevance against your query (by default, or with relevance=) is free. Adding relevant_to= holds 4 extra credits and refunds down to 1 credit per started 25 rows that were newly judged: rows already judged for the same topic are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - query (required): Keywords describing the channels or messages to find, for example `byd spare parts`. Put a phrase in double quotes to require it. Keywords only: the search is already limited to public Telegram pages, so a `site:` operator is refused before billing. - page (optional, integer): Page number, starting at 1. Forwarding `pagination.next_cursor` as `cursor` does the same. - date_posted (optional, enum: last-hour | last-day | last-week | last-month | last-year): Only pages the search index dates within this window: last-hour, last-day, last-week, last-month or last-year. Useful for monitoring new activity. - region (optional, string): Two-letter country code (US, AE, IN, KR) whose results to prefer. - limit (optional, integer): Return at most this many channels from the page (1 to 10). With `include=details` it also lowers the credits held, one per channel. - include (optional, enum: details): Set to `details` (one token only) to fill, on every channel in this one call, `author.followers` (the exact subscriber count), `author.bio` (the description), `author.avatar_url`, `author.verified`, and the title where it was null. Holds 1 credit per channel (at most 10 a page) and keeps only the channels filled from a fresh lookup; channels already in cache and channels the lookup cannot fill are refunded, so a page is never more than 12 credits. Read `data.hydration` for the rows, the credits held and kept, and the time. Without it the call is unchanged. - relevance (optional, enum: score | filter): Optional. Without this param every row already carries computed.relevance against your query, free ({ p, sense, depth, spam }: is this row about what your query means, or a different thing that shares its words?), and nothing is dropped or reordered. score asks for it explicitly and waits for every row; filter also drops the rows that are not about your query, and lists their ids in data.relevance.dropped_ids. Your query is the topic; nothing to configure. A row that could not be judged is never dropped and carries relevance: null. Pagination is unchanged, so a filtered page can hold fewer rows. Free with your query as the topic; with relevant_to it adds 1 credit per started 25 newly judged rows (rows already judged for the same topic are free, and so is a cached page). - relevance_threshold (optional, string): Optional, only with relevance. The probability (0 to 1) a row must reach to be kept by relevance=filter. Default 0.5. Lower keeps more rows, higher keeps fewer. - relevant_to (optional, string): Optional, only with relevance. Up to 200 characters describing what you mean, used as the topic instead of the query. Use it when the query is ambiguous, for example query=cleopatra with relevant_to=Cleopatra, the IGT slot game. - judgments (optional, enum: on | off): Optional, on (the default) or off. By default every row gains free SocialCrawl judgments (computed.labels, and computed.relevance on search endpoints), reported in data.labels (mode default) and data.relevance (origin default), each with a status (complete, partial or skipped) and pending: the rows still being judged when the page was sent, which carry null now and are filled on your next call or cached read. Default judgments never add credits, never change an existing field, and never drop or reorder a row. off returns the page exactly as before, with none of those keys. label=none does the same. - dry_run (optional, enum: 1): Optional. When 1, return a cost preview for this labelled or relevance-filtered request without fetching the page or judging any row. data.estimate reports rows_expected, rows_cached, label_credits_min, label_credits_max and base_credits. 0 credits charged. - fit (optional, enum: goal): Optional. When goal, keep the rows and fields needed for the goal you pass in goal= (plus any that are uncertain, and the first and last), and replace the rest with a stub. data.held_back lists the held ids and a recall id that re-reads the full page from cache at no extra charge. Without this param the page is unchanged. - goal (optional, string): Required by fit=goal. What you are trying to do, in your own words, up to 300 characters. - fit_tokens (optional, integer): Optional, only with fit=goal. Soft cap on how much of the page to keep, in tokens. Uncertain blocks and the first and last block are kept even if they exceed it. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/telegram/search" \ --data-urlencode "query=byd spare parts" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## Threads ### GET /threads/profile Get Threads user profile Credit cost: 1 (standard) Parameters: - handle (required): Threads username without the @ symbol ``` curl -G "https://www.socialcrawl.dev/v1/threads/profile" \ --data-urlencode "handle=zuck" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /threads/user/posts List Threads user posts Credit cost: 1-165 (metered): 1 credit for the bundled window of about 15 posts, which is what a call with no `limit`, or `limit` at 15 or below, costs. Above 15 the deeper lane is metered at 3 credits per post RETURNED: the call holds `limit x 3` up front (the 50 maximum holds 150) and refunds down to what came back. With `include=engagement` the call also holds 1 credit per post of the window (15) and keeps 1 for each post whose view count or display name it filled from a fresh lookup; posts served from the post lookup's cache (10 minutes) are free and the rest is refunded, so the default window costs 16 at most. Beside a `limit` above 15 the deeper rows already carry both fields, so the 15 are refunded in full Parameters: - handle (required): Threads username without the @ symbol - since (optional, string): Only posts published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Older posts are left off the page, and the page that reaches one ends the walk: `next_cursor` is not returned and `pagination.stopped_at` is `since`. Pinned posts sit out of date order and never end the walk. The page still costs what a page costs, so a daily poll pays for the pages it walks and no more. - stop_at_id (optional, string): The id (`post.id`) or URL (`post.url`) of the newest post you already hold. The page stops just before it: that post and everything after it are left off, `next_cursor` is not returned, and `pagination.stopped_at` is `known_id`. A pinned post never counts as the stop point. If it is not on this page the page is returned in full with its cursor, so keep walking. `pagination.stopped_at` is `end` when the list ran out first and `null` while there is more to walk. - trim (optional, boolean): Ask the upstream for a stripped record. This is a real shape change, not just less whitespace: a trimmed row carries only the id, text, shortcode, like count, timestamp and author, so media, reply count, share count, view count, topic tag, pinned flag and quoted post all come back null. Same 1 credit either way, so send it only when the text and the like count are all you need. Ignored when `limit` is above 15, because the deeper source has no trimmed mode. - limit (optional, integer): How many posts to collect, 1 to 50. Leave it off, or send 15 or less, and nothing changes: you get the bundled window for exactly 1 credit. Above 15 the posts come from a deeper source and the call is metered at 3 credits per post actually returned, so an account with only 20 public posts costs 60 credits however high you set this. - include (optional, enum: engagement): Send `engagement` to fill `engagement.views` and `post.author.display_name` on each post of the default window by looking it up on `/v1/threads/post` in the same call. 1 credit per post filled from a fresh lookup (15 at most); posts served from that lookup's cache (10 minutes) and posts it could not fill are free. Adds 4 to 12 seconds, never more than 15. One token only: `engagement`. Beside a `limit` above 15 it costs nothing, because those rows already carry both fields. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. ``` curl -G "https://www.socialcrawl.dev/v1/threads/user/posts" \ --data-urlencode "handle=zuck" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /threads/post Get Threads post details Credit cost: 1 (standard) Parameters: - url (required): Full URL of the Threads post, for example https://www.threads.com/@zuck/post/DZpPDXbCeTt. Both threads.com and threads.net are accepted. - trim (optional, boolean): Ask the upstream for a stripped record. This is a real shape change, not just less whitespace: a trimmed row carries only the id, text, shortcode, like count, timestamp and author, so media, reply count, share count, view count, topic tag, pinned flag and quoted post all come back null. Same 1 credit either way, so send it only when the text and the like count are all you need. ``` curl -G "https://www.socialcrawl.dev/v1/threads/post" \ --data-urlencode "url=https://www.threads.com/@zuck/post/DZpPDXbCeTt" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /threads/search Search Threads posts Credit cost: 1-34 (metered): 1 credit per result window. A call with no `limit` is one window and costs exactly 1. With `limit` set the walker consumes `ceil(limit / 15)` windows - 4 for limit=50, 7 for the 100 maximum - holds that many up front and refunds every window it did not need. Query relaxation can add up to 4 more windows on a page-1 request, billed only for the relaxed searches that returned posts and refunded for the ones that did not; send `expand=false` to hold none of them. When your balance, or the API key's limit, covers the call without them but not with them, it runs as `expand=false` instead of answering 402, and `data._warnings` says so. A request carrying a cursor never relaxes, so a pagination loop's last hop costs nothing extra. With `include=engagement` the call also holds 20 credits and keeps 1 for each of the first 20 posts whose view count or display name it filled from a fresh lookup; posts served from the post lookup's cache (10 minutes) are free, a post Threads has published no view count for is free (it still gets its pinned flag), and the rest is refunded Relevance against your query (by default, or with relevance=) is free. Adding relevant_to= holds 4 extra credits and refunds down to 1 credit per started 25 rows that were newly judged: rows already judged for the same topic are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. The default labels (sponsored, intent, niche) are free. label=mention, quality, ai_style, ai_written, injection, or label=intent with offer=, holds 4 extra credits and refunds down to 1 credit per started 25 posts that were newly judged: posts already labelled are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. With `seen`, a page's price falls with its share of rows you already received under that id; credits for joined data (`include=`) are never discounted. Parameters: - query (required): Search keyword or phrase to find Threads posts - start_date (optional, string): Inclusive start of the search period (YYYY-MM-DD). Pagination never walks past it, and a relaxed query drops any post older than it. Threads treats the date pair as a ranking hint rather than a filter, so the window is enforced on our side: a post we cannot date is treated as outside it. - end_date (optional, string): Inclusive end of the search period (YYYY-MM-DD). Pagination starts here and walks back in time, and a relaxed query drops any post newer than it. - trim (optional, boolean): Ask the upstream for a stripped record. This is a real shape change, not just less whitespace: a trimmed row carries only the id, text, shortcode, like count, timestamp and author, so media, reply count, share count, view count, topic tag, pinned flag and quoted post all come back null. Same 1 credit either way, so send it only when the text and the like count are all you need. - cursor (optional, string): Opaque cursor from a prior response's pagination.next_cursor, passed back verbatim, to fetch the next (older) result window. It carries your date bounds, so paging cannot escape the period you asked for. A request that sends a cursor is never query-relaxed. - limit (optional, integer): Collect AT LEAST this many unique posts in one call (1-100). This is a collection target, not a page size: the API walks whole result windows server-side until it has collected this many (or the query runs dry), so the response usually carries somewhat more than you asked for and never fewer unless the query ran out. Nothing you paid for is trimmed away. Billed 1 credit per window consumed (about 15-20 posts each), with the unused window budget refunded. Omit for a single window. BUDGET FOR THE WALL CLOCK: the windows run one after another, at roughly 3.4 seconds each, so a high limit is a long request. Measured on production 07/09/2026, cache-cold: no limit 3.5s for 20 posts, limit=30 7.3s for 37, limit=50 10.2s for 55, limit=100 19.5s for 111. If your HTTP client defaults to a 10-second timeout, limit=50 and above will not fit inside it. Either raise the client timeout, or ask for a smaller limit and page with pagination.next_cursor, which costs the same credits for the same posts. - expand (optional, boolean): Set to false to search the exact phrase only. Defaults to true, which relaxes a query Threads does not recognise into its adjacent word pairs and merges the results, billing 1 credit per relaxed search that returned posts inside your date window. Ignored on a request that carries a cursor. When the credits you have cover the call with expand=false but not as sent, the call runs with expand=false instead of answering 402, and data._warnings says so. - include (optional, enum: engagement): Send `engagement` to fill `engagement.views` and `post.flags.pinned` on the first 20 posts by looking each one up on `/v1/threads/post` in the same call. 1 credit per post whose view count or display name was filled from a fresh lookup (20 at most); posts served from that lookup's cache (10 minutes) are free, and so is a post Threads has published no view count for yet, which still gets its pinned flag. Adds 3 to 12 seconds, never more than 12. One token only: `engagement`. Not accepted with a `limit` above 20; page with the cursor for more. - relevance (optional, enum: score | filter): Optional. Without this param every row already carries computed.relevance against your query, free ({ p, sense, depth, spam }: is this row about what your query means, or a different thing that shares its words?), and nothing is dropped or reordered. score asks for it explicitly and waits for every row; filter also drops the rows that are not about your query, and lists their ids in data.relevance.dropped_ids. Your query is the topic; nothing to configure. A row that could not be judged is never dropped and carries relevance: null. Pagination is unchanged, so a filtered page can hold fewer rows. Free with your query as the topic; with relevant_to it adds 1 credit per started 25 newly judged rows (rows already judged for the same topic are free, and so is a cached page). - relevance_threshold (optional, string): Optional, only with relevance. The probability (0 to 1) a row must reach to be kept by relevance=filter. Default 0.5. Lower keeps more rows, higher keeps fewer. - relevant_to (optional, string): Optional, only with relevance. Up to 200 characters describing what you mean, used as the topic instead of the query. Use it when the query is ambiguous, for example query=cleopatra with relevant_to=Cleopatra, the IGT slot game. - judgments (optional, enum: on | off): Optional, on (the default) or off. By default every row gains free SocialCrawl judgments (computed.labels, and computed.relevance on search endpoints), reported in data.labels (mode default) and data.relevance (origin default), each with a status (complete, partial or skipped) and pending: the rows still being judged when the page was sent, which carry null now and are filled on your next call or cached read. Default judgments never add credits, never change an existing field, and never drop or reorder a row. off returns the page exactly as before, with none of those keys. label=none does the same. - dry_run (optional, enum: 1): Optional. When 1, return a cost preview for this labelled or relevance-filtered request without fetching the page or judging any row. data.estimate reports rows_expected, rows_cached, label_credits_min, label_credits_max and base_credits. 0 credits charged. - fit (optional, enum: goal): Optional. When goal, keep the rows and fields needed for the goal you pass in goal= (plus any that are uncertain, and the first and last), and replace the rest with a stub. data.held_back lists the held ids and a recall id that re-reads the full page from cache at no extra charge. Without this param the page is unchanged. - goal (optional, string): Required by fit=goal. What you are trying to do, in your own words, up to 300 characters. - fit_tokens (optional, integer): Optional, only with fit=goal. Soft cap on how much of the page to keep, in tokens. Uncertain blocks and the first and last block are kept even if they exceed it. - label (optional, string): Optional CSV of SocialCrawl labels to add to every post. Without this param every page already carries sponsored, intent and niche, free; label= adds the labels you name to them (the defaults keep running). sponsored, intent and niche are free when asked for too; mention, quality, ai_style, intent with offer=, and injection add 1 credit per started 25 newly judged posts. judgments=off (or label=none) turns the default labels off. sponsored: is the post a paid or gifted promotion (p, 0 to 1), did it carry a disclosure marker such as #ad or 광고 (disclosed), is it likely paid with no marker (undisclosed), and which of the accounts it mentions does it promote (brand, or null). These are signals to review, never a finding. mention (needs brand=): is the post about that brand rather than something that shares its name (about_brand, 0 to 1), how it feels about the brand on five levels (sentiment_level 0 to 4 and sentiment_score 0 to 1, null when the post is not about the brand), is it sarcastic, which aspect it talks about (taste_or_quality, price_or_value, availability_or_delivery, health_or_safety, advertising_or_campaign, customer_service, none), and did the author buy or use it (first_hand). intent: what the author is mainly doing (label: asking_for_recommendation, comparing_options, switching_away, complaining, promoting, news_or_discussion, other, or null when unsure, with confidence), whether they read as a potential buyer rather than a seller (buyer, seller), how pressing the need is (urgency, 0 to 3), and, when you pass offer=, whether your offer would plausibly help them (fits_offer). niche: which of the 33 niches of the published taxonomy sc-niche-v1 the caption belongs to, or personal_no_niche, or other (label, confidence, taxonomy), with label null when the caption is too thin to tell or the pick is unsure. It reads the caption only, not the video. quality: how much checkable detail the caption carries (fact_density 0 to 3), whether it mainly asks for likes, replies, shares, follows or tags (engagement_bait, 0 to 1), whether it is written to provoke anger as a way to get engagement (rage_bait, 0 to 1; about the writing, never the side taken), whether it only repeats someone else's news or view (secondhand, 0 to 1), and what the post is mainly doing (post_aim: inform, opinion, sell, entertain, provoke, other). exclude=engagement_bait drops posts whose engagement_bait is 0.8 or higher. ai_style: writing patterns common in AI-generated text, judged on the text alone. It does not say who wrote the post, and a person who writes in this style is flagged too. Six patterns are judged, each a probability (0 to 1): binary_contrast ("This is not X. It's Y."), faux_insight ("Here's what nobody tells you" and throat-clearing openers), colon_reveal ("The best part: it learns."), profound_kicker (ends on an aphorism or mic-drop line), puffery ("marks a pivotal moment", "highlighting the importance of") and rhetorical_setup ("What if I told you", a question the post answers itself, "Not a X. Not a Y. A Z."). Four are counted in the text: slop_words (words and phrases such as delve, leverage, robust, game changer, at the end of the day), em_dashes, emoji_bullets (lines that start with an emoji or a symbol bullet) and one_line_paragraphs (paragraphs that are one short line). pattern_count is how many patterns are present (a judged pattern at 0.8 or higher, slop_words 1 or more, em_dashes 2 or more, emoji_bullets 2 or more, one_line_paragraphs 5 or more) and markers lists their names, comma-separated. A post with less than about eight words of text is only counted, with every judged pattern 0. The judged patterns read the first 400 and the last 150 characters of a long post, and the counts read the whole post. exclude=ai_style drops posts with 2 or more patterns. ai_written: how likely the post was written by an AI model, from SocialCrawl's own text classifier, reading the opening of the post (about 1,000 characters): p (0 to 1, calibrated), level (likely (p 0.95 or higher), possible (p 0.71 or higher) or unlikely) and model (the model name; a new name means a new scale). A post under 8 words is too short to read: p null, level too_short. It is a probability, not proof: a person can be flagged and an AI-written post can pass, and the shorter the post the less certain it is. exclude=ai_written drops likely posts. injection flags text that addresses an AI system and tries to direct it (flagged, p); it never drops or rewrites a row. A post that could not be judged carries labels: null. Posts already labelled for anyone are free, and so is a cached page. data.labels reports what was judged and billed. - exclude (optional, string): Optional CSV (engagement_bait, ai_style, ai_written). engagement_bait, only with label=quality, drops posts whose engagement_bait probability is 0.8 or higher. ai_style, only with label=ai_style, drops posts that show 2 or more of its writing patterns (pattern_count 2 or higher). ai_written, only with label=ai_written, drops posts whose level is likely (p 0.95 or higher), or whose p reaches your ai_written_threshold; a post too short to read is never dropped. Dropped posts are listed in data.labels.dropped_ids. A post that could not be judged is never dropped. - ai_written_threshold (optional, string): Optional, only with label=ai_written and exclude=ai_written. The p (0 to 1) at which exclude=ai_written drops a post. Default 0.95 (level likely). 0.71 also drops possible posts: more AI posts go, and more posts by people with them. - brand (optional, string): Required by label=mention, ignored otherwise. The brand or product the posts are judged against, up to 300 characters, for example brand=Buldak. Without it label=mention is skipped with the warning label_mention_needs_brand and is not billed. - brand_description (optional, string): Optional, with label=mention. One plain sentence saying what the brand is, for example brand_description=Samyang's spicy instant noodle brand. Use it when the name is also an ordinary word or another company's name. - offer (optional, string): Optional, with label=intent. One or two plain sentences saying what you sell, up to 300 characters, for example offer=A web design agency that builds websites for small businesses. Adds fits_offer to every post; without it fits_offer is null. - label_evidence (optional, enum: 1): Optional, only with label=. When 1, every labelled row also carries computed.labels_evidence. = { quote, sentence_index }: the sentence in the row that most clearly shows the label, copied verbatim. Absent or null when no single sentence shows it. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. - seen (optional, string): An id you choose (1 to 64 letters, digits, `.`, `_` or `-`). Rows your account already received under the same `seen` id are removed from the page, and the page price falls with the share of repeats: page credits x new rows / rows on the page, rounded up, so a page of nothing but repeats is free. Only the page's own price is discounted: credits for joined data (`include=`) are charged only for rows you have not received, and are never discounted. The memory lasts 24 hours from the last call that used the id, holds row ids only, and is private to your account. Use one id across a set of related searches; `data.walk.repeats` counts what was removed. ``` curl -G "https://www.socialcrawl.dev/v1/threads/search" \ --data-urlencode "query=artificial intelligence" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /threads/post/comments Get comments on a Threads post Credit cost: 1-10 (metered): 1 credit for the bundled window of about 20 replies, which is what a call with no `limit`, or `limit` at 25 or below, costs. Above 25 the deeper lane is metered at 1 credit per 5 replies RETURNED: the call holds `ceil(limit / 5)` up front (the 50 maximum holds 10) and refunds down to what came back, so a post with 12 replies settles at 3 credits however high `limit` was set Parameters: - url (required): Full URL of the Threads post whose replies you want, for example https://www.threads.com/@zuck/post/DZpPDXbCeTt. Both threads.com and threads.net are accepted. - trim (optional, boolean): Ask the upstream for a stripped record. This is a real shape change, not just less whitespace: a trimmed row carries only the id, text, shortcode, like count, timestamp and author, so media, reply count, share count, view count, topic tag, pinned flag and quoted post all come back null. Same 1 credit either way, so send it only when the text and the like count are all you need. Ignored when `limit` is above 25, because the deeper source has no trimmed mode. - limit (optional, integer): How many replies to collect, 1 to 50. Leave it off, or send 25 or less, and nothing changes: you get the bundled window for exactly 1 credit. Above 25 the replies come from a deeper source and the call is metered at 1 credit per 5 replies actually returned, so a post with only 12 replies costs 3 credits however high you set this. Threads publishes only part of a large conversation without a login, so a post declaring a thousand replies will still return about 50. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. ``` curl -G "https://www.socialcrawl.dev/v1/threads/post/comments" \ --data-urlencode "url=https://www.threads.com/@zuck/post/DZpPDXbCeTt" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /threads/search/users Search Threads users Credit cost: 1-13 (metered): 1 credit for the identity window, which is what a call with no `include` costs. With `include=profile` the call holds 1 credit per row of the page (12, or `limit`) and keeps 1 for each account it filled from a fresh lookup; accounts served from the profile lookup's cache (15 minutes) are free and the rest is refunded, so a call costs 13 at most Parameters: - query (required): Search keyword or phrase to find Threads users - include (optional, enum: profile): Send `profile` to fill `author.followers`, `author.bio`, `author.private` and `author.ext.bio_link` on each account by looking it up on `/v1/threads/profile` in the same call. 1 credit per account filled from a fresh lookup (12 at most, or `limit`); accounts served from that lookup's cache (15 minutes) and accounts it could not fill are free. Adds 3 to 6 seconds, never more than 12. One token only: `profile`. - limit (optional, integer): Take the top N accounts of the page, 1 to 12. With `include=profile` it also caps the extra credits: `limit=5&include=profile` holds 6 credits, not 13. A plain call with `limit` still costs 1. - include_details (optional, boolean): The older spelling of `include=profile`, kept working: `include_details=true` runs the same join at the same price (1 credit per account filled, 13 at most), and `false` is the plain call. Prefer `include=profile` in new code. ``` curl -G "https://www.socialcrawl.dev/v1/threads/search/users" \ --data-urlencode "query=tech" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## Tiktok ### GET /tiktok/profile Get TikTok user profile (oneOf: handle|user_id) Credit cost: 1 (standard) Parameters: - handle (optional, string): TikTok username without the @ symbol. - user_id (optional, string): TikTok numeric user ID. Use this for faster responses. - counts (optional, enum: app | web): `app` (default) or `web`. `web` returns the follower, following, likes and video counts TikTok's website shows (`statsV2` `followerCount`, `followingCount`, `heartCount`, `videoCount`) in place of the app's counters. Same price. `author.ext.counts_source` confirms which set was returned. - Constraint: one of handle, user_id (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/tiktok/profile" \ --data-urlencode "handle=charlidamelio" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /tiktok/profile/videos List TikTok user videos (oneOf: handle|user_id) Credit cost: 1-5 (metered): 1 credit per page. The default labels (sponsored, intent, niche) are free. label=mention, quality, ai_style, ai_written, injection, or label=intent with offer=, holds 4 extra credits and refunds down to 1 credit per started 25 posts that were newly judged: posts already labelled are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - since (optional, string): Only videos published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Older videos are left off the page, and the page that reaches one ends the walk: `next_cursor` is not returned and `pagination.stopped_at` is `since`. Pinned videos sit out of date order and never end the walk. The page still costs what a page costs, so a daily poll pays for the pages it walks and no more. - stop_at_id (optional, string): The id (`post.id`) or URL (`post.url`) of the newest video you already hold. The page stops just before it: that video and everything after it are left off, `next_cursor` is not returned, and `pagination.stopped_at` is `known_id`. A pinned video never counts as the stop point. If it is not on this page the page is returned in full with its cursor, so keep walking. `pagination.stopped_at` is `end` when the list ran out first and `null` while there is more to walk. - handle (optional, string): TikTok username without the @ symbol - user_id (optional, string): TikTok user id. Use this for faster responses. - sort_by (optional, enum: latest | popular): Accepted for compatibility and ignored on this endpoint: neither `latest` nor `popular` reorders the page. Videos come back in the source's own order, described above. To rank a creator's videos by views or likes, page through with `max_cursor` and sort the collected rows yourself. - max_cursor (optional, string): Cursor to get more videos. Get 'max_cursor' from previous response. It is a Unix timestamp in milliseconds, and the page returns the videos posted before it, so you can also start from a date of your choice: `max_cursor=1590969600000` returns the videos posted just before 01/06/2020, then keep paging with each response's `max_cursor` (measured 22/10/2026: 27-31/05/2020 on khaby.lame, 26-31/12/2018 on nba). `until` only filters the rows of a page and does not move where the walk starts. - region (optional, string): Region (Country) you want the proxy in. Defaults to US. It does not filter the videos. The country TikTok registers each video to is post.ext.region. - trim (optional, boolean): Accepted for compatibility; the response is already the canonical shape, so this flag has no effect. - format (optional, enum: stationery): Optional. stationery files each video into wedding, birthday, birth, sympathy, seasonal greeting, or leaves it ungrouped. data.format_buckets gives the counts, the ungrouped share, and a by-day count when the page spans more than one day. A null save count stays null. brief= skips the walk when the page is not about that subject. No extra credits. - brief (optional, string): Optional, with format=stationery. The subject these videos should be about. When the page is not about it, data.format_buckets.skip_creator is true and the videos are not filed. - label (optional, string): Optional CSV of SocialCrawl labels to add to every post. Without this param every page already carries sponsored, intent and niche, free; label= adds the labels you name to them (the defaults keep running). sponsored, intent and niche are free when asked for too; mention, quality, ai_style, intent with offer=, and injection add 1 credit per started 25 newly judged posts. judgments=off (or label=none) turns the default labels off. sponsored: is the post a paid or gifted promotion (p, 0 to 1), did it carry a disclosure marker such as #ad or 광고 (disclosed), is it likely paid with no marker (undisclosed), and which of the accounts it mentions does it promote (brand, or null). These are signals to review, never a finding. mention (needs brand=): is the post about that brand rather than something that shares its name (about_brand, 0 to 1), how it feels about the brand on five levels (sentiment_level 0 to 4 and sentiment_score 0 to 1, null when the post is not about the brand), is it sarcastic, which aspect it talks about (taste_or_quality, price_or_value, availability_or_delivery, health_or_safety, advertising_or_campaign, customer_service, none), and did the author buy or use it (first_hand). intent: what the author is mainly doing (label: asking_for_recommendation, comparing_options, switching_away, complaining, promoting, news_or_discussion, other, or null when unsure, with confidence), whether they read as a potential buyer rather than a seller (buyer, seller), how pressing the need is (urgency, 0 to 3), and, when you pass offer=, whether your offer would plausibly help them (fits_offer). niche: which of the 33 niches of the published taxonomy sc-niche-v1 the caption belongs to, or personal_no_niche, or other (label, confidence, taxonomy), with label null when the caption is too thin to tell or the pick is unsure. It reads the caption only, not the video. quality: how much checkable detail the caption carries (fact_density 0 to 3), whether it mainly asks for likes, replies, shares, follows or tags (engagement_bait, 0 to 1), whether it is written to provoke anger as a way to get engagement (rage_bait, 0 to 1; about the writing, never the side taken), whether it only repeats someone else's news or view (secondhand, 0 to 1), and what the post is mainly doing (post_aim: inform, opinion, sell, entertain, provoke, other). exclude=engagement_bait drops posts whose engagement_bait is 0.8 or higher. ai_style: writing patterns common in AI-generated text, judged on the text alone. It does not say who wrote the post, and a person who writes in this style is flagged too. Six patterns are judged, each a probability (0 to 1): binary_contrast ("This is not X. It's Y."), faux_insight ("Here's what nobody tells you" and throat-clearing openers), colon_reveal ("The best part: it learns."), profound_kicker (ends on an aphorism or mic-drop line), puffery ("marks a pivotal moment", "highlighting the importance of") and rhetorical_setup ("What if I told you", a question the post answers itself, "Not a X. Not a Y. A Z."). Four are counted in the text: slop_words (words and phrases such as delve, leverage, robust, game changer, at the end of the day), em_dashes, emoji_bullets (lines that start with an emoji or a symbol bullet) and one_line_paragraphs (paragraphs that are one short line). pattern_count is how many patterns are present (a judged pattern at 0.8 or higher, slop_words 1 or more, em_dashes 2 or more, emoji_bullets 2 or more, one_line_paragraphs 5 or more) and markers lists their names, comma-separated. A post with less than about eight words of text is only counted, with every judged pattern 0. The judged patterns read the first 400 and the last 150 characters of a long post, and the counts read the whole post. exclude=ai_style drops posts with 2 or more patterns. ai_written: how likely the post was written by an AI model, from SocialCrawl's own text classifier, reading the opening of the post (about 1,000 characters): p (0 to 1, calibrated), level (likely (p 0.95 or higher), possible (p 0.71 or higher) or unlikely) and model (the model name; a new name means a new scale). A post under 8 words is too short to read: p null, level too_short. It is a probability, not proof: a person can be flagged and an AI-written post can pass, and the shorter the post the less certain it is. exclude=ai_written drops likely posts. injection flags text that addresses an AI system and tries to direct it (flagged, p); it never drops or rewrites a row. A post that could not be judged carries labels: null. Posts already labelled for anyone are free, and so is a cached page. data.labels reports what was judged and billed. - exclude (optional, string): Optional CSV (engagement_bait, ai_style, ai_written). engagement_bait, only with label=quality, drops posts whose engagement_bait probability is 0.8 or higher. ai_style, only with label=ai_style, drops posts that show 2 or more of its writing patterns (pattern_count 2 or higher). ai_written, only with label=ai_written, drops posts whose level is likely (p 0.95 or higher), or whose p reaches your ai_written_threshold; a post too short to read is never dropped. Dropped posts are listed in data.labels.dropped_ids. A post that could not be judged is never dropped. - ai_written_threshold (optional, string): Optional, only with label=ai_written and exclude=ai_written. The p (0 to 1) at which exclude=ai_written drops a post. Default 0.95 (level likely). 0.71 also drops possible posts: more AI posts go, and more posts by people with them. - brand (optional, string): Required by label=mention, ignored otherwise. The brand or product the posts are judged against, up to 300 characters, for example brand=Buldak. Without it label=mention is skipped with the warning label_mention_needs_brand and is not billed. - brand_description (optional, string): Optional, with label=mention. One plain sentence saying what the brand is, for example brand_description=Samyang's spicy instant noodle brand. Use it when the name is also an ordinary word or another company's name. - offer (optional, string): Optional, with label=intent. One or two plain sentences saying what you sell, up to 300 characters, for example offer=A web design agency that builds websites for small businesses. Adds fits_offer to every post; without it fits_offer is null. - judgments (optional, enum: on | off): Optional, on (the default) or off. By default every row gains free SocialCrawl judgments (computed.labels, and computed.relevance on search endpoints), reported in data.labels (mode default) and data.relevance (origin default), each with a status (complete, partial or skipped) and pending: the rows still being judged when the page was sent, which carry null now and are filled on your next call or cached read. Default judgments never add credits, never change an existing field, and never drop or reorder a row. off returns the page exactly as before, with none of those keys. label=none does the same. - dry_run (optional, enum: 1): Optional. When 1, return a cost preview for this labelled or relevance-filtered request without fetching the page or judging any row. data.estimate reports rows_expected, rows_cached, label_credits_min, label_credits_max and base_credits. 0 credits charged. - label_evidence (optional, enum: 1): Optional, only with label=. When 1, every labelled row also carries computed.labels_evidence. = { quote, sentence_index }: the sentence in the row that most clearly shows the label, copied verbatim. Absent or null when no single sentence shows it. - fit (optional, enum: goal): Optional. When goal, keep the rows and fields needed for the goal you pass in goal= (plus any that are uncertain, and the first and last), and replace the rest with a stub. data.held_back lists the held ids and a recall id that re-reads the full page from cache at no extra charge. Without this param the page is unchanged. - goal (optional, string): Required by fit=goal. What you are trying to do, in your own words, up to 300 characters. - fit_tokens (optional, integer): Optional, only with fit=goal. Soft cap on how much of the page to keep, in tokens. Uncertain blocks and the first and last block are kept even if they exceed it. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. - Constraint: one of handle, user_id (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/tiktok/profile/videos" \ --data-urlencode "handle=charlidamelio" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /tiktok/post Get TikTok post details Credit cost: 1 (standard) Parameters: - url (required): Full URL of the TikTok video - region (optional, string): Two-letter country code for the proxy region (US, GB, FR, PH). The primary source does not take a region, so it is honoured only when a fallback source serves the request; a region-locked video (commonly from the Philippines) that fails on the primary is retried on the fallback with this value. - trim (optional, boolean): Accepted for compatibility; the response is already the canonical shape, so this flag has no effect. - download_media (optional, boolean): Set to true to also download the video/images and get back durable hosted media URLs under `data.post.ext.download_media_urls` (`[{ post_id, cdn_url, type, cached }]`). Use these for archiving; the raw `media_urls` are short-lived signed CDN links that expire. Adds a few seconds of latency while the media is fetched. Availability is source-dependent and most responses will not carry it: when the serving source cannot host durable copies the field is absent, the response carries a `data._warnings` note saying so, and `data.post.content.media_urls` still carries the media. ``` curl -G "https://www.socialcrawl.dev/v1/tiktok/post" \ --data-urlencode "url=https://www.tiktok.com/@stoolpresidente/video/7623818255903329566" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /tiktok/post/comments List TikTok post comments Credit cost: 1-7 (metered): 1 credit per page, with the default labels (sentiment, question, purchase_intent, complaint) included free. label=spam, toxic or low_quality holds 4 extra credits and refunds down to 1 credit per started 25 comments that were newly judged: comments already labelled are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - url (required): Full URL of the TikTok video to fetch comments for - cursor (optional, string): Cursor to get more comments. Get 'cursor' from previous response. - trim (optional, boolean): Accepted for compatibility; the response is already the canonical shape, so this flag has no effect. - sort (optional, enum: top | recent): Optional. Without it, comments come in TikTok's own order (a relevance ranking, not newest first). `recent` returns them newest first by published_at; `top` returns them by like count. TikTok has no newest-first order of its own, so `recent` sorts the comments that were read: set scan_pages to read more of the thread. data.scan.complete is true only when the whole thread was read. - scan_pages (optional, integer): Optional, 1 to 3, default 1. Reads that many pages, drops comments repeated across pages, and returns them all in the `sort` order (TikTok's own order when sort is absent). Costs 1 credit for each page that added comments; a page of repeats is not billed. data.scan reports the pages read and billed and whether the thread ended. - label (optional, string): Optional CSV of SocialCrawl labels to add to every comment: sentiment, spam, question, purchase_intent, complaint, toxic, low_quality, injection. Without this param every page already carries sentiment, question, purchase_intent and complaint, free; label= adds the labels you name to them (the defaults keep running). Each comment gains computed.labels with probabilities (0 to 1), so you choose the cut-off; a comment that could not be judged carries labels: null. sentiment, question, purchase_intent and complaint are free when asked for too; spam, toxic, low_quality and injection add 1 credit per started 25 newly judged comments (comments already labelled for anyone are free, and so is a cached page). injection flags text that addresses an AI system and tries to direct it (flagged, p); it never drops or rewrites a row. judgments=off (or label=none) turns the default labels off. data.labels reports what was judged and billed. - exclude (optional, string): Optional CSV (spam, low_quality), only with the same preset in label. Drops comments whose probability is 0.8 or higher and lists their ids in data.labels.dropped_ids. A comment that could not be judged is never dropped. - judgments (optional, enum: on | off): Optional, on (the default) or off. By default every row gains free SocialCrawl judgments (computed.labels, and computed.relevance on search endpoints), reported in data.labels (mode default) and data.relevance (origin default), each with a status (complete, partial or skipped) and pending: the rows still being judged when the page was sent, which carry null now and are filled on your next call or cached read. Default judgments never add credits, never change an existing field, and never drop or reorder a row. off returns the page exactly as before, with none of those keys. label=none does the same. - dry_run (optional, enum: 1): Optional. When 1, return a cost preview for this labelled or relevance-filtered request without fetching the page or judging any row. data.estimate reports rows_expected, rows_cached, label_credits_min, label_credits_max and base_credits. 0 credits charged. - label_evidence (optional, enum: 1): Optional, only with label=. When 1, every labelled row also carries computed.labels_evidence. = { quote, sentence_index }: the sentence in the row that most clearly shows the label, copied verbatim. Absent or null when no single sentence shows it. - fit (optional, enum: goal): Optional. When goal, keep the rows and fields needed for the goal you pass in goal= (plus any that are uncertain, and the first and last), and replace the rest with a stub. data.held_back lists the held ids and a recall id that re-reads the full page from cache at no extra charge. Without this param the page is unchanged. - goal (optional, string): Required by fit=goal. What you are trying to do, in your own words, up to 300 characters. - fit_tokens (optional, integer): Optional, only with fit=goal. Soft cap on how much of the page to keep, in tokens. Uncertain blocks and the first and last block are kept even if they exceed it. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off the page, and the page that reaches one ends the walk: `next_cursor` is not returned and `pagination.stopped_at` is `since`. Sending it sets the newest-first sort shown in this endpoint's sort parameter (any other sort value is a free 400). The page still costs what a page costs, so you pay for the pages you walk and no more. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/tiktok/post/comments" \ --data-urlencode "url=https://www.tiktok.com/@stoolpresidente/video/7623818255903329566" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /tiktok/video/comment/replies List TikTok comment replies Credit cost: 1-5 (metered): 1 credit per page, with the default labels (sentiment, question, purchase_intent, complaint) included free. label=spam, toxic or low_quality holds 4 extra credits and refunds down to 1 credit per started 25 comments that were newly judged: comments already labelled are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - comment_id (required): TikTok comment ID. This is the cid from the comments endpoint. - url (required): TikTok video URL. This is the url from the comments endpoint. - cursor (optional, integer): Cursor to get more replies. Get 'cursor' from previous response. - label (optional, string): Optional CSV of SocialCrawl labels to add to every comment: sentiment, spam, question, purchase_intent, complaint, toxic, low_quality, injection. Without this param every page already carries sentiment, question, purchase_intent and complaint, free; label= adds the labels you name to them (the defaults keep running). Each comment gains computed.labels with probabilities (0 to 1), so you choose the cut-off; a comment that could not be judged carries labels: null. sentiment, question, purchase_intent and complaint are free when asked for too; spam, toxic, low_quality and injection add 1 credit per started 25 newly judged comments (comments already labelled for anyone are free, and so is a cached page). injection flags text that addresses an AI system and tries to direct it (flagged, p); it never drops or rewrites a row. judgments=off (or label=none) turns the default labels off. data.labels reports what was judged and billed. - exclude (optional, string): Optional CSV (spam, low_quality), only with the same preset in label. Drops comments whose probability is 0.8 or higher and lists their ids in data.labels.dropped_ids. A comment that could not be judged is never dropped. - judgments (optional, enum: on | off): Optional, on (the default) or off. By default every row gains free SocialCrawl judgments (computed.labels, and computed.relevance on search endpoints), reported in data.labels (mode default) and data.relevance (origin default), each with a status (complete, partial or skipped) and pending: the rows still being judged when the page was sent, which carry null now and are filled on your next call or cached read. Default judgments never add credits, never change an existing field, and never drop or reorder a row. off returns the page exactly as before, with none of those keys. label=none does the same. - dry_run (optional, enum: 1): Optional. When 1, return a cost preview for this labelled or relevance-filtered request without fetching the page or judging any row. data.estimate reports rows_expected, rows_cached, label_credits_min, label_credits_max and base_credits. 0 credits charged. - label_evidence (optional, enum: 1): Optional, only with label=. When 1, every labelled row also carries computed.labels_evidence. = { quote, sentence_index }: the sentence in the row that most clearly shows the label, copied verbatim. Absent or null when no single sentence shows it. - fit (optional, enum: goal): Optional. When goal, keep the rows and fields needed for the goal you pass in goal= (plus any that are uncertain, and the first and last), and replace the rest with a stub. data.held_back lists the held ids and a recall id that re-reads the full page from cache at no extra charge. Without this param the page is unchanged. - goal (optional, string): Required by fit=goal. What you are trying to do, in your own words, up to 300 characters. - fit_tokens (optional, integer): Optional, only with fit=goal. Soft cap on how much of the page to keep, in tokens. Uncertain blocks and the first and last block are kept even if they exceed it. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/tiktok/video/comment/replies" \ --data-urlencode "comment_id=7623828115408274207" \ --data-urlencode "url=https://www.tiktok.com/@stoolpresidente/video/7623818255903329566" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /tiktok/comment Look up one TikTok comment by URL or id (oneOf: comment_url|post_url) Credit cost: 2-6 (metered): 2 credits for a standard lookup, 6 with `deep_scan=true`, which keeps paging the thread when the comment is not in the first pages. Reach for deep_scan only after a standard lookup comes back not-found Parameters: - comment_url (optional, string): A TikTok comment URL: `https://www.tiktok.com/@{handle}/video/{videoId}?comment_id={cid}`, the `m.tiktok.com/v/{id}.html?...&share_comment_id={cid}` share form, or a `vm.tiktok.com/{code}` / `tiktok.com/t/{code}` shortlink. The `cid`/`comment_id`/`share_comment_id` query value may be numeric or the base64 form TikTok's share sheet emits (`?cid=NzY1...`): both are accepted; base64 is decoded automatically. Mutually exclusive with `post_url`+`comment_id`. - post_url (optional, string): The post URL (`https://www.tiktok.com/@{handle}/video/{videoId}`). Combine with `comment_id`, or with `author_username`/`text_contains` for a search. - comment_id (optional, string): The target comment's numeric id (the `cid` from the comments endpoint). Requires `post_url`. Note that TikTok share links carry a base64-encoded `cid`: pass such a link as `comment_url` instead (decoded automatically), or base64-decode the value to its digits before passing it here. - parent_comment_id (optional, string): The parent comment's numeric id: supply this when the target is a reply so it can be resolved directly via the native replies endpoint. - author_username (optional, string): Return up to `max` comments authored by this username (no comment id needed). Mutually exclusive with `text_contains` and any comment id. - text_contains (optional, string): Return up to `max` comments whose text contains this snippet (case-insensitive). Mutually exclusive with `author_username` and any comment id. - deep_scan (optional, boolean): Widen the scan budget for deeply-buried comments (raises the page ceiling and deadline). Bills 6 credits instead of 2. - position_hint (optional, string): Opaque token from a prior lookup's `lookup.position_hint`. Passing it back probes the comment's last-known location first, making a re-check of an already-found comment cheap. - max (optional, integer): For `author_username`/`text_contains` search: max matches to return (1-20, default 5). - Constraint: one of comment_url, post_url (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/tiktok/comment" \ --data-urlencode "comment_url=https://www.tiktok.com/@mrbeast/video/7654638524729216287?comment_id=7654640784985211670" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /tiktok/search Search TikTok videos by keyword Credit cost: 1-42 (metered): 1 credit for the page, including any limit of 30 or less. A limit above 30 holds ceil(N/4)+2 credits for the N rows the source fills (60 to 120, rounded up to a multiple of 30), 17 at 60 and 32 at 120, and unfilled slots are refunded. Repeated videos are removed after billing. Relevance against your query (by default, or with relevance=) is free. Adding relevant_to= holds 5 extra credits and refunds down to 1 credit per started 25 rows that were newly judged: rows already judged for the same topic are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. The default labels (sponsored, intent, niche) are free. label=mention, quality, ai_style, ai_written, injection, or label=intent with offer=, holds 5 extra credits and refunds down to 1 credit per started 25 posts that were newly judged: posts already labelled are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. With `seen`, a page's price falls with its share of rows you already received under that id; credits for joined data (`include=`) are never discounted. Parameters: - query (required): Search keyword or phrase to find TikTok videos - date_posted (optional, enum: yesterday | this-week | this-month | last-3-months | last-6-months | all-time): Time Frame - sort_by (optional, enum: relevance | most-liked | date-posted): Sort by - region (optional, string): Sets the proxy country (ISO 3166-1 alpha-2). It does not filter the page to that country. Each row already carries post.ext.region, the country the video is registered to; filter on that leaf when you need only one country. - cursor (optional, integer): Cursor to get more videos. Get 'cursor' from previous response. Ignored when `limit` is above 30: that call is a single page. - trim (optional, boolean): Accepted for compatibility; the response is already the canonical shape, so this flag has no effect. - limit (optional, integer): A limit of 30 or less returns the first limit rows of the normal page for 1 credit; next_cursor still steps the whole page of about 30, so the rest of that page is skipped. A limit above 30 selects the bulk labeled page: up to 120 videos in one call, each with post.ext.region. The value rounds up to the next multiple of 30 (60, 90 or 120). 17 credits at 60, 32 at 120; slots the source does not fill are refunded. date_posted and sort_by apply on the bulk page too. The source can repeat a video; repeats are removed, so a 120 page is often a little under 120 unique videos. Does not filter by country; drop rows yourself on post.ext.region. - expand (optional, boolean): Send `true` to keep paging past TikTok's own limit for one query, which ends a walk at about 200 to 400 videos. When your ranking runs out, the walk continues with TikTok's related searches for your query that contain every word of it (for `coffee`: `iced coffee at home`, `coffee cold brew recipe`) and then with the other two rankings, each to its end, keeping your `date_posted` and `region`. A video can come up again on a later step, so deduplicate on `post.id`. Rows after your own ranking are not in your `sort_by` order. Every page is 1 credit, as usual; page with `cursor`, or collect many pages in one call with `max_items`. - relevance (optional, enum: score | filter): Optional. Without this param every row already carries computed.relevance against your query, free ({ p, sense, depth, spam }: is this row about what your query means, or a different thing that shares its words?), and nothing is dropped or reordered. score asks for it explicitly and waits for every row; filter also drops the rows that are not about your query, and lists their ids in data.relevance.dropped_ids. Your query is the topic; nothing to configure. A row that could not be judged is never dropped and carries relevance: null. Pagination is unchanged, so a filtered page can hold fewer rows. Free with your query as the topic; with relevant_to it adds 1 credit per started 25 newly judged rows (rows already judged for the same topic are free, and so is a cached page). - relevance_threshold (optional, string): Optional, only with relevance. The probability (0 to 1) a row must reach to be kept by relevance=filter. Default 0.5. Lower keeps more rows, higher keeps fewer. - relevant_to (optional, string): Optional, only with relevance. Up to 200 characters describing what you mean, used as the topic instead of the query. Use it when the query is ambiguous, for example query=cleopatra with relevant_to=Cleopatra, the IGT slot game. - judgments (optional, enum: on | off): Optional, on (the default) or off. By default every row gains free SocialCrawl judgments (computed.labels, and computed.relevance on search endpoints), reported in data.labels (mode default) and data.relevance (origin default), each with a status (complete, partial or skipped) and pending: the rows still being judged when the page was sent, which carry null now and are filled on your next call or cached read. Default judgments never add credits, never change an existing field, and never drop or reorder a row. off returns the page exactly as before, with none of those keys. label=none does the same. - dry_run (optional, enum: 1): Optional. When 1, return a cost preview for this labelled or relevance-filtered request without fetching the page or judging any row. data.estimate reports rows_expected, rows_cached, label_credits_min, label_credits_max and base_credits. 0 credits charged. - fit (optional, enum: goal): Optional. When goal, keep the rows and fields needed for the goal you pass in goal= (plus any that are uncertain, and the first and last), and replace the rest with a stub. data.held_back lists the held ids and a recall id that re-reads the full page from cache at no extra charge. Without this param the page is unchanged. - goal (optional, string): Required by fit=goal. What you are trying to do, in your own words, up to 300 characters. - fit_tokens (optional, integer): Optional, only with fit=goal. Soft cap on how much of the page to keep, in tokens. Uncertain blocks and the first and last block are kept even if they exceed it. - label (optional, string): Optional CSV of SocialCrawl labels to add to every post. Without this param every page already carries sponsored, intent and niche, free; label= adds the labels you name to them (the defaults keep running). sponsored, intent and niche are free when asked for too; mention, quality, ai_style, intent with offer=, and injection add 1 credit per started 25 newly judged posts. judgments=off (or label=none) turns the default labels off. sponsored: is the post a paid or gifted promotion (p, 0 to 1), did it carry a disclosure marker such as #ad or 광고 (disclosed), is it likely paid with no marker (undisclosed), and which of the accounts it mentions does it promote (brand, or null). These are signals to review, never a finding. mention (needs brand=): is the post about that brand rather than something that shares its name (about_brand, 0 to 1), how it feels about the brand on five levels (sentiment_level 0 to 4 and sentiment_score 0 to 1, null when the post is not about the brand), is it sarcastic, which aspect it talks about (taste_or_quality, price_or_value, availability_or_delivery, health_or_safety, advertising_or_campaign, customer_service, none), and did the author buy or use it (first_hand). intent: what the author is mainly doing (label: asking_for_recommendation, comparing_options, switching_away, complaining, promoting, news_or_discussion, other, or null when unsure, with confidence), whether they read as a potential buyer rather than a seller (buyer, seller), how pressing the need is (urgency, 0 to 3), and, when you pass offer=, whether your offer would plausibly help them (fits_offer). niche: which of the 33 niches of the published taxonomy sc-niche-v1 the caption belongs to, or personal_no_niche, or other (label, confidence, taxonomy), with label null when the caption is too thin to tell or the pick is unsure. It reads the caption only, not the video. quality: how much checkable detail the caption carries (fact_density 0 to 3), whether it mainly asks for likes, replies, shares, follows or tags (engagement_bait, 0 to 1), whether it is written to provoke anger as a way to get engagement (rage_bait, 0 to 1; about the writing, never the side taken), whether it only repeats someone else's news or view (secondhand, 0 to 1), and what the post is mainly doing (post_aim: inform, opinion, sell, entertain, provoke, other). exclude=engagement_bait drops posts whose engagement_bait is 0.8 or higher. ai_style: writing patterns common in AI-generated text, judged on the text alone. It does not say who wrote the post, and a person who writes in this style is flagged too. Six patterns are judged, each a probability (0 to 1): binary_contrast ("This is not X. It's Y."), faux_insight ("Here's what nobody tells you" and throat-clearing openers), colon_reveal ("The best part: it learns."), profound_kicker (ends on an aphorism or mic-drop line), puffery ("marks a pivotal moment", "highlighting the importance of") and rhetorical_setup ("What if I told you", a question the post answers itself, "Not a X. Not a Y. A Z."). Four are counted in the text: slop_words (words and phrases such as delve, leverage, robust, game changer, at the end of the day), em_dashes, emoji_bullets (lines that start with an emoji or a symbol bullet) and one_line_paragraphs (paragraphs that are one short line). pattern_count is how many patterns are present (a judged pattern at 0.8 or higher, slop_words 1 or more, em_dashes 2 or more, emoji_bullets 2 or more, one_line_paragraphs 5 or more) and markers lists their names, comma-separated. A post with less than about eight words of text is only counted, with every judged pattern 0. The judged patterns read the first 400 and the last 150 characters of a long post, and the counts read the whole post. exclude=ai_style drops posts with 2 or more patterns. ai_written: how likely the post was written by an AI model, from SocialCrawl's own text classifier, reading the opening of the post (about 1,000 characters): p (0 to 1, calibrated), level (likely (p 0.95 or higher), possible (p 0.71 or higher) or unlikely) and model (the model name; a new name means a new scale). A post under 8 words is too short to read: p null, level too_short. It is a probability, not proof: a person can be flagged and an AI-written post can pass, and the shorter the post the less certain it is. exclude=ai_written drops likely posts. injection flags text that addresses an AI system and tries to direct it (flagged, p); it never drops or rewrites a row. A post that could not be judged carries labels: null. Posts already labelled for anyone are free, and so is a cached page. data.labels reports what was judged and billed. - exclude (optional, string): Optional CSV (engagement_bait, ai_style, ai_written). engagement_bait, only with label=quality, drops posts whose engagement_bait probability is 0.8 or higher. ai_style, only with label=ai_style, drops posts that show 2 or more of its writing patterns (pattern_count 2 or higher). ai_written, only with label=ai_written, drops posts whose level is likely (p 0.95 or higher), or whose p reaches your ai_written_threshold; a post too short to read is never dropped. Dropped posts are listed in data.labels.dropped_ids. A post that could not be judged is never dropped. - ai_written_threshold (optional, string): Optional, only with label=ai_written and exclude=ai_written. The p (0 to 1) at which exclude=ai_written drops a post. Default 0.95 (level likely). 0.71 also drops possible posts: more AI posts go, and more posts by people with them. - brand (optional, string): Required by label=mention, ignored otherwise. The brand or product the posts are judged against, up to 300 characters, for example brand=Buldak. Without it label=mention is skipped with the warning label_mention_needs_brand and is not billed. - brand_description (optional, string): Optional, with label=mention. One plain sentence saying what the brand is, for example brand_description=Samyang's spicy instant noodle brand. Use it when the name is also an ordinary word or another company's name. - offer (optional, string): Optional, with label=intent. One or two plain sentences saying what you sell, up to 300 characters, for example offer=A web design agency that builds websites for small businesses. Adds fits_offer to every post; without it fits_offer is null. - label_evidence (optional, enum: 1): Optional, only with label=. When 1, every labelled row also carries computed.labels_evidence. = { quote, sentence_index }: the sentence in the row that most clearly shows the label, copied verbatim. Absent or null when no single sentence shows it. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - min_views (optional, integer): Keep only rows with at least this many views (`post.engagement.views`). A row whose view count is unknown is discarded, so every returned row meets the floor. Filtering runs on our side after each page is fetched, so every page walked is billed as usual; `data.walk.discarded` counts what was removed and why. Each page is its own entry in `GET /v1/credits/transactions`, under the page's request id listed in `data.walk.request_ids`. - max_age_days (optional, integer): Keep only rows published within this many days (`post.published_at`), 1 to 3650. A row with no date is discarded. Filtering runs on our side after each page is fetched, so every page walked is billed as usual; `data.walk.discarded` counts what was removed and why. Each page is its own entry in `GET /v1/credits/transactions`, under the page's request id listed in `data.walk.request_ids`. - country (optional, string): Comma-separated two-letter ISO 3166-1 codes, for example `US,GB`. Keep only videos TikTok registers to one of these countries (`post.ext.region`); a row with no country is discarded. This filters rows; it does not change where the search runs (that is `region`). Filtering runs on our side after each page is fetched, so every page walked is billed as usual; `data.walk.discarded` counts what was removed and why. Each page is its own entry in `GET /v1/credits/transactions`, under the page's request id listed in `data.walk.request_ids`. - exclude_country (optional, string): Comma-separated two-letter ISO 3166-1 codes, for example `IN`. Discard videos TikTok registers to any of these countries (`post.ext.region`); a row with no country is kept. Filtering runs on our side after each page is fetched, so every page walked is billed as usual; `data.walk.discarded` counts what was removed and why. Each page is its own entry in `GET /v1/credits/transactions`, under the page's request id listed in `data.walk.request_ids`. - sort_rows (optional, enum: views): `views`: return the kept rows ordered by view count, highest first, across every page walked. Rows without a view count go last. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them (after any filter). Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows (after any filter), then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. - seen (optional, string): An id you choose (1 to 64 letters, digits, `.`, `_` or `-`). Rows your account already received under the same `seen` id are removed from the page, and the page price falls with the share of repeats: page credits x new rows / rows on the page, rounded up, so a page of nothing but repeats is free. Only the page's own price is discounted: credits for joined data (`include=`) are charged only for rows you have not received, and are never discounted. The memory lasts 24 hours from the last call that used the id, holds row ids only, and is private to your account. Use one id across a set of related searches; `data.walk.repeats` counts what was removed. ``` curl -G "https://www.socialcrawl.dev/v1/tiktok/search" \ --data-urlencode "query=cooking recipes" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /tiktok/trending Get TikTok trending feed Credit cost: 5 (advanced) Parameters: - region (required): ISO 3166-1 alpha-2 country code (e.g., US, GB, DE). On the default feed it sets the country the request is routed through and does not filter videos; with `feed=local` it sets the country the For You feed is built for. Either way, check `ext.region` on each row for the country a video is actually from. - trim (optional, boolean): Accepted for compatibility; it has no effect with `feed=local`. - feed (optional, enum: global | local): `global` (default): the web trending feed routed through `region`, mostly worldwide content, about 13 videos in 3 to 12 seconds. `local`: the For You feed for a phone in `region`, most videos from that country, a median of 24 videos with 9 calls in 10 answering within 22 seconds, and a refunded 503 when none come back. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. ``` curl -G "https://www.socialcrawl.dev/v1/tiktok/trending" \ --data-urlencode "region=US" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /tiktok/search/hashtag Search TikTok by hashtag Credit cost: 1-5 (metered): 1 credit per page. The default labels (sponsored, intent, niche) are free. label=mention, quality, ai_style, ai_written, injection, or label=intent with offer=, holds 4 extra credits and refunds down to 1 credit per started 25 posts that were newly judged: posts already labelled are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. With `seen`, a page's price falls with its share of rows you already received under that id; credits for joined data (`include=`) are never discounted. Parameters: - hashtag (required): Hashtag to search for without the # symbol - region (optional, string): Region the proxy will be set to. Note: this isn't going to grab you all tiktoks from this region, you're just setting the proxy there. - cursor (optional, integer): Cursor to get more videos. Get 'cursor' from previous response. - trim (optional, boolean): Accepted for compatibility; the response is already the canonical shape, so this flag has no effect. - label (optional, string): Optional CSV of SocialCrawl labels to add to every post. Without this param every page already carries sponsored, intent and niche, free; label= adds the labels you name to them (the defaults keep running). sponsored, intent and niche are free when asked for too; mention, quality, ai_style, intent with offer=, and injection add 1 credit per started 25 newly judged posts. judgments=off (or label=none) turns the default labels off. sponsored: is the post a paid or gifted promotion (p, 0 to 1), did it carry a disclosure marker such as #ad or 광고 (disclosed), is it likely paid with no marker (undisclosed), and which of the accounts it mentions does it promote (brand, or null). These are signals to review, never a finding. mention (needs brand=): is the post about that brand rather than something that shares its name (about_brand, 0 to 1), how it feels about the brand on five levels (sentiment_level 0 to 4 and sentiment_score 0 to 1, null when the post is not about the brand), is it sarcastic, which aspect it talks about (taste_or_quality, price_or_value, availability_or_delivery, health_or_safety, advertising_or_campaign, customer_service, none), and did the author buy or use it (first_hand). intent: what the author is mainly doing (label: asking_for_recommendation, comparing_options, switching_away, complaining, promoting, news_or_discussion, other, or null when unsure, with confidence), whether they read as a potential buyer rather than a seller (buyer, seller), how pressing the need is (urgency, 0 to 3), and, when you pass offer=, whether your offer would plausibly help them (fits_offer). niche: which of the 33 niches of the published taxonomy sc-niche-v1 the caption belongs to, or personal_no_niche, or other (label, confidence, taxonomy), with label null when the caption is too thin to tell or the pick is unsure. It reads the caption only, not the video. quality: how much checkable detail the caption carries (fact_density 0 to 3), whether it mainly asks for likes, replies, shares, follows or tags (engagement_bait, 0 to 1), whether it is written to provoke anger as a way to get engagement (rage_bait, 0 to 1; about the writing, never the side taken), whether it only repeats someone else's news or view (secondhand, 0 to 1), and what the post is mainly doing (post_aim: inform, opinion, sell, entertain, provoke, other). exclude=engagement_bait drops posts whose engagement_bait is 0.8 or higher. ai_style: writing patterns common in AI-generated text, judged on the text alone. It does not say who wrote the post, and a person who writes in this style is flagged too. Six patterns are judged, each a probability (0 to 1): binary_contrast ("This is not X. It's Y."), faux_insight ("Here's what nobody tells you" and throat-clearing openers), colon_reveal ("The best part: it learns."), profound_kicker (ends on an aphorism or mic-drop line), puffery ("marks a pivotal moment", "highlighting the importance of") and rhetorical_setup ("What if I told you", a question the post answers itself, "Not a X. Not a Y. A Z."). Four are counted in the text: slop_words (words and phrases such as delve, leverage, robust, game changer, at the end of the day), em_dashes, emoji_bullets (lines that start with an emoji or a symbol bullet) and one_line_paragraphs (paragraphs that are one short line). pattern_count is how many patterns are present (a judged pattern at 0.8 or higher, slop_words 1 or more, em_dashes 2 or more, emoji_bullets 2 or more, one_line_paragraphs 5 or more) and markers lists their names, comma-separated. A post with less than about eight words of text is only counted, with every judged pattern 0. The judged patterns read the first 400 and the last 150 characters of a long post, and the counts read the whole post. exclude=ai_style drops posts with 2 or more patterns. ai_written: how likely the post was written by an AI model, from SocialCrawl's own text classifier, reading the opening of the post (about 1,000 characters): p (0 to 1, calibrated), level (likely (p 0.95 or higher), possible (p 0.71 or higher) or unlikely) and model (the model name; a new name means a new scale). A post under 8 words is too short to read: p null, level too_short. It is a probability, not proof: a person can be flagged and an AI-written post can pass, and the shorter the post the less certain it is. exclude=ai_written drops likely posts. injection flags text that addresses an AI system and tries to direct it (flagged, p); it never drops or rewrites a row. A post that could not be judged carries labels: null. Posts already labelled for anyone are free, and so is a cached page. data.labels reports what was judged and billed. - exclude (optional, string): Optional CSV (engagement_bait, ai_style, ai_written). engagement_bait, only with label=quality, drops posts whose engagement_bait probability is 0.8 or higher. ai_style, only with label=ai_style, drops posts that show 2 or more of its writing patterns (pattern_count 2 or higher). ai_written, only with label=ai_written, drops posts whose level is likely (p 0.95 or higher), or whose p reaches your ai_written_threshold; a post too short to read is never dropped. Dropped posts are listed in data.labels.dropped_ids. A post that could not be judged is never dropped. - ai_written_threshold (optional, string): Optional, only with label=ai_written and exclude=ai_written. The p (0 to 1) at which exclude=ai_written drops a post. Default 0.95 (level likely). 0.71 also drops possible posts: more AI posts go, and more posts by people with them. - brand (optional, string): Required by label=mention, ignored otherwise. The brand or product the posts are judged against, up to 300 characters, for example brand=Buldak. Without it label=mention is skipped with the warning label_mention_needs_brand and is not billed. - brand_description (optional, string): Optional, with label=mention. One plain sentence saying what the brand is, for example brand_description=Samyang's spicy instant noodle brand. Use it when the name is also an ordinary word or another company's name. - offer (optional, string): Optional, with label=intent. One or two plain sentences saying what you sell, up to 300 characters, for example offer=A web design agency that builds websites for small businesses. Adds fits_offer to every post; without it fits_offer is null. - judgments (optional, enum: on | off): Optional, on (the default) or off. By default every row gains free SocialCrawl judgments (computed.labels, and computed.relevance on search endpoints), reported in data.labels (mode default) and data.relevance (origin default), each with a status (complete, partial or skipped) and pending: the rows still being judged when the page was sent, which carry null now and are filled on your next call or cached read. Default judgments never add credits, never change an existing field, and never drop or reorder a row. off returns the page exactly as before, with none of those keys. label=none does the same. - dry_run (optional, enum: 1): Optional. When 1, return a cost preview for this labelled or relevance-filtered request without fetching the page or judging any row. data.estimate reports rows_expected, rows_cached, label_credits_min, label_credits_max and base_credits. 0 credits charged. - label_evidence (optional, enum: 1): Optional, only with label=. When 1, every labelled row also carries computed.labels_evidence. = { quote, sentence_index }: the sentence in the row that most clearly shows the label, copied verbatim. Absent or null when no single sentence shows it. - fit (optional, enum: goal): Optional. When goal, keep the rows and fields needed for the goal you pass in goal= (plus any that are uncertain, and the first and last), and replace the rest with a stub. data.held_back lists the held ids and a recall id that re-reads the full page from cache at no extra charge. Without this param the page is unchanged. - goal (optional, string): Required by fit=goal. What you are trying to do, in your own words, up to 300 characters. - fit_tokens (optional, integer): Optional, only with fit=goal. Soft cap on how much of the page to keep, in tokens. Uncertain blocks and the first and last block are kept even if they exceed it. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - min_views (optional, integer): Keep only rows with at least this many views (`post.engagement.views`). A row whose view count is unknown is discarded, so every returned row meets the floor. Filtering runs on our side after each page is fetched, so every page walked is billed as usual; `data.walk.discarded` counts what was removed and why. Each page is its own entry in `GET /v1/credits/transactions`, under the page's request id listed in `data.walk.request_ids`. - max_age_days (optional, integer): Keep only rows published within this many days (`post.published_at`), 1 to 3650. A row with no date is discarded. Filtering runs on our side after each page is fetched, so every page walked is billed as usual; `data.walk.discarded` counts what was removed and why. Each page is its own entry in `GET /v1/credits/transactions`, under the page's request id listed in `data.walk.request_ids`. - sort_rows (optional, enum: views): `views`: return the kept rows ordered by view count, highest first, across every page walked. Rows without a view count go last. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them (after any filter). Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows (after any filter), then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. - seen (optional, string): An id you choose (1 to 64 letters, digits, `.`, `_` or `-`). Rows your account already received under the same `seen` id are removed from the page, and the page price falls with the share of repeats: page credits x new rows / rows on the page, rounded up, so a page of nothing but repeats is free. Only the page's own price is discounted: credits for joined data (`include=`) are charged only for rows you have not received, and are never discounted. The memory lasts 24 hours from the last call that used the id, holds row ids only, and is private to your account. Use one id across a set of related searches; `data.walk.repeats` counts what was removed. ``` curl -G "https://www.socialcrawl.dev/v1/tiktok/search/hashtag" \ --data-urlencode "hashtag=fyp" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /tiktok/search/top TikTok top search results Credit cost: 1 (standard) Parameters: - query (required): Search keyword or phrase - publish_time (optional, enum: yesterday | this-week | this-month | last-3-months | last-6-months | all-time): Time Frame TikTok was posted - sort_by (optional, enum: relevance | most-liked | date-posted): Sort by - region (optional, string): Sets the proxy country (ISO 3166-1 alpha-2). It does not filter the page to that country. Each row already carries post.ext.region, the country the video is registered to; filter on that leaf when you need only one country. - cursor (optional, integer): Cursor to get more videos. Get 'cursor' from previous response. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - min_views (optional, integer): Keep only rows with at least this many views (`post.engagement.views`). A row whose view count is unknown is discarded, so every returned row meets the floor. Filtering runs on our side after each page is fetched, so every page walked is billed as usual; `data.walk.discarded` counts what was removed and why. Each page is its own entry in `GET /v1/credits/transactions`, under the page's request id listed in `data.walk.request_ids`. - max_age_days (optional, integer): Keep only rows published within this many days (`post.published_at`), 1 to 3650. A row with no date is discarded. Filtering runs on our side after each page is fetched, so every page walked is billed as usual; `data.walk.discarded` counts what was removed and why. Each page is its own entry in `GET /v1/credits/transactions`, under the page's request id listed in `data.walk.request_ids`. - country (optional, string): Comma-separated two-letter ISO 3166-1 codes, for example `US,GB`. Keep only videos TikTok registers to one of these countries (`post.ext.region`); a row with no country is discarded. This filters rows; it does not change where the search runs (that is `region`). Filtering runs on our side after each page is fetched, so every page walked is billed as usual; `data.walk.discarded` counts what was removed and why. Each page is its own entry in `GET /v1/credits/transactions`, under the page's request id listed in `data.walk.request_ids`. - exclude_country (optional, string): Comma-separated two-letter ISO 3166-1 codes, for example `IN`. Discard videos TikTok registers to any of these countries (`post.ext.region`); a row with no country is kept. Filtering runs on our side after each page is fetched, so every page walked is billed as usual; `data.walk.discarded` counts what was removed and why. Each page is its own entry in `GET /v1/credits/transactions`, under the page's request id listed in `data.walk.request_ids`. - sort_rows (optional, enum: views): `views`: return the kept rows ordered by view count, highest first, across every page walked. Rows without a view count go last. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them (after any filter). Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows (after any filter), then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. - seen (optional, string): An id you choose (1 to 64 letters, digits, `.`, `_` or `-`). Rows your account already received under the same `seen` id are removed from the page, and the page price falls with the share of repeats: page credits x new rows / rows on the page, rounded up, so a page of nothing but repeats is free. Only the page's own price is discounted: credits for joined data (`include=`) are charged only for rows you have not received, and are never discounted. The memory lasts 24 hours from the last call that used the id, holds row ids only, and is private to your account. Use one id across a set of related searches; `data.walk.repeats` counts what was removed. ``` curl -G "https://www.socialcrawl.dev/v1/tiktok/search/top" \ --data-urlencode "query=dance challenge" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /tiktok/search/users Search TikTok users Credit cost: 1-100 (metered): 1 credit for the page. include=profile holds 1 credit per row, at most 30 (or `limit`), and keeps only the rows filled from a fresh lookup, so a page is at most 31 credits; rows served from cache are free, unfilled rows are refunded, and a repeat within the cache window costs 0. country= holds 5 credits per requested in-country row (8 by default, at most 20) and keeps only the rows returned, so a miss is 0. The page is cached for 2 minutes and the per-account lookups for 15 minutes. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - query (required): Search keyword or phrase to find TikTok users - cursor (optional, integer): Cursor to get more users. Get 'cursor' from previous response. - trim (optional, boolean): Accepted for compatibility; the response is already the canonical shape, so this flag has no effect. - include (optional, enum: profile): Set to `profile` (one token only) to fill `author.bio`, `author.location` (the ISO region), `author.ext.bio_link` and `author.ext.business_category` on every row in this one call. Holds 1 credit per row (at most 30, or `limit`) and keeps only the rows filled from a fresh lookup; rows already in cache and rows that could not be filled are refunded, so the most a call can cost is 1 + rows. Adds 4 to 9 seconds on a fresh page (never more than 12) and nothing when the rows are already cached; read `data.hydration` for the rows, the credits held and kept, and the time. - limit (optional, integer): Take the top N rows of the page (1 to 30) after the search has run. With `include=profile` it also caps the extra credits at N: `limit=5&include=profile` costs at most 6. With `country=` it is the in-country page size (1 to 20, default 8) and the credit cap at 5 per row. On the global page it is not a page size: `next_cursor` still advances past the full page, so rows beyond N on this page are not returned by the next page. - country (optional, enum: US | GB | CA | AU | NZ | IE | DE | FR | ES | IT | PT | NL | BE | CH | AT | SE | NO | DK | FI | IS | PL | CZ | SK | HU | RO | BG | GR | HR | RS | SI | UA | RU | TR | IL | AE | SA | QA | KW | EG | MA | ZA | NG | KE | GH | TZ | ET | BR | MX | AR | CO | CL | PE | VE | EC | UY | PY | BO | CR | PA | DO | JM | JP | KR | CN | TW | HK | MO | SG | MY | ID | TH | VN | PH | IN | PK | BD | LK | NP | MM | KH): ISO 3166-1 alpha-2 account region to filter by (KR, US, DE, …). Returns only creators whose public region is that country, 5 credits per row returned (8 by default, at most 20). A row is also dropped when neither its matched video, bio nor name is about your query, so a query that names only a market or a kind of account ("influencers") returns an empty page at 0 credits; put the topic in `query`. Omit it for the global 1-credit page. A code this endpoint does not accept is a free 400. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/tiktok/search/users" \ --data-urlencode "query=cooking" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /tiktok/similar List similar TikTok accounts Credit cost: 5 credits a call. An empty list is refunded, an unknown handle is a free 404, and the list is cached for 15 minutes, so a repeat within that window costs 0. Parameters: - handle (required): TikTok username of the seed creator, without the @ symbol. ``` curl -G "https://www.socialcrawl.dev/v1/tiktok/similar" \ --data-urlencode "handle=gordonramsayofficial" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /tiktok/user/audience Get TikTok user audience demographics Credit cost: 5 (advanced) Parameters: - handle (required): TikTok username without the @ symbol ``` curl -G "https://www.socialcrawl.dev/v1/tiktok/user/audience" \ --data-urlencode "handle=charlidamelio" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /tiktok/user/followers List TikTok user followers (oneOf: handle|user_id) Credit cost: 1 (standard) Parameters: - handle (optional, string): TikTok username without the @ symbol - user_id (optional, string): User id. Use this for faster response times. - min_time (optional, integer): Used to paginate. Get 'min_time' from previous response. - trim (optional, boolean): Accepted for compatibility; the response is already the canonical shape, so this flag has no effect. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. - Constraint: one of handle, user_id (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/tiktok/user/followers" \ --data-urlencode "handle=stoolpresidente" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /tiktok/user/following List TikTok user following (oneOf: handle|user_id) Credit cost: 1 (standard) Parameters: - handle (optional, string): TikTok username without the @ symbol - user_id (optional, string): TikTok user id. Use this for faster responses. - min_time (optional, integer): Used to paginate. Get 'min_time' from previous response. - trim (optional, boolean): Accepted for compatibility; the response is already the canonical shape, so this flag has no effect. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. - Constraint: one of handle, user_id (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/tiktok/user/following" \ --data-urlencode "handle=stoolpresidente" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /tiktok/user/live Get TikTok user live stream Credit cost: 1 (standard) Parameters: - handle (required): TikTok username without the @ symbol ``` curl -G "https://www.socialcrawl.dev/v1/tiktok/user/live" \ --data-urlencode "handle=charlidamelio" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /tiktok/post/transcript Get TikTok video transcript Credit cost: 10 (premium) Parameters: - url (required): Full URL of the TikTok video - language (optional, string): Language of the transcript. 2 letter language code, ie 'en', 'es', 'fr', 'de', 'it', 'ja', 'ko', 'zh'. Selects the caption track, and is passed to AI transcription of a longer video as the spoken language. Without it, AI transcription detects the language itself and can mistake a less common one for English, so set it when you know the language. If AI transcription of a longer video cannot transcribe the language you set, the request returns 404 RESOURCE_NOT_FOUND with `error.details.reason: language_unsupported` at 0 credits rather than a transcript in another language; retrying returns the same result. - use_ai_as_fallback (optional, string): Set to 'true' to fall back to AI transcription when the video has no captions. Included in the endpoint's 10 credits (no surcharge). AI transcription covers videos up to 10 minutes (600 seconds): a longer video with no captions returns 400 INVALID_REQUEST with `error.details.reason: video_too_long`, `duration_seconds` and `max_seconds`, free and deterministic, so retrying returns the same answer. - moments (optional, string): Optional CSV of moment kinds to find: brand, sponsor_read, product_demo, call_to_action. Returns data.moments = [{ kind, start_ms, end_ms, quote }] when the transcript has timestamps. Price of the extra step is an owner decision; today it does not add credits. - brand (optional, string): Optional, with moments=. The company or product to look for, so a namesake is not counted. - q (optional, string): Optional question about the video, up to 300 characters, for example q=what discount code do they give. Returns data.moments = [{ kind: "answers_query", start_ms, end_ms, quote }] for each stretch of about six transcript lines that states the answer: a stretch that only shares the topic is left out, and a question the video never answers returns an empty list. The quote is one line copied from the transcript and the times come from the segments. Send moments= or q=, not both. If the step cannot run, the transcript is returned without data.moments and data._warnings carries moments_unavailable. Today it does not add credits. ``` curl -G "https://www.socialcrawl.dev/v1/tiktok/post/transcript" \ --data-urlencode "url=https://www.tiktok.com/@stoolpresidente/video/7499229683859426602" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /tiktok/video/screen-text Get TikTok video on-screen text Credit cost: 5-10 (metered): 5 credits a call, the same for a video's cover or a photo post page of 10 slides. 10 credits with `frames=all` on a video; when the post is a photo post, or the video cannot be read and the cover is read instead, the call costs 5 and the response says so in `warnings`. Refunded when the post cannot be found, when nothing on it could be read, or when a parameter does not fit the post (a `slide_offset` past the last slide or on a video, a video over 180 seconds with `frames=all`). Parameters: - url (required): Full URL of the TikTok video or photo post (a `/video/` or `/photo/` link) - frames (optional, enum: cover | all): What to read on a video. `cover` (the default) reads the cover frame for 5 credits. `all` reads the whole video, up to 180 seconds, for 10 credits, and adds `subtitles`, `timeline` and `duration_seconds`. Has no effect on photo posts, which are read as usual for 5 credits. - slide_offset (optional, integer): Photo posts only: the 0-based index of the first slide to read, 10 slides a call. Default 0. `slide_offset=10` reads slides 11 to 20. Each slide in `images` keeps its index in the whole post, and `next_slide_offset` gives the value for the next call. ``` curl -G "https://www.socialcrawl.dev/v1/tiktok/video/screen-text" \ --data-urlencode "url=https://www.tiktok.com/@stoolpresidente/video/7499229683859426602" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /tiktok/song Get TikTok song details Credit cost: 1 (standard) Parameters: - clipId (required): TikTok sound/song clip ID ``` curl -G "https://www.socialcrawl.dev/v1/tiktok/song" \ --data-urlencode "clipId=7439295283975702544" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /tiktok/song/videos List TikTok videos using a song (oneOf: clipId) Credit cost: 1 (standard) Parameters: - clipId (optional, string): TikTok sound/song clip ID - cursor (optional, integer): The cursor to get the next page of results. - use (optional, boolean): Optional. 1 adds data.adoption.uses, a count of what the captions are doing with the sound. Omit it and the date histogram is still returned. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. - Constraint: one of clipId (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/tiktok/song/videos" \ --data-urlencode "clipId=7439295283975702544" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /tiktok/hashtags/popular Read TikTok's own trending-hashtag board for a market and time window: the overall board plus 15 industry boards. Metered: 2 credits per hashtag returned, minimum 6 Credit cost: 6-96 (metered): 2 credits per hashtag returned, minimum 6. One board is 6 credits. industry=all holds 96 credits (sixteen boards of three) and settles down to the hashtags actually returned, typically 88 to 92. An empty board costs nothing. Parameters: - countryCode (optional, enum: US | FR | DE | IT | ES | GB | AR | AU | BR | CA | CO | EG | ID | IL | JP | KR | MY | MX | PH | SA | SG | ZA | TW | TH | TR | AE | VN): Market to read the board for, as an ISO country code. One of the 27 markets TikTok publishes the board for. Defaults to US. - period (optional, enum: 7 | 30 | 90): Window in days: 7, 30 or 90. Defaults to 7. Each window is a different board, not a longer list. - industry (optional, enum: all | education | vehicle-and-transportation | baby-kids-and-maternity | beauty-and-personal-care | tech-and-electronics | travel | household-products | pets | home-improvement | apparel-and-accessories | news-and-entertainment | games | food-and-beverage | sports-and-outdoor | health): Which board to read. Omit for the overall board (3 hashtags). Pass an industry slug for that industry's board (3 hashtags), or `all` for the overall board plus all 15 industry boards in one call. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. ``` curl "https://www.socialcrawl.dev/v1/tiktok/hashtags/popular" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /tiktok/videos/popular Read TikTok's own Top Videos board for the US, Japan, Vietnam, Thailand or Indonesia. Metered: 25 credits per board plus 1 per video returned Credit cost: 26-45 (metered): 25 credits per board plus 1 credit per video returned. The default limit of 20 holds 45 credits and settles to the videos actually returned; an empty board costs nothing. Parameters: - countryCode (optional, enum: US | JP | VN | TH | ID): Market: US, JP, VN, TH or ID, the only markets TikTok publishes this board for. Defaults to US. - period (optional, enum: 7 | 30): Window in days: 7 or 30. Defaults to 7. - orderBy (optional, enum: views | engagement | six_second_views): How TikTok ranks the board: views (the default), engagement, or six_second_views. - limit (optional, integer): How many videos to return, 1-20, default 20. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. ``` curl "https://www.socialcrawl.dev/v1/tiktok/videos/popular" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /tiktok/profile/region Get TikTok profile region Credit cost: 1 (standard) Parameters: - handle (required): TikTok username without the @ symbol ``` curl -G "https://www.socialcrawl.dev/v1/tiktok/profile/region" \ --data-urlencode "handle=stoolpresidente" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /tiktok/adlibrary/search Search the TikTok Ad Library (oneOf: query|advertiser_name) Credit cost: 5-17 (metered): 5 credits for the page. include=ad holds 1 credit per ad, at most 12 (or `limit`), and keeps only the ads filled from a fresh lookup, so a page is at most 17 credits; ads served from cache are free, unfilled ads are refunded, and a repeat within the cache window costs 0. The page is cached for 2 minutes and the per-ad lookups for 30 minutes. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - query (optional, string): Keyword or phrase to search the TikTok Ad Library. - advertiser_name (optional, string): Advertiser name as it appears in the TikTok Ad Library. - cursor (optional, string): Cursor from the previous response to fetch the next page. - include (optional, enum: ad): Set to `ad` (one token only) to fill the registered brand name, the landing page, the advertiser's TikTok profile link and avatar (`post.ext.ad.brand_name`, `post.ext.ad.landing_page`, `post.ext.ad.profile_web_link`, `post.author.avatar_url`), plus the objectives, countries and source where the library has them, on every ad in this one call. Holds 1 credit per ad (at most 12, or `limit`) and keeps only the ads filled from a fresh lookup; ads already in cache and ads that could not be filled are refunded, so the most a call can cost is 5 + ads. Adds 5 to 20 seconds on a fresh page (never more than 20) and nothing when the ads are already cached; read `data.hydration` for the rows, the credits held and kept, and the time. - limit (optional, integer): Take the top N ads of the page (1 to 12) after the search has run. With `include=ad` it also caps the extra credits at N: `limit=3&include=ad` costs at most 8. It is not a page size: `next_cursor` still advances past the full page. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. - Constraint: one of query, advertiser_name (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/tiktok/adlibrary/search" \ --data-urlencode "advertiser_name=Gymshark" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /tiktok/adlibrary/ad Get a TikTok Ad Library ad (oneOf: ad_id|url) Credit cost: 5 (advanced) Parameters: - ad_id (optional, string): TikTok Ad Library ad id, or a library.tiktok.com ads/detail URL. - url (optional, string): TikTok Ad Library ads/detail URL. - Constraint: one of ad_id, url (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/tiktok/adlibrary/ad" \ --data-urlencode "ad_id=1869335220395266" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /tiktok/search/suggestions Get TikTok search suggestions Credit cost: 1 (standard) Parameters: - query (required): Partial search query to autocomplete. - region (optional, string): ISO 3166-1 alpha-2 country code to localize suggestions. ``` curl -G "https://www.socialcrawl.dev/v1/tiktok/search/suggestions" \ --data-urlencode "query=dogs" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /tiktok/collection/videos List videos in a TikTok collection Credit cost: 1 (standard) Parameters: - url (required): Full URL of the TikTok collection. - max_cursor (optional, string): Cursor from the previous response to fetch the next page of collection videos. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/tiktok/collection/videos" \ --data-urlencode "url=https://www.tiktok.com/@kibblemaster808/collection/Want-to-go-7665668414573546258" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /tiktok/profile/playlists List a TikTok account's playlists (oneOf: handle|user_id) Credit cost: 1 (standard) Parameters: - handle (optional, string): TikTok username without the @ symbol. - user_id (optional, string): TikTok numeric user ID. Use this for faster responses. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - Constraint: one of handle, user_id (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/tiktok/profile/playlists" \ --data-urlencode "handle=chipotle" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /tiktok/playlist/videos List videos in a TikTok playlist (oneOf: handle|user_id) Credit cost: 1 (standard) Parameters: - playlist_id (required): Playlist id from /v1/tiktok/profile/playlists (`post.id` on that response). - handle (optional, string): TikTok username of the playlist owner, without the @ symbol. - user_id (optional, string): TikTok numeric user ID of the playlist owner. Use this for faster responses. - cursor (optional, string): Cursor from the previous response to fetch the next page of videos. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. - Constraint: one of handle, user_id (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/tiktok/playlist/videos" \ --data-urlencode "playlist_id=7229371484127382314" \ --data-urlencode "handle=chipotle" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /tiktok/user/liked List the videos a TikTok account has liked (oneOf: handle|user_id) Credit cost: 1 (standard) Parameters: - handle (optional, string): TikTok username without the @ symbol. - user_id (optional, string): TikTok numeric user ID. Use this for faster responses. - cursor (optional, string): Cursor from the previous response to fetch the next page of liked videos. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. - Constraint: one of handle, user_id (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/tiktok/user/liked" \ --data-urlencode "handle=chipotle" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /tiktok/location/posts List TikTok videos tagged at a place Credit cost: 1 (standard) Parameters: - location_id (required): TikTok place id, as it appears in the place page URL. - cursor (optional, string): Cursor from the previous response to fetch the next page of videos. - region (optional, string): Two-letter country code for the proxy location. Defaults to US. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/tiktok/location/posts" \ --data-urlencode "location_id=22535865202815278" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /tiktok/effects Get TikTok effects by id Credit cost: 1 (standard) Parameters: - ids (required): Comma-separated TikTok effect ids, up to 50 per request. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. ``` curl -G "https://www.socialcrawl.dev/v1/tiktok/effects" \ --data-urlencode "ids=1108584,1109028" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /tiktok/effect/videos List TikTok videos made with an effect Credit cost: 1 (standard) Parameters: - effect_id (required): TikTok effect id, as returned by /v1/tiktok/effects. - cursor (optional, string): Cursor from the previous response to fetch the next page of videos. - region (optional, string): Two-letter country code for the proxy location. Defaults to US. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/tiktok/effect/videos" \ --data-urlencode "effect_id=1108584" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /tiktok/search/music Search TikTok sounds Credit cost: 1 (standard) Parameters: - query (required): Search keyword, matched against sound titles and creators. - cursor (optional, string): Cursor from the previous response to fetch the next page of sounds. - sort_by (optional, enum: relevance | most-used | most-recent | shortest | longest): Result ordering: relevance (default), most-used, most-recent, shortest or longest. - filter_by (optional, enum: all | title | creators): Which field the keyword is matched against: all (default), title or creators. - region (optional, string): Two-letter country code for the proxy location. Defaults to US. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/tiktok/search/music" \ --data-urlencode "query=espresso" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /tiktok/hashtag Get TikTok hashtag details (oneOf: hashtag|hashtag_id) Credit cost: 1 (standard) Parameters: - hashtag (optional, string): Hashtag name, with or without the leading # symbol. - hashtag_id (optional, string): TikTok numeric hashtag id. Skips the name lookup when you already have it. - Constraint: one of hashtag, hashtag_id (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/tiktok/hashtag" \ --data-urlencode "hashtag=kpop" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /tiktok/ads/top Read TikTok's Creative Center Top Ads board: the best-performing ads in a market, ranked by CTR, impressions or engagement. Metered: 1 credit per ad returned, minimum 10 Credit cost: 10-100 (metered): 1 credit per ad returned, minimum 10 credits. A limit of 20 holds 20 credits and settles down to the number of ads that actually came back; a filter combination with no board costs nothing. With include=retention, 2 credits per ad that carries a retention curve (minimum 20), and 1 per ad TikTok serves no curve for. Parameters: - country (optional, enum: US | CA | MX | BR | GB | DE | FR | IT | ES | NL | PL | SE | TR | SA | AE | AU | JP | KR | ID | TH | VN | MY | PH | SG): Creative Center market to read the board for. Defaults to US. This is the market the ads ran in, not the language they are in. - period (optional, enum: 7 | 30 | 180): Lookback window in days: 7, 30 or 180. Defaults to 30. A longer window is a different board, not more rows. - order_by (optional, enum: for_you | impression | ctr | play_2s_rate | play_6s_rate | cvr | like): How TikTok ranks the board: for_you (TikTok's own blend, the default), ctr, impression, like, cvr, play_2s_rate or play_6s_rate. - ad_format (optional, enum: All ad types | Spark ads | Non-Spark ads): Restrict to Spark ads (ads boosted from an organic post) or Non-Spark ads. Defaults to all. - ad_language (optional, enum: en | ja | zh | vi | th | pt | id): Filter by the language of the ad copy. - like_tier (optional, enum: 1 | 2 | 3 | 4 | 5): TikTok's engagement percentile band, 1 (top) to 5. Use it to read further down the ranking without raising limit. - industry (optional, string): Creative Center sub-category label, for example 'Skincare' or 'Cosmetics'. Use a sub-category, not a top-level group: the board returns nothing for the broad groups such as 'Financial Services' or 'Education'. Omit for all industries. - keyword (optional, string): Search the board by brand or product keyword. - limit (optional, integer): How many ads to return, 1-100, default 20. The board is served in blocks of ten, so any limit below 10 still costs 10 credits. With include=retention, 1-20. - include (optional, string): Send `retention` to add each ad's second-by-second audience retention curve (`ext.ad.retention`) and its points at second 2 and second 6 (`ext.ad.retention_2s`, `ext.ad.retention_6s`). 2 credits per ad that carries a curve, minimum 20; up to 20 ads per call; usually 20 to 35 seconds; not combinable with keyword or industry. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. ``` curl "https://www.socialcrawl.dev/v1/tiktok/ads/top" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /tiktok/profile/full TikTok profile, recent posts, and computed analytics in one call. (oneOf: handle|user_id) Credit cost: 5 (override; tier standard) Parameters: - handle (optional, string): TikTok username or handle, with or without a leading @. One of the identity params is required. - user_id (optional, string): Numeric TikTok user id. Use it instead of the handle when you already have the stable id, which survives a rename. One of the identity params is required. - posts (optional, integer): How many of the fetched recent posts to return and compute the metrics over (1-100, default 25). One call reads one page, which on TikTok is 10 videos, so a larger value returns that page and a `_warnings` note; pass posts_cursor back as cursor for the next page. - cursor (optional, string): Pass a prior response's posts_cursor to read the next page of posts. Each call is a separate 5-credit call whose metrics cover that page only. - include (optional, string): CSV subset of posts,computed (default both). include=computed omits the raw posts[] array from the response to save payload. The metrics are computed from the same fetched posts either way, so they do not change. - Constraint: one of handle, user_id (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/tiktok/profile/full" \ --data-urlencode "handle=mrbeast" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /tiktok/search/mentions Search TikTok videos that mention an account Credit cost: 0-2 (metered): Metered, 0-2 credits: each call reads one page of /v1/tiktok/search (1 credit) and /v1/tiktok/search (1 credit), each billed at that endpoint's own price exactly as a direct call is. A page that found nothing or failed costs nothing, and a page served from cache is free. A page is billed when it returned rows, even if none of them carried the mention; data.legs shows each page's charge and data.excluded what was left out. Parameters: - handle (required): The account's username, with or without the @, or its profile URL. - cursor (optional, string): data.next_cursor from the previous response, sent back verbatim for the next page. - since (optional, string): Only posts published on or after this date, YYYY-MM-DD (UTC). - until (optional, string): Only posts published on or before this date, YYYY-MM-DD (UTC); that whole day is included. - include_self (optional, boolean): When true, also keep posts by the account itself. Default false. ``` curl -G "https://www.socialcrawl.dev/v1/tiktok/search/mentions" \ --data-urlencode "handle=nike" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## Tiktokshop ### GET /tiktokshop/product Get TikTok Shop product details (oneOf: url|product_id) Credit cost: 1-4 (metered): 1 credit for the product. include=details holds 3 more and keeps them only when it fills a description the plain call lacked, refunding them otherwise, so a hydrated call is at most 4 credits; a SEA product, which carries its description already, settles at 1. Product lookups are cached. Parameters: - url (optional, string): Full URL of the TikTok Shop product page (`www.tiktok.com/shop/pdp/...` or `shop.tiktok.com/.../pdp/...`). Provide this or `product_id`. - product_id (optional, string): Numeric TikTok Shop product id, as returned in `product.id` by tiktokshop/search, tiktokshop/products and tiktokshop/user/showcase. Provide this or `url`. - region (optional, enum: US | GB | ID | MY | TH | SG | VN | PH): ISO 3166-1 alpha-2 region for the lookup. Default US. Served: US, SG, MY, TH, VN, PH. GB and ID are temporarily unavailable (503, no charge). Other values are rejected before billing. - include (optional, enum: details): Set to `details` (one token only) to add the listing description (`product.description`), brand, specifications, and the shipping fee, free-shipping flag, delivery estimate and official-shop badge (`shop_identity`) under `product.ext.tiktokshop` when the plain call came back without them. 3 extra credits, kept only when a description comes back; a repeat within the cache window is 0. Adds about 5 to 15 seconds when a lookup runs; read `data.hydration` for the credits held and kept and the time. - Constraint: one of url, product_id (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/tiktokshop/product" \ --data-urlencode "url=https://www.tiktok.com/shop/pdp/goli-ashwagandha-gummies-with-vitamin-d-ksm-66-vegan-non-gmo/1729587769570529799" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /tiktokshop/product/reviews List TikTok Shop product reviews (oneOf: url|product_id) Credit cost: 1-5 (metered): 1 credit per page. The default labels (sentiment, issue) are free. label=reports, incentivized or injection holds 4 extra credits and refunds down to 1 credit per started 25 reviews that were newly judged (the first 100 reviews of a page are judged): reviews already labelled are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - url (optional, string): Full URL of the TikTok Shop product page. Provide this or `product_id`. - product_id (optional, string): The ID of the product (required if url is not provided) - region (optional, enum: US | GB | DE | FR | IT | ID | MY | MX | PH | SG | ES | TH | VN | BR | JP | IE): ISO 3166-1 alpha-2 region for the product (e.g. `US`). Important when the product is regionally scoped. - page (optional, integer): Page number to retrieve, 10 reviews per page. Prefer passing back `pagination.next_cursor`, which carries the same position and stays correct if the underlying paging mechanism changes. - label (optional, string): Optional CSV of SocialCrawl labels to add to every review. Without this param every page already carries sentiment and issue, free; label= adds the labels you name to them (the defaults keep running). sentiment and issue are free when asked for too; reports, incentivized and injection add 1 credit per started 25 newly judged reviews. judgments=off (or label=none) turns the default labels off. They read the review's title and text, never its stars. sentiment: how the reviewer feels, on five levels (level 0 to 4, score_0_1, confidence), plus rating_mismatch, true when the words clearly contradict the star rating (a glowing text under 1 star, a furious one under 5), null when the review has no rating. issue: the main problem the review reports (label: product_defect, sizing_or_fit, shipping_or_delivery, customer_service, price_or_value, missing_feature, other, or none, with confidence; label is null when unsure). reports (needs reports=): does this review say that the thing you describe happened (p, 0 to 1). incentivized: does the reviewer say they got the product free, discounted or rewarded for the review (p), and did the text carry a disclosure such as 'in exchange for my honest review', Vine or 체험단 (disclosed). injection flags text that addresses an AI system and tries to direct it (flagged, p); it never drops or rewrites a row. These are signals to read, never a verdict on a review or a reviewer. A review that could not be judged carries labels: null. Reviews already labelled for anyone are free, and so is a cached page. data.labels reports what was judged and billed. - reports (optional, string): Required by label=reports, ignored otherwise. What to look for, in plain words, up to 300 characters, for example reports=the battery drains quickly or reports=배송이 늦음. Every review gains labels.reports.p, the probability that it says so, in any wording or language. Without it label=reports is skipped with the warning label_reports_needs_reports and is not billed. - judgments (optional, enum: on | off): Optional, on (the default) or off. By default every row gains free SocialCrawl judgments (computed.labels, and computed.relevance on search endpoints), reported in data.labels (mode default) and data.relevance (origin default), each with a status (complete, partial or skipped) and pending: the rows still being judged when the page was sent, which carry null now and are filled on your next call or cached read. Default judgments never add credits, never change an existing field, and never drop or reorder a row. off returns the page exactly as before, with none of those keys. label=none does the same. - dry_run (optional, enum: 1): Optional. When 1, return a cost preview for this labelled or relevance-filtered request without fetching the page or judging any row. data.estimate reports rows_expected, rows_cached, label_credits_min, label_credits_max and base_credits. 0 credits charged. - label_evidence (optional, enum: 1): Optional, only with label=. When 1, every labelled row also carries computed.labels_evidence. = { quote, sentence_index }: the sentence in the row that most clearly shows the label, copied verbatim. Absent or null when no single sentence shows it. - fit (optional, enum: goal): Optional. When goal, keep the rows and fields needed for the goal you pass in goal= (plus any that are uncertain, and the first and last), and replace the rest with a stub. data.held_back lists the held ids and a recall id that re-reads the full page from cache at no extra charge. Without this param the page is unchanged. - goal (optional, string): Required by fit=goal. What you are trying to do, in your own words, up to 300 characters. - fit_tokens (optional, integer): Optional, only with fit=goal. Soft cap on how much of the page to keep, in tokens. Uncertain blocks and the first and last block are kept even if they exceed it. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. - Constraint: one of url, product_id (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/tiktokshop/product/reviews" \ --data-urlencode "url=https://www.tiktok.com/shop/pdp/cat-nail-clipper-by-potaroma-adjustable-sizes-built-in-file-safe-for-kittens-cats/1731578642912612516" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /tiktokshop/products List TikTok Shop products Credit cost: 1 (standard) Parameters: - url (required): Full URL of the TikTok Shop page - sort_by (optional, enum: top | new_releases): Sort products by best-selling (`top`) or newest (`new_releases`). Defaults to `top`. - region (optional, enum: US | GB | DE | FR | IT | ID | MY | MX | PH | SG | ES | TH | VN | BR | JP | IE): Region to get shop products from. A storefront is only reachable from its own market, so a GB shop needs `region=GB`; the default is US. - cursor (optional, string): Pagination cursor. Pass back `pagination.next_cursor` from the previous response. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/tiktokshop/products" \ --data-urlencode "url=https://www.tiktok.com/shop/store/goli-nutrition/7495794203056835079" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /tiktokshop/search Search TikTok Shop products Credit cost: 1 (standard) Parameters: - query (required): Search keyword or phrase to find TikTok Shop products - page (optional, integer): Page number to retrieve. Prefer passing back `pagination.next_cursor`, which carries the same position. - region (optional, enum: US | GB | DE | FR | IT | ID | MY | MX | PH | SG | ES | TH | VN | BR | JP | IE): Region to search shop products in. Results, prices and currency follow the market; defaults to US. `price.current` and `price.original` are always decimal amounts in the major unit of `price.currency` in every region (10.90 means 10.90 EUR). Outside the US, TikTok prices each shopper session separately, so a product's price can differ between calls and `price.original` appears only when that session was offered a promotion; those responses carry a `session_priced` entry in `data._warnings`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/tiktokshop/search" \ --data-urlencode "query=phone case" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /tiktokshop/user/showcase List TikTok user showcase products Credit cost: 1 (standard) Parameters: - handle (required): The handle of the user - region (optional, enum: US): Region the lookup is served from. Currently US only (an upstream limitation; other regions are rejected before billing). Support for more regions is planned upstream but has no date. - cursor (optional, string): The `next_cursor` from the previous page. A cursor from deep in a large showcase stays valid for 7 days; an expired one is a free 400, and the walk restarts from the first page. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/tiktokshop/user/showcase" \ --data-urlencode "handle=mrtiktokreviews" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /tiktokshop/sales TikTok Shop units sold and estimated sales per market Credit cost: 0-8 (metered): Metered, 0-8 credits: each market is one /v1/tiktokshop/search page billed at that endpoint's own price (1 credit), exactly as a direct call is billed. A market whose page found nothing or failed costs nothing, and a page served from cache is free. data.regions[].credits shows each market's charge and credits_used the total. Parameters: - query (required): Product keyword, sent unchanged to each market's search (1-200 characters). - regions (optional, string): Comma-separated markets, up to 8: US, GB, DE, FR, IT, ES, IE, ID, MY, MX, PH, SG, TH, VN, BR, JP. Default US. Each market costs one search page (1 credit) when it returns listings. - top (optional, integer): How many top listings by units to return per market, 1 to 20. Default 5. ``` curl -G "https://www.socialcrawl.dev/v1/tiktokshop/sales" \ --data-urlencode "query=lip gloss" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## Tripadvisor ### GET /tripadvisor/search Search TripAdvisor businesses & places Credit cost: 1 (standard) Parameters: - q (required): Business category, company name, or prominent place (e.g. 'pizza restaurant'). - location (optional, string): Full location name (e.g. 'New York,New York,United States'). Default: United States. Results are location-bound. - language (optional, string): Optional language code to narrow results, e.g. 'en'. - depth (optional, integer): Number of results to retrieve (multiples of 30, default 30, max 60 on this synchronous endpoint). ``` curl -G "https://www.socialcrawl.dev/v1/tripadvisor/search" \ --data-urlencode "q=pizza restaurant" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /tripadvisor/reviews Get TripAdvisor reviews for a place Credit cost: 1-5 (metered): 1 credit per page. The default labels (sentiment, issue) are free. label=reports, incentivized or injection holds 4 extra credits and refunds down to 1 credit per started 25 reviews that were newly judged (the first 100 reviews of a page are judged): reviews already labelled are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. Parameters: - url_path (required): The TripAdvisor `url_path` of the place (from a search result), e.g. 'Hotel_Review-g60763-d23462501-Reviews-Margaritaville_Times_Square-New_York_City_New_York.html'. - depth (optional, integer): Number of reviews to retrieve (multiples of 10, default 10, max 30 on this synchronous endpoint). - sort_by (optional, enum: most_recent | detailed_reviews): Review ordering: `most_recent` or `detailed_reviews`. - rating (optional, enum: excellent | very_good | average | poor | terrible): Filter by traveler rating bucket: excellent | very_good | average | poor | terrible. - visit_type (optional, enum: families | couples | solo | business | friends): Filter by traveler type: families | couples | solo | business | friends. - search_reviews_keyword (optional, string): Only return reviews containing this keyword. - translate (optional, boolean): Translate reviews to the place's domain language (default true). The `translated` flag + `original_language` are always returned. - label (optional, string): Optional CSV of SocialCrawl labels to add to every review. Without this param every page already carries sentiment and issue, free; label= adds the labels you name to them (the defaults keep running). sentiment and issue are free when asked for too; reports, incentivized and injection add 1 credit per started 25 newly judged reviews. judgments=off (or label=none) turns the default labels off. They read the review's title and text, never its stars. sentiment: how the reviewer feels, on five levels (level 0 to 4, score_0_1, confidence), plus rating_mismatch, true when the words clearly contradict the star rating (a glowing text under 1 star, a furious one under 5), null when the review has no rating. issue: the main problem the review reports (label: product_defect, sizing_or_fit, shipping_or_delivery, customer_service, price_or_value, missing_feature, other, or none, with confidence; label is null when unsure). reports (needs reports=): does this review say that the thing you describe happened (p, 0 to 1). incentivized: does the reviewer say they got the product free, discounted or rewarded for the review (p), and did the text carry a disclosure such as 'in exchange for my honest review', Vine or 체험단 (disclosed). injection flags text that addresses an AI system and tries to direct it (flagged, p); it never drops or rewrites a row. These are signals to read, never a verdict on a review or a reviewer. A review that could not be judged carries labels: null. Reviews already labelled for anyone are free, and so is a cached page. data.labels reports what was judged and billed. - reports (optional, string): Required by label=reports, ignored otherwise. What to look for, in plain words, up to 300 characters, for example reports=the battery drains quickly or reports=배송이 늦음. Every review gains labels.reports.p, the probability that it says so, in any wording or language. Without it label=reports is skipped with the warning label_reports_needs_reports and is not billed. - judgments (optional, enum: on | off): Optional, on (the default) or off. By default every row gains free SocialCrawl judgments (computed.labels, and computed.relevance on search endpoints), reported in data.labels (mode default) and data.relevance (origin default), each with a status (complete, partial or skipped) and pending: the rows still being judged when the page was sent, which carry null now and are filled on your next call or cached read. Default judgments never add credits, never change an existing field, and never drop or reorder a row. off returns the page exactly as before, with none of those keys. label=none does the same. - dry_run (optional, enum: 1): Optional. When 1, return a cost preview for this labelled or relevance-filtered request without fetching the page or judging any row. data.estimate reports rows_expected, rows_cached, label_credits_min, label_credits_max and base_credits. 0 credits charged. - label_evidence (optional, enum: 1): Optional, only with label=. When 1, every labelled row also carries computed.labels_evidence. = { quote, sentence_index }: the sentence in the row that most clearly shows the label, copied verbatim. Absent or null when no single sentence shows it. - fit (optional, enum: goal): Optional. When goal, keep the rows and fields needed for the goal you pass in goal= (plus any that are uncertain, and the first and last), and replace the rest with a stub. data.held_back lists the held ids and a recall id that re-reads the full page from cache at no extra charge. Without this param the page is unchanged. - goal (optional, string): Required by fit=goal. What you are trying to do, in your own words, up to 300 characters. - fit_tokens (optional, integer): Optional, only with fit=goal. Soft cap on how much of the page to keep, in tokens. Uncertain blocks and the first and last block are kept even if they exceed it. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off the page, and the page that reaches one ends the walk: `next_cursor` is not returned and `pagination.stopped_at` is `since`. Sending it sets the newest-first sort shown in this endpoint's sort parameter (any other sort value is a free 400). The page still costs what a page costs, so you pay for the pages you walk and no more. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. ``` curl -G "https://www.socialcrawl.dev/v1/tripadvisor/reviews" \ --data-urlencode "url_path=Hotel_Review-g60763-d23462501-Reviews-Margaritaville_Times_Square-New_York_City_New_York.html" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /tripadvisor/place Get a TripAdvisor place by URL Credit cost: 1 (standard) Parameters: - url_path (required): The TripAdvisor `url_path` of the place (from a search result), or a full TripAdvisor page URL. - language (optional, string): Optional locale for the response, e.g. 'en_US'. - currency (optional, string): Optional 3-letter currency code for price fields, e.g. 'USD'. ``` curl -G "https://www.socialcrawl.dev/v1/tripadvisor/place" \ --data-urlencode "url_path=Hotel_Review-g60763-d93450-Reviews-Hyatt_Grand_Central_New_York-New_York_City_New_York.html" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /tripadvisor/autocomplete Autocomplete TripAdvisor places Credit cost: 1 (standard) Parameters: - q (required): Destination, place, hotel, restaurant, or attraction name, e.g. 'Paris'. - depth (optional, integer): Number of suggestions to return (default 10, max 50). - language (optional, string): Optional locale, e.g. 'en-US'. ``` curl -G "https://www.socialcrawl.dev/v1/tripadvisor/autocomplete" \ --data-urlencode "q=Paris" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /tripadvisor/hotels Search TripAdvisor hotels Credit cost: 1 (standard) Parameters: - q (required): City, region, or destination, e.g. 'Paris'. - depth (optional, integer): Number of hotels to retrieve (default 30, max 100). - sort_by (optional, enum: BEST_VALUE | RANKED | PRICE_LOW_TO_HIGH | DISTANCE): Ordering: BEST_VALUE | RANKED | PRICE_LOW_TO_HIGH | DISTANCE. - price_min (optional, integer): Minimum nightly price. Omit for no minimum. - price_max (optional, integer): Maximum nightly price. Omit for no maximum. - star_rating_min (optional, enum: any | 0 | 1 | 2 | 3 | 4 | 5): Minimum hotel class: any | 0 | 1 | 2 | 3 | 4 | 5. - review_score_min (optional, enum: any | 3 | 3.5 | 4 | 4.5 | 5): Minimum guest rating: any | 3 | 3.5 | 4 | 4.5 | 5. - property_type (optional, enum: any | hotel | resort | apartment | villa | hostel | guest_house | bed_and_breakfast): Property type: any | hotel | resort | apartment | villa | hostel | guest_house | bed_and_breakfast. - currency (optional, string): Optional 3-letter currency code, e.g. 'USD'. ``` curl -G "https://www.socialcrawl.dev/v1/tripadvisor/hotels" \ --data-urlencode "q=Paris" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /tripadvisor/hotel Get a TripAdvisor hotel Credit cost: 1 (standard) Parameters: - url_path (required): The TripAdvisor `url_path` of the hotel, or a full TripAdvisor hotel URL. - language (optional, string): Optional locale for the response, e.g. 'en_US'. - currency (optional, string): Optional 3-letter currency code for price fields, e.g. 'USD'. ``` curl -G "https://www.socialcrawl.dev/v1/tripadvisor/hotel" \ --data-urlencode "url_path=Hotel_Review-g60763-d93450-Reviews-Hyatt_Grand_Central_New_York-New_York_City_New_York.html" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /tripadvisor/restaurants Search TripAdvisor restaurants Credit cost: 1 (standard) Parameters: - q (required): City, region, or destination, e.g. 'Paris'. - depth (optional, integer): Number of restaurants to retrieve (default 30, max 100). - cuisine (optional, enum: any | local | italian | french | indian | chinese | japanese | mexican | seafood | steakhouse | cafe | fast_food): Cuisine filter: any | local | italian | french | indian | chinese | japanese | mexican | seafood | steakhouse | cafe | fast_food. - meal_type (optional, enum: any | breakfast | lunch | dinner | brunch): Meal filter: any | breakfast | lunch | dinner | brunch. - price_level (optional, enum: any | cheap | moderate | expensive | fine_dining): Price band: any | cheap | moderate | expensive | fine_dining. - dietary_restriction (optional, enum: any | vegetarian | vegan | gluten_free | halal | kosher): Dietary filter: any | vegetarian | vegan | gluten_free | halal | kosher. - open_now (optional, boolean): Only return restaurants currently marked open. - online_reservations (optional, boolean): Only return restaurants that accept online reservations. - language (optional, string): Optional locale, e.g. 'en-US'. ``` curl -G "https://www.socialcrawl.dev/v1/tripadvisor/restaurants" \ --data-urlencode "q=Paris" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /tripadvisor/restaurant Get a TripAdvisor restaurant Credit cost: 1 (standard) Parameters: - url_path (required): The TripAdvisor `url_path` of the restaurant, or a full TripAdvisor restaurant URL. - language (optional, string): Optional locale for the response, e.g. 'en_US'. - currency (optional, string): Optional 3-letter currency code for price fields, e.g. 'USD'. ``` curl -G "https://www.socialcrawl.dev/v1/tripadvisor/restaurant" \ --data-urlencode "url_path=Restaurant_Review-g187147-d14039269-Reviews-Can_Alegria_Paris-Paris_Ile_de_France.html" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /tripadvisor/restaurant/reviews Get TripAdvisor reviews for a restaurant Credit cost: 1-5 (metered): 1 credit per page. The default labels (sentiment, issue) are free. label=reports, incentivized or injection holds 4 extra credits and refunds down to 1 credit per started 25 reviews that were newly judged (the first 100 reviews of a page are judged): reviews already labelled are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. Parameters: - url_path (required): The TripAdvisor `url_path` of the restaurant, or a full TripAdvisor restaurant URL. - depth (optional, integer): Number of reviews to retrieve (default 20, max 40). - language (optional, string): Review language code, e.g. 'en'. - label (optional, string): Optional CSV of SocialCrawl labels to add to every review. Without this param every page already carries sentiment and issue, free; label= adds the labels you name to them (the defaults keep running). sentiment and issue are free when asked for too; reports, incentivized and injection add 1 credit per started 25 newly judged reviews. judgments=off (or label=none) turns the default labels off. They read the review's title and text, never its stars. sentiment: how the reviewer feels, on five levels (level 0 to 4, score_0_1, confidence), plus rating_mismatch, true when the words clearly contradict the star rating (a glowing text under 1 star, a furious one under 5), null when the review has no rating. issue: the main problem the review reports (label: product_defect, sizing_or_fit, shipping_or_delivery, customer_service, price_or_value, missing_feature, other, or none, with confidence; label is null when unsure). reports (needs reports=): does this review say that the thing you describe happened (p, 0 to 1). incentivized: does the reviewer say they got the product free, discounted or rewarded for the review (p), and did the text carry a disclosure such as 'in exchange for my honest review', Vine or 체험단 (disclosed). injection flags text that addresses an AI system and tries to direct it (flagged, p); it never drops or rewrites a row. These are signals to read, never a verdict on a review or a reviewer. A review that could not be judged carries labels: null. Reviews already labelled for anyone are free, and so is a cached page. data.labels reports what was judged and billed. - reports (optional, string): Required by label=reports, ignored otherwise. What to look for, in plain words, up to 300 characters, for example reports=the battery drains quickly or reports=배송이 늦음. Every review gains labels.reports.p, the probability that it says so, in any wording or language. Without it label=reports is skipped with the warning label_reports_needs_reports and is not billed. - judgments (optional, enum: on | off): Optional, on (the default) or off. By default every row gains free SocialCrawl judgments (computed.labels, and computed.relevance on search endpoints), reported in data.labels (mode default) and data.relevance (origin default), each with a status (complete, partial or skipped) and pending: the rows still being judged when the page was sent, which carry null now and are filled on your next call or cached read. Default judgments never add credits, never change an existing field, and never drop or reorder a row. off returns the page exactly as before, with none of those keys. label=none does the same. - dry_run (optional, enum: 1): Optional. When 1, return a cost preview for this labelled or relevance-filtered request without fetching the page or judging any row. data.estimate reports rows_expected, rows_cached, label_credits_min, label_credits_max and base_credits. 0 credits charged. - label_evidence (optional, enum: 1): Optional, only with label=. When 1, every labelled row also carries computed.labels_evidence. = { quote, sentence_index }: the sentence in the row that most clearly shows the label, copied verbatim. Absent or null when no single sentence shows it. - fit (optional, enum: goal): Optional. When goal, keep the rows and fields needed for the goal you pass in goal= (plus any that are uncertain, and the first and last), and replace the rest with a stub. data.held_back lists the held ids and a recall id that re-reads the full page from cache at no extra charge. Without this param the page is unchanged. - goal (optional, string): Required by fit=goal. What you are trying to do, in your own words, up to 300 characters. - fit_tokens (optional, integer): Optional, only with fit=goal. Soft cap on how much of the page to keep, in tokens. Uncertain blocks and the first and last block are kept even if they exceed it. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. ``` curl -G "https://www.socialcrawl.dev/v1/tripadvisor/restaurant/reviews" \ --data-urlencode "url_path=Restaurant_Review-g187147-d14039269-Reviews-Can_Alegria_Paris-Paris_Ile_de_France.html" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /tripadvisor/attractions Search TripAdvisor attractions and things to do Credit cost: 1 (standard) Parameters: - q (required): City, region, or destination, e.g. 'Bangkok'. - depth (optional, integer): Number of attractions to retrieve (default 30, max 100). - category (optional, enum: any | attractions | tours | outdoor_activities | museums | shopping | nightlife | spas | classes_workshops): Category: any | attractions | tours | outdoor_activities | museums | shopping | nightlife | spas | classes_workshops. - duration (optional, enum: any | lt_1_hour | 1_to_4_hours | 4_hours_to_1_day | multi_day): Activity duration: any | lt_1_hour | 1_to_4_hours | 4_hours_to_1_day | multi_day. - traveler_rating (optional, enum: any | 3 | 3.5 | 4 | 4.5 | 5): Minimum traveler rating: any | 3 | 3.5 | 4 | 4.5 | 5. - free_cancellation (optional, boolean): Only return activities with free cancellation. - skip_the_line (optional, boolean): Only return skip-the-line activities. - private_tour (optional, boolean): Only return private tours. - currency (optional, string): Optional 3-letter currency code, e.g. 'USD'. ``` curl -G "https://www.socialcrawl.dev/v1/tripadvisor/attractions" \ --data-urlencode "q=Bangkok" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /tripadvisor/attraction Get a TripAdvisor attraction Credit cost: 1 (standard) Parameters: - url_path (required): The TripAdvisor `url_path` of the attraction, or a full TripAdvisor attraction URL. - language (optional, string): Optional locale for the response, e.g. 'en_US'. - currency (optional, string): Optional 3-letter currency code for price fields, e.g. 'USD'. ``` curl -G "https://www.socialcrawl.dev/v1/tripadvisor/attraction" \ --data-urlencode "url_path=Attraction_Review-g60763-d105127-Reviews-Central_Park-New_York_City_New_York.html" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /tripadvisor/attraction/reviews Get TripAdvisor reviews for an attraction Credit cost: 1-5 (metered): 1 credit per page. The default labels (sentiment, issue) are free. label=reports, incentivized or injection holds 4 extra credits and refunds down to 1 credit per started 25 reviews that were newly judged (the first 100 reviews of a page are judged): reviews already labelled are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. Parameters: - url_path (required): The TripAdvisor `url_path` of the attraction, or a full TripAdvisor attraction URL. - depth (optional, integer): Number of reviews to retrieve (default 20, max 40). - language (optional, string): Review language code, e.g. 'en'. - label (optional, string): Optional CSV of SocialCrawl labels to add to every review. Without this param every page already carries sentiment and issue, free; label= adds the labels you name to them (the defaults keep running). sentiment and issue are free when asked for too; reports, incentivized and injection add 1 credit per started 25 newly judged reviews. judgments=off (or label=none) turns the default labels off. They read the review's title and text, never its stars. sentiment: how the reviewer feels, on five levels (level 0 to 4, score_0_1, confidence), plus rating_mismatch, true when the words clearly contradict the star rating (a glowing text under 1 star, a furious one under 5), null when the review has no rating. issue: the main problem the review reports (label: product_defect, sizing_or_fit, shipping_or_delivery, customer_service, price_or_value, missing_feature, other, or none, with confidence; label is null when unsure). reports (needs reports=): does this review say that the thing you describe happened (p, 0 to 1). incentivized: does the reviewer say they got the product free, discounted or rewarded for the review (p), and did the text carry a disclosure such as 'in exchange for my honest review', Vine or 체험단 (disclosed). injection flags text that addresses an AI system and tries to direct it (flagged, p); it never drops or rewrites a row. These are signals to read, never a verdict on a review or a reviewer. A review that could not be judged carries labels: null. Reviews already labelled for anyone are free, and so is a cached page. data.labels reports what was judged and billed. - reports (optional, string): Required by label=reports, ignored otherwise. What to look for, in plain words, up to 300 characters, for example reports=the battery drains quickly or reports=배송이 늦음. Every review gains labels.reports.p, the probability that it says so, in any wording or language. Without it label=reports is skipped with the warning label_reports_needs_reports and is not billed. - judgments (optional, enum: on | off): Optional, on (the default) or off. By default every row gains free SocialCrawl judgments (computed.labels, and computed.relevance on search endpoints), reported in data.labels (mode default) and data.relevance (origin default), each with a status (complete, partial or skipped) and pending: the rows still being judged when the page was sent, which carry null now and are filled on your next call or cached read. Default judgments never add credits, never change an existing field, and never drop or reorder a row. off returns the page exactly as before, with none of those keys. label=none does the same. - dry_run (optional, enum: 1): Optional. When 1, return a cost preview for this labelled or relevance-filtered request without fetching the page or judging any row. data.estimate reports rows_expected, rows_cached, label_credits_min, label_credits_max and base_credits. 0 credits charged. - label_evidence (optional, enum: 1): Optional, only with label=. When 1, every labelled row also carries computed.labels_evidence. = { quote, sentence_index }: the sentence in the row that most clearly shows the label, copied verbatim. Absent or null when no single sentence shows it. - fit (optional, enum: goal): Optional. When goal, keep the rows and fields needed for the goal you pass in goal= (plus any that are uncertain, and the first and last), and replace the rest with a stub. data.held_back lists the held ids and a recall id that re-reads the full page from cache at no extra charge. Without this param the page is unchanged. - goal (optional, string): Required by fit=goal. What you are trying to do, in your own words, up to 300 characters. - fit_tokens (optional, integer): Optional, only with fit=goal. Soft cap on how much of the page to keep, in tokens. Uncertain blocks and the first and last block are kept even if they exceed it. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. ``` curl -G "https://www.socialcrawl.dev/v1/tripadvisor/attraction/reviews" \ --data-urlencode "url_path=Attraction_Review-g60763-d105127-Reviews-Central_Park-New_York_City_New_York.html" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /tripadvisor/experience-types List TripAdvisor experience types for a destination Credit cost: 1 (standard) Parameters: - query (required): City, region, or destination, e.g. 'Bangkok'. - currency (optional, string): Optional 3-letter currency code, e.g. 'USD'. ``` curl -G "https://www.socialcrawl.dev/v1/tripadvisor/experience-types" \ --data-urlencode "query=Bangkok" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /tripadvisor/cruises Search TripAdvisor cruises Credit cost: 1 (standard) Parameters: - q (required): Cruise destination, e.g. 'Caribbean'. - depth (optional, integer): Number of sailings to retrieve (default 20, max 100). - departure_month (optional, string): Departure month in YYYY-MM format, e.g. '2026-07'. - cabin_type (optional, enum: any | inside | oceanview | balcony | suite): Cabin type: any | inside | oceanview | balcony | suite. - length (optional, enum: any | 1-2 | 3-5 | 6-9 | 10-14 | 15+): Trip length in nights: any | 1-2 | 3-5 | 6-9 | 10-14 | 15+. - currency (optional, string): Optional 3-letter currency code, e.g. 'USD'. - language (optional, string): Optional locale, e.g. 'en_US'. ``` curl -G "https://www.socialcrawl.dev/v1/tripadvisor/cruises" \ --data-urlencode "q=Caribbean" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /tripadvisor/cruise Get a TripAdvisor cruise ship Credit cost: 1 (standard) Parameters: - url_path (required): The TripAdvisor cruise `url_path`, or a full TripAdvisor cruise URL. - language (optional, string): Optional locale for the response, e.g. 'en_US'. - currency (optional, string): Optional 3-letter currency code, e.g. 'USD'. ``` curl -G "https://www.socialcrawl.dev/v1/tripadvisor/cruise" \ --data-urlencode "url_path=Cruise_Review-d15691642-Reviews-MSC_Seaside" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /tripadvisor/cruise/reviews Get TripAdvisor reviews for a cruise ship Credit cost: 1-5 (metered): 1 credit per page. The default labels (sentiment, issue) are free. label=reports, incentivized or injection holds 4 extra credits and refunds down to 1 credit per started 25 reviews that were newly judged (the first 100 reviews of a page are judged): reviews already labelled are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. Parameters: - url_path (required): The TripAdvisor cruise `url_path`, or a full TripAdvisor cruise URL. - depth (optional, integer): Number of reviews to retrieve (default 20, max 40). - language (optional, string): Review language code, e.g. 'en'. - label (optional, string): Optional CSV of SocialCrawl labels to add to every review. Without this param every page already carries sentiment and issue, free; label= adds the labels you name to them (the defaults keep running). sentiment and issue are free when asked for too; reports, incentivized and injection add 1 credit per started 25 newly judged reviews. judgments=off (or label=none) turns the default labels off. They read the review's title and text, never its stars. sentiment: how the reviewer feels, on five levels (level 0 to 4, score_0_1, confidence), plus rating_mismatch, true when the words clearly contradict the star rating (a glowing text under 1 star, a furious one under 5), null when the review has no rating. issue: the main problem the review reports (label: product_defect, sizing_or_fit, shipping_or_delivery, customer_service, price_or_value, missing_feature, other, or none, with confidence; label is null when unsure). reports (needs reports=): does this review say that the thing you describe happened (p, 0 to 1). incentivized: does the reviewer say they got the product free, discounted or rewarded for the review (p), and did the text carry a disclosure such as 'in exchange for my honest review', Vine or 체험단 (disclosed). injection flags text that addresses an AI system and tries to direct it (flagged, p); it never drops or rewrites a row. These are signals to read, never a verdict on a review or a reviewer. A review that could not be judged carries labels: null. Reviews already labelled for anyone are free, and so is a cached page. data.labels reports what was judged and billed. - reports (optional, string): Required by label=reports, ignored otherwise. What to look for, in plain words, up to 300 characters, for example reports=the battery drains quickly or reports=배송이 늦음. Every review gains labels.reports.p, the probability that it says so, in any wording or language. Without it label=reports is skipped with the warning label_reports_needs_reports and is not billed. - judgments (optional, enum: on | off): Optional, on (the default) or off. By default every row gains free SocialCrawl judgments (computed.labels, and computed.relevance on search endpoints), reported in data.labels (mode default) and data.relevance (origin default), each with a status (complete, partial or skipped) and pending: the rows still being judged when the page was sent, which carry null now and are filled on your next call or cached read. Default judgments never add credits, never change an existing field, and never drop or reorder a row. off returns the page exactly as before, with none of those keys. label=none does the same. - dry_run (optional, enum: 1): Optional. When 1, return a cost preview for this labelled or relevance-filtered request without fetching the page or judging any row. data.estimate reports rows_expected, rows_cached, label_credits_min, label_credits_max and base_credits. 0 credits charged. - label_evidence (optional, enum: 1): Optional, only with label=. When 1, every labelled row also carries computed.labels_evidence. = { quote, sentence_index }: the sentence in the row that most clearly shows the label, copied verbatim. Absent or null when no single sentence shows it. - fit (optional, enum: goal): Optional. When goal, keep the rows and fields needed for the goal you pass in goal= (plus any that are uncertain, and the first and last), and replace the rest with a stub. data.held_back lists the held ids and a recall id that re-reads the full page from cache at no extra charge. Without this param the page is unchanged. - goal (optional, string): Required by fit=goal. What you are trying to do, in your own words, up to 300 characters. - fit_tokens (optional, integer): Optional, only with fit=goal. Soft cap on how much of the page to keep, in tokens. Uncertain blocks and the first and last block are kept even if they exceed it. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. ``` curl -G "https://www.socialcrawl.dev/v1/tripadvisor/cruise/reviews" \ --data-urlencode "url_path=Cruise_Review-d15691642-Reviews-MSC_Seaside" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## Trustpilot ### GET /trustpilot/business-search Search Trustpilot businesses Credit cost: 1 (standard) Parameters: - query (required): Business name or keyword (e.g. 'nike'). Matches companies, not products; do not pass a bare domain. - depth (optional, integer): Number of business results to retrieve (default 20, multiples of 10, max 140). More results = longer task time. ``` curl -G "https://www.socialcrawl.dev/v1/trustpilot/business-search" \ --data-urlencode "query=nike" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /trustpilot/reviews Get Trustpilot reviews for a business Credit cost: 5-9 (metered): 5 credits per page. The default labels (sentiment, issue) are free. label=reports, incentivized or injection holds 4 extra credits and refunds down to 1 credit per started 25 reviews that were newly judged (the first 100 reviews of a page are judged): reviews already labelled are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. Parameters: - domain (required): The business's domain on Trustpilot (e.g. 'www.nike.com' or 'booking.com'), from business-search. - depth (optional, integer): Number of reviews to retrieve (default 20, multiples of 20, max 200). There is no pagination beyond 200. - sort (optional, enum: recency | relevance): Review ordering: `recency` (newest first, default) or `relevance`. - label (optional, string): Optional CSV of SocialCrawl labels to add to every review. Without this param every page already carries sentiment and issue, free; label= adds the labels you name to them (the defaults keep running). sentiment and issue are free when asked for too; reports, incentivized and injection add 1 credit per started 25 newly judged reviews. judgments=off (or label=none) turns the default labels off. They read the review's title and text, never its stars. sentiment: how the reviewer feels, on five levels (level 0 to 4, score_0_1, confidence), plus rating_mismatch, true when the words clearly contradict the star rating (a glowing text under 1 star, a furious one under 5), null when the review has no rating. issue: the main problem the review reports (label: product_defect, sizing_or_fit, shipping_or_delivery, customer_service, price_or_value, missing_feature, other, or none, with confidence; label is null when unsure). reports (needs reports=): does this review say that the thing you describe happened (p, 0 to 1). incentivized: does the reviewer say they got the product free, discounted or rewarded for the review (p), and did the text carry a disclosure such as 'in exchange for my honest review', Vine or 체험단 (disclosed). injection flags text that addresses an AI system and tries to direct it (flagged, p); it never drops or rewrites a row. These are signals to read, never a verdict on a review or a reviewer. A review that could not be judged carries labels: null. Reviews already labelled for anyone are free, and so is a cached page. data.labels reports what was judged and billed. - reports (optional, string): Required by label=reports, ignored otherwise. What to look for, in plain words, up to 300 characters, for example reports=the battery drains quickly or reports=배송이 늦음. Every review gains labels.reports.p, the probability that it says so, in any wording or language. Without it label=reports is skipped with the warning label_reports_needs_reports and is not billed. - judgments (optional, enum: on | off): Optional, on (the default) or off. By default every row gains free SocialCrawl judgments (computed.labels, and computed.relevance on search endpoints), reported in data.labels (mode default) and data.relevance (origin default), each with a status (complete, partial or skipped) and pending: the rows still being judged when the page was sent, which carry null now and are filled on your next call or cached read. Default judgments never add credits, never change an existing field, and never drop or reorder a row. off returns the page exactly as before, with none of those keys. label=none does the same. - dry_run (optional, enum: 1): Optional. When 1, return a cost preview for this labelled or relevance-filtered request without fetching the page or judging any row. data.estimate reports rows_expected, rows_cached, label_credits_min, label_credits_max and base_credits. 0 credits charged. - label_evidence (optional, enum: 1): Optional, only with label=. When 1, every labelled row also carries computed.labels_evidence. = { quote, sentence_index }: the sentence in the row that most clearly shows the label, copied verbatim. Absent or null when no single sentence shows it. - fit (optional, enum: goal): Optional. When goal, keep the rows and fields needed for the goal you pass in goal= (plus any that are uncertain, and the first and last), and replace the rest with a stub. data.held_back lists the held ids and a recall id that re-reads the full page from cache at no extra charge. Without this param the page is unchanged. - goal (optional, string): Required by fit=goal. What you are trying to do, in your own words, up to 300 characters. - fit_tokens (optional, integer): Optional, only with fit=goal. Soft cap on how much of the page to keep, in tokens. Uncertain blocks and the first and last block are kept even if they exceed it. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off the page, and the page that reaches one ends the walk: `next_cursor` is not returned and `pagination.stopped_at` is `since`. Sending it sets the newest-first sort shown in this endpoint's sort parameter (any other sort value is a free 400). The page still costs what a page costs, so you pay for the pages you walk and no more. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. ``` curl -G "https://www.socialcrawl.dev/v1/trustpilot/reviews" \ --data-urlencode "domain=www.nike.com" \ -H "x-api-key: YOUR_API_KEY" ``` ### POST /trustpilot/reviews/history Start a Trustpilot review history job Credit cost: 6-10005 (metered): 1 credit per returned review, plus 5 credits to open. Submitting holds depth plus 5 credits and refunds the unused portion when the job settles. Parameters: - domain (required): The business domain, for example booking.com. - depth (required): How many reviews to collect, an integer from 1 to 10000. Page the result with GET /v1/trustpilot/reviews/history/{job_id}?offset=&limit=. ``` curl -X POST "https://www.socialcrawl.dev/v1/trustpilot/reviews/history" \ -H "x-api-key: YOUR_API_KEY" \ -H "content-type: application/json" \ -d '{"domain":"booking.com","depth":772}' ``` --- ## Truthsocial ### GET /truthsocial/profile Get Truth Social user profile Credit cost: 1 (standard) Parameters: - handle (required): Truth Social username without the @ symbol ``` curl -G "https://www.socialcrawl.dev/v1/truthsocial/profile" \ --data-urlencode "handle=realDonaldTrump" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /truthsocial/user/posts List Truth Social user posts (oneOf: handle|user_id) Credit cost: 1 (standard) Parameters: - handle (optional, string): Truth Social username without the @ symbol - user_id (optional, string): Truth Social user id. Use this for faster response times. Trumps is 107780257626128497. It is the 'id' field in the profile endpoint. - next_max_id (optional, string): Used to paginate to next page - trim (optional, boolean): Set to true for a trimmed down version of the response - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. - Constraint: one of handle, user_id (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/truthsocial/user/posts" \ --data-urlencode "handle=realDonaldTrump" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /truthsocial/post Get Truth Social post details Credit cost: 1 (standard) Parameters: - url (required): Full URL of the Truth Social post ``` curl -G "https://www.socialcrawl.dev/v1/truthsocial/post" \ --data-urlencode "url=https://truthsocial.com/@realDonaldTrump/posts/117400465758567373" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## Twitch ### GET /twitch/profile Get Twitch streamer profile Credit cost: 1 (standard) Parameters: - handle (required): Twitch username ``` curl -G "https://www.socialcrawl.dev/v1/twitch/profile" \ --data-urlencode "handle=ninja" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /twitch/clip Get Twitch clip details Credit cost: 1 (standard) Parameters: - url (required): Full URL of the Twitch clip ``` curl -G "https://www.socialcrawl.dev/v1/twitch/clip" \ --data-urlencode "url=https://www.twitch.tv/ninja/clip/BeautifulSolidYamOSkomodo-NXBFNORy-qFIXZ9R" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /twitch/user/videos List a Twitch user's videos Credit cost: 1 (standard) Parameters: - handle (required): Twitch username. - filter_by (optional, enum: HIGHLIGHT | UPLOAD): Filter the returned videos by type: `HIGHLIGHT` or `UPLOAD`. (Archived past broadcasts are not currently available upstream.) - sort_by (optional, enum: TIME | VIEWS): Sort order: `TIME` (newest first) or `VIEWS`. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. ``` curl -G "https://www.socialcrawl.dev/v1/twitch/user/videos" \ --data-urlencode "handle=ishowspeed" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /twitch/user/schedule Get a Twitch user's stream schedule Credit cost: 1 (standard) Parameters: - handle (required): Twitch username. ``` curl -G "https://www.socialcrawl.dev/v1/twitch/user/schedule" \ --data-urlencode "handle=kaicenat" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## Twitter ### GET /twitter/profile Get Twitter user profile Credit cost: 1 (standard) Parameters: - handle (required): Twitter username without the @ symbol ``` curl -G "https://www.socialcrawl.dev/v1/twitter/profile" \ --data-urlencode "handle=elonmusk" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /twitter/user/tweets List Twitter user tweets Credit cost: 1-5 (metered): 1 credit per page. The default labels (sponsored, intent, niche) are free. label=mention, quality, ai_style, ai_written, injection, or label=intent with offer=, holds 4 extra credits and refunds down to 1 credit per started 25 posts that were newly judged: posts already labelled are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - handle (required): Twitter username without the @ symbol - since (optional, string): Only tweets published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Older tweets are left off the page, and the page that reaches one ends the walk: `next_cursor` is not returned and `pagination.stopped_at` is `since`. Pinned tweets sit out of date order and never end the walk. The page still costs what a page costs, so a daily poll pays for the pages it walks and no more. - stop_at_id (optional, string): The id (`post.id`) or URL (`post.url`) of the newest tweet you already hold. The page stops just before it: that tweet and everything after it are left off, `next_cursor` is not returned, and `pagination.stopped_at` is `known_id`. A pinned tweet never counts as the stop point. If it is not on this page the page is returned in full with its cursor, so keep walking. `pagination.stopped_at` is `end` when the list ran out first and `null` while there is more to walk. - trim (optional, boolean): Accepted for backwards compatibility and ignored. It shrank the raw upstream payload, which the canonical response never exposed, so it changed nothing you can see - cursor (optional, string): Cursor from the previous response's pagination.next_cursor to fetch the next page - label (optional, string): Optional CSV of SocialCrawl labels to add to every post. Without this param every page already carries sponsored, intent and niche, free; label= adds the labels you name to them (the defaults keep running). sponsored, intent and niche are free when asked for too; mention, quality, ai_style, intent with offer=, and injection add 1 credit per started 25 newly judged posts. judgments=off (or label=none) turns the default labels off. sponsored: is the post a paid or gifted promotion (p, 0 to 1), did it carry a disclosure marker such as #ad or 광고 (disclosed), is it likely paid with no marker (undisclosed), and which of the accounts it mentions does it promote (brand, or null). These are signals to review, never a finding. mention (needs brand=): is the post about that brand rather than something that shares its name (about_brand, 0 to 1), how it feels about the brand on five levels (sentiment_level 0 to 4 and sentiment_score 0 to 1, null when the post is not about the brand), is it sarcastic, which aspect it talks about (taste_or_quality, price_or_value, availability_or_delivery, health_or_safety, advertising_or_campaign, customer_service, none), and did the author buy or use it (first_hand). intent: what the author is mainly doing (label: asking_for_recommendation, comparing_options, switching_away, complaining, promoting, news_or_discussion, other, or null when unsure, with confidence), whether they read as a potential buyer rather than a seller (buyer, seller), how pressing the need is (urgency, 0 to 3), and, when you pass offer=, whether your offer would plausibly help them (fits_offer). niche: which of the 33 niches of the published taxonomy sc-niche-v1 the caption belongs to, or personal_no_niche, or other (label, confidence, taxonomy), with label null when the caption is too thin to tell or the pick is unsure. It reads the caption only, not the video. quality: how much checkable detail the caption carries (fact_density 0 to 3), whether it mainly asks for likes, replies, shares, follows or tags (engagement_bait, 0 to 1), whether it is written to provoke anger as a way to get engagement (rage_bait, 0 to 1; about the writing, never the side taken), whether it only repeats someone else's news or view (secondhand, 0 to 1), and what the post is mainly doing (post_aim: inform, opinion, sell, entertain, provoke, other). exclude=engagement_bait drops posts whose engagement_bait is 0.8 or higher. ai_style: writing patterns common in AI-generated text, judged on the text alone. It does not say who wrote the post, and a person who writes in this style is flagged too. Six patterns are judged, each a probability (0 to 1): binary_contrast ("This is not X. It's Y."), faux_insight ("Here's what nobody tells you" and throat-clearing openers), colon_reveal ("The best part: it learns."), profound_kicker (ends on an aphorism or mic-drop line), puffery ("marks a pivotal moment", "highlighting the importance of") and rhetorical_setup ("What if I told you", a question the post answers itself, "Not a X. Not a Y. A Z."). Four are counted in the text: slop_words (words and phrases such as delve, leverage, robust, game changer, at the end of the day), em_dashes, emoji_bullets (lines that start with an emoji or a symbol bullet) and one_line_paragraphs (paragraphs that are one short line). pattern_count is how many patterns are present (a judged pattern at 0.8 or higher, slop_words 1 or more, em_dashes 2 or more, emoji_bullets 2 or more, one_line_paragraphs 5 or more) and markers lists their names, comma-separated. A post with less than about eight words of text is only counted, with every judged pattern 0. The judged patterns read the first 400 and the last 150 characters of a long post, and the counts read the whole post. exclude=ai_style drops posts with 2 or more patterns. ai_written: how likely the post was written by an AI model, from SocialCrawl's own text classifier, reading the opening of the post (about 1,000 characters): p (0 to 1, calibrated), level (likely (p 0.95 or higher), possible (p 0.71 or higher) or unlikely) and model (the model name; a new name means a new scale). A post under 8 words is too short to read: p null, level too_short. It is a probability, not proof: a person can be flagged and an AI-written post can pass, and the shorter the post the less certain it is. exclude=ai_written drops likely posts. injection flags text that addresses an AI system and tries to direct it (flagged, p); it never drops or rewrites a row. A post that could not be judged carries labels: null. Posts already labelled for anyone are free, and so is a cached page. data.labels reports what was judged and billed. - exclude (optional, string): Optional CSV (engagement_bait, ai_style, ai_written). engagement_bait, only with label=quality, drops posts whose engagement_bait probability is 0.8 or higher. ai_style, only with label=ai_style, drops posts that show 2 or more of its writing patterns (pattern_count 2 or higher). ai_written, only with label=ai_written, drops posts whose level is likely (p 0.95 or higher), or whose p reaches your ai_written_threshold; a post too short to read is never dropped. Dropped posts are listed in data.labels.dropped_ids. A post that could not be judged is never dropped. - ai_written_threshold (optional, string): Optional, only with label=ai_written and exclude=ai_written. The p (0 to 1) at which exclude=ai_written drops a post. Default 0.95 (level likely). 0.71 also drops possible posts: more AI posts go, and more posts by people with them. - brand (optional, string): Required by label=mention, ignored otherwise. The brand or product the posts are judged against, up to 300 characters, for example brand=Buldak. Without it label=mention is skipped with the warning label_mention_needs_brand and is not billed. - brand_description (optional, string): Optional, with label=mention. One plain sentence saying what the brand is, for example brand_description=Samyang's spicy instant noodle brand. Use it when the name is also an ordinary word or another company's name. - offer (optional, string): Optional, with label=intent. One or two plain sentences saying what you sell, up to 300 characters, for example offer=A web design agency that builds websites for small businesses. Adds fits_offer to every post; without it fits_offer is null. - judgments (optional, enum: on | off): Optional, on (the default) or off. By default every row gains free SocialCrawl judgments (computed.labels, and computed.relevance on search endpoints), reported in data.labels (mode default) and data.relevance (origin default), each with a status (complete, partial or skipped) and pending: the rows still being judged when the page was sent, which carry null now and are filled on your next call or cached read. Default judgments never add credits, never change an existing field, and never drop or reorder a row. off returns the page exactly as before, with none of those keys. label=none does the same. - dry_run (optional, enum: 1): Optional. When 1, return a cost preview for this labelled or relevance-filtered request without fetching the page or judging any row. data.estimate reports rows_expected, rows_cached, label_credits_min, label_credits_max and base_credits. 0 credits charged. - label_evidence (optional, enum: 1): Optional, only with label=. When 1, every labelled row also carries computed.labels_evidence. = { quote, sentence_index }: the sentence in the row that most clearly shows the label, copied verbatim. Absent or null when no single sentence shows it. - fit (optional, enum: goal): Optional. When goal, keep the rows and fields needed for the goal you pass in goal= (plus any that are uncertain, and the first and last), and replace the rest with a stub. data.held_back lists the held ids and a recall id that re-reads the full page from cache at no extra charge. Without this param the page is unchanged. - goal (optional, string): Required by fit=goal. What you are trying to do, in your own words, up to 300 characters. - fit_tokens (optional, integer): Optional, only with fit=goal. Soft cap on how much of the page to keep, in tokens. Uncertain blocks and the first and last block are kept even if they exceed it. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/twitter/user/tweets" \ --data-urlencode "handle=elonmusk" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /twitter/tweet Get Twitter tweet details Credit cost: 1 (standard) Parameters: - url (required): Full URL of the tweet. The numeric status id is what identifies it; the handle in the path is not checked - trim (optional, boolean): Accepted for backwards compatibility and ignored. It shrank the raw upstream payload, which the canonical response never exposed, so it changed nothing you can see and cost the author details ``` curl -G "https://www.socialcrawl.dev/v1/twitter/tweet" \ --data-urlencode "url=https://x.com/NASA/status/2073078061499617543" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /twitter/community Get Twitter community details Credit cost: 1 (standard) Parameters: - url (required): Full URL of the Twitter/X community, in the form https://x.com/i/communities/{id} ``` curl -G "https://www.socialcrawl.dev/v1/twitter/community" \ --data-urlencode "url=https://x.com/i/communities/1926186499399139650" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /twitter/community/tweets List Twitter community tweets Credit cost: 1 (standard) Parameters: - url (required): Full URL of the Twitter/X community, in the form https://x.com/i/communities/{id} - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. ``` curl -G "https://www.socialcrawl.dev/v1/twitter/community/tweets" \ --data-urlencode "url=https://x.com/i/communities/1926186499399139650" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /twitter/tweet/transcript Get Twitter video transcript Credit cost: 10 (premium) Parameters: - url (required): Full URL of the tweet containing a video ``` curl -G "https://www.socialcrawl.dev/v1/twitter/tweet/transcript" \ --data-urlencode "url=https://x.com/TheoVon/status/1916982720317821050" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /twitter/ai-search AI-powered X (Twitter) search via xAI Grok Credit cost: 5 (advanced) Parameters: - query (required): Natural-language prompt describing what you want to learn from X. The model autonomously searches X using the x_search tool with any handle / date filters you provide. - from_handles (optional, string): Comma-separated X handles (max 10). Restricts the search to posts from these accounts only. Mutually exclusive with exclude_handles. - exclude_handles (optional, string): Comma-separated X handles (max 10) to exclude from search results. Mutually exclusive with from_handles. - from_date (optional, string): ISO 8601 start date (YYYY-MM-DD). Limits the search window to posts on or after this date. - to_date (optional, string): ISO 8601 end date (YYYY-MM-DD). Limits the search window to posts on or before this date. ``` curl -G "https://www.socialcrawl.dev/v1/twitter/ai-search" \ --data-urlencode "query=What is @elonmusk saying about xAI this week?" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /twitter/search/tweets Search Twitter tweets Credit cost: 1-9 (metered): 1 credit per page. Relevance against your query (by default, or with relevance=) is free. Adding relevant_to= holds 4 extra credits and refunds down to 1 credit per started 25 rows that were newly judged: rows already judged for the same topic are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. The default labels (sponsored, intent, niche) are free. label=mention, quality, ai_style, ai_written, injection, or label=intent with offer=, holds 4 extra credits and refunds down to 1 credit per started 25 posts that were newly judged: posts already labelled are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. With `seen`, a page's price falls with its share of rows you already received under that id; credits for joined data (`include=`) are never discounted. Parameters: - query (required): Keyword or phrase to search tweets for. X operators inside this string narrow the search itself, for example since:YYYY-MM-DD, until:YYYY-MM-DD, from:handle, quoted phrases, min_faves:20, filter:images, and filter:videos. Quote a multi-word phrase when you want relevance rather than engagement ranking from sort=top - sort (optional, enum: latest | top): Result ranking: latest (default) for the newest matches first, top for the most popular matches. top ranks by engagement, not relevance, so an unquoted multi-term query can return popular posts that match none of your terms. Quote the phrase or add an operator to tighten it - cursor (optional, string): Cursor from the previous response's pagination.next_cursor to fetch the next page - switch_from (optional, string): Optional brand. data.switch_receipts lists posts where the author says they personally left that brand, with the verbatim text, the reason, where they went, and a link. There is no percentage. No extra credits. - relevance (optional, enum: score | filter): Optional. Without this param every row already carries computed.relevance against your query, free ({ p, sense, depth, spam }: is this row about what your query means, or a different thing that shares its words?), and nothing is dropped or reordered. score asks for it explicitly and waits for every row; filter also drops the rows that are not about your query, and lists their ids in data.relevance.dropped_ids. Your query is the topic; nothing to configure. A row that could not be judged is never dropped and carries relevance: null. Pagination is unchanged, so a filtered page can hold fewer rows. Free with your query as the topic; with relevant_to it adds 1 credit per started 25 newly judged rows (rows already judged for the same topic are free, and so is a cached page). - relevance_threshold (optional, string): Optional, only with relevance. The probability (0 to 1) a row must reach to be kept by relevance=filter. Default 0.5. Lower keeps more rows, higher keeps fewer. - relevant_to (optional, string): Optional, only with relevance. Up to 200 characters describing what you mean, used as the topic instead of the query. Use it when the query is ambiguous, for example query=cleopatra with relevant_to=Cleopatra, the IGT slot game. - judgments (optional, enum: on | off): Optional, on (the default) or off. By default every row gains free SocialCrawl judgments (computed.labels, and computed.relevance on search endpoints), reported in data.labels (mode default) and data.relevance (origin default), each with a status (complete, partial or skipped) and pending: the rows still being judged when the page was sent, which carry null now and are filled on your next call or cached read. Default judgments never add credits, never change an existing field, and never drop or reorder a row. off returns the page exactly as before, with none of those keys. label=none does the same. - dry_run (optional, enum: 1): Optional. When 1, return a cost preview for this labelled or relevance-filtered request without fetching the page or judging any row. data.estimate reports rows_expected, rows_cached, label_credits_min, label_credits_max and base_credits. 0 credits charged. - fit (optional, enum: goal): Optional. When goal, keep the rows and fields needed for the goal you pass in goal= (plus any that are uncertain, and the first and last), and replace the rest with a stub. data.held_back lists the held ids and a recall id that re-reads the full page from cache at no extra charge. Without this param the page is unchanged. - goal (optional, string): Required by fit=goal. What you are trying to do, in your own words, up to 300 characters. - fit_tokens (optional, integer): Optional, only with fit=goal. Soft cap on how much of the page to keep, in tokens. Uncertain blocks and the first and last block are kept even if they exceed it. - label (optional, string): Optional CSV of SocialCrawl labels to add to every post. Without this param every page already carries sponsored, intent and niche, free; label= adds the labels you name to them (the defaults keep running). sponsored, intent and niche are free when asked for too; mention, quality, ai_style, intent with offer=, and injection add 1 credit per started 25 newly judged posts. judgments=off (or label=none) turns the default labels off. sponsored: is the post a paid or gifted promotion (p, 0 to 1), did it carry a disclosure marker such as #ad or 광고 (disclosed), is it likely paid with no marker (undisclosed), and which of the accounts it mentions does it promote (brand, or null). These are signals to review, never a finding. mention (needs brand=): is the post about that brand rather than something that shares its name (about_brand, 0 to 1), how it feels about the brand on five levels (sentiment_level 0 to 4 and sentiment_score 0 to 1, null when the post is not about the brand), is it sarcastic, which aspect it talks about (taste_or_quality, price_or_value, availability_or_delivery, health_or_safety, advertising_or_campaign, customer_service, none), and did the author buy or use it (first_hand). intent: what the author is mainly doing (label: asking_for_recommendation, comparing_options, switching_away, complaining, promoting, news_or_discussion, other, or null when unsure, with confidence), whether they read as a potential buyer rather than a seller (buyer, seller), how pressing the need is (urgency, 0 to 3), and, when you pass offer=, whether your offer would plausibly help them (fits_offer). niche: which of the 33 niches of the published taxonomy sc-niche-v1 the caption belongs to, or personal_no_niche, or other (label, confidence, taxonomy), with label null when the caption is too thin to tell or the pick is unsure. It reads the caption only, not the video. quality: how much checkable detail the caption carries (fact_density 0 to 3), whether it mainly asks for likes, replies, shares, follows or tags (engagement_bait, 0 to 1), whether it is written to provoke anger as a way to get engagement (rage_bait, 0 to 1; about the writing, never the side taken), whether it only repeats someone else's news or view (secondhand, 0 to 1), and what the post is mainly doing (post_aim: inform, opinion, sell, entertain, provoke, other). exclude=engagement_bait drops posts whose engagement_bait is 0.8 or higher. ai_style: writing patterns common in AI-generated text, judged on the text alone. It does not say who wrote the post, and a person who writes in this style is flagged too. Six patterns are judged, each a probability (0 to 1): binary_contrast ("This is not X. It's Y."), faux_insight ("Here's what nobody tells you" and throat-clearing openers), colon_reveal ("The best part: it learns."), profound_kicker (ends on an aphorism or mic-drop line), puffery ("marks a pivotal moment", "highlighting the importance of") and rhetorical_setup ("What if I told you", a question the post answers itself, "Not a X. Not a Y. A Z."). Four are counted in the text: slop_words (words and phrases such as delve, leverage, robust, game changer, at the end of the day), em_dashes, emoji_bullets (lines that start with an emoji or a symbol bullet) and one_line_paragraphs (paragraphs that are one short line). pattern_count is how many patterns are present (a judged pattern at 0.8 or higher, slop_words 1 or more, em_dashes 2 or more, emoji_bullets 2 or more, one_line_paragraphs 5 or more) and markers lists their names, comma-separated. A post with less than about eight words of text is only counted, with every judged pattern 0. The judged patterns read the first 400 and the last 150 characters of a long post, and the counts read the whole post. exclude=ai_style drops posts with 2 or more patterns. ai_written: how likely the post was written by an AI model, from SocialCrawl's own text classifier, reading the opening of the post (about 1,000 characters): p (0 to 1, calibrated), level (likely (p 0.95 or higher), possible (p 0.71 or higher) or unlikely) and model (the model name; a new name means a new scale). A post under 8 words is too short to read: p null, level too_short. It is a probability, not proof: a person can be flagged and an AI-written post can pass, and the shorter the post the less certain it is. exclude=ai_written drops likely posts. injection flags text that addresses an AI system and tries to direct it (flagged, p); it never drops or rewrites a row. A post that could not be judged carries labels: null. Posts already labelled for anyone are free, and so is a cached page. data.labels reports what was judged and billed. - exclude (optional, string): Optional CSV (engagement_bait, ai_style, ai_written). engagement_bait, only with label=quality, drops posts whose engagement_bait probability is 0.8 or higher. ai_style, only with label=ai_style, drops posts that show 2 or more of its writing patterns (pattern_count 2 or higher). ai_written, only with label=ai_written, drops posts whose level is likely (p 0.95 or higher), or whose p reaches your ai_written_threshold; a post too short to read is never dropped. Dropped posts are listed in data.labels.dropped_ids. A post that could not be judged is never dropped. - ai_written_threshold (optional, string): Optional, only with label=ai_written and exclude=ai_written. The p (0 to 1) at which exclude=ai_written drops a post. Default 0.95 (level likely). 0.71 also drops possible posts: more AI posts go, and more posts by people with them. - brand (optional, string): Required by label=mention, ignored otherwise. The brand or product the posts are judged against, up to 300 characters, for example brand=Buldak. Without it label=mention is skipped with the warning label_mention_needs_brand and is not billed. - brand_description (optional, string): Optional, with label=mention. One plain sentence saying what the brand is, for example brand_description=Samyang's spicy instant noodle brand. Use it when the name is also an ordinary word or another company's name. - offer (optional, string): Optional, with label=intent. One or two plain sentences saying what you sell, up to 300 characters, for example offer=A web design agency that builds websites for small businesses. Adds fits_offer to every post; without it fits_offer is null. - label_evidence (optional, enum: 1): Optional, only with label=. When 1, every labelled row also carries computed.labels_evidence. = { quote, sentence_index }: the sentence in the row that most clearly shows the label, copied verbatim. Absent or null when no single sentence shows it. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off the page, and the page that reaches one ends the walk: `next_cursor` is not returned and `pagination.stopped_at` is `since`. Sending it sets the newest-first sort shown in this endpoint's sort parameter (any other sort value is a free 400). The page still costs what a page costs, so you pay for the pages you walk and no more. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - min_views (optional, integer): Keep only rows with at least this many views (`post.engagement.views`). A row whose view count is unknown is discarded, so every returned row meets the floor. Filtering runs on our side after each page is fetched, so every page walked is billed as usual; `data.walk.discarded` counts what was removed and why. Each page is its own entry in `GET /v1/credits/transactions`, under the page's request id listed in `data.walk.request_ids`. - max_age_days (optional, integer): Keep only rows published within this many days (`post.published_at`), 1 to 3650. A row with no date is discarded. Filtering runs on our side after each page is fetched, so every page walked is billed as usual; `data.walk.discarded` counts what was removed and why. Each page is its own entry in `GET /v1/credits/transactions`, under the page's request id listed in `data.walk.request_ids`. - sort_rows (optional, enum: views): `views`: return the kept rows ordered by view count, highest first, across every page walked. Rows without a view count go last. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them (after any filter). Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows (after any filter), then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. - seen (optional, string): An id you choose (1 to 64 letters, digits, `.`, `_` or `-`). Rows your account already received under the same `seen` id are removed from the page, and the page price falls with the share of repeats: page credits x new rows / rows on the page, rounded up, so a page of nothing but repeats is free. Only the page's own price is discounted: credits for joined data (`include=`) are charged only for rows you have not received, and are never discounted. The memory lasts 24 hours from the last call that used the id, holds row ids only, and is private to your account. Use one id across a set of related searches; `data.walk.repeats` counts what was removed. ``` curl -G "https://www.socialcrawl.dev/v1/twitter/search/tweets" \ --data-urlencode "query=nike running shoes" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /twitter/tweet/replies List Twitter tweet replies Credit cost: 1-5 (metered): 1 credit per page, with the default labels (sentiment, question, purchase_intent, complaint) included free. label=spam, toxic or low_quality holds 4 extra credits and refunds down to 1 credit per started 25 comments that were newly judged: comments already labelled are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - url (required): Full URL of the tweet to fetch replies for - cursor (optional, string): Cursor from the previous response's pagination.next_cursor to fetch more replies. It remembers `sort` and `since_id`, so you do not need to send them again - sort (optional, enum: relevance | latest): Reply order: latest for the newest replies first, with later pages continuing in time order, or relevance for the platform's ranking. Leave it out for the default ranking, which is not in time order - since_id (optional, string): The newest reply id from your last check (digits only). Returns only replies newer than it, newest first, and stops once it reaches older ones, so no next_cursor comes back after that; it also reads the next page once, so a reply X served a page late is not missed. A check with nothing new returns an empty list and costs nothing. Sets sort=latest; sort=relevance with it is a free 400 - label (optional, string): Optional CSV of SocialCrawl labels to add to every comment: sentiment, spam, question, purchase_intent, complaint, toxic, low_quality, injection. Without this param every page already carries sentiment, question, purchase_intent and complaint, free; label= adds the labels you name to them (the defaults keep running). Each comment gains computed.labels with probabilities (0 to 1), so you choose the cut-off; a comment that could not be judged carries labels: null. sentiment, question, purchase_intent and complaint are free when asked for too; spam, toxic, low_quality and injection add 1 credit per started 25 newly judged comments (comments already labelled for anyone are free, and so is a cached page). injection flags text that addresses an AI system and tries to direct it (flagged, p); it never drops or rewrites a row. judgments=off (or label=none) turns the default labels off. data.labels reports what was judged and billed. - exclude (optional, string): Optional CSV (spam, low_quality), only with the same preset in label. Drops comments whose probability is 0.8 or higher and lists their ids in data.labels.dropped_ids. A comment that could not be judged is never dropped. - judgments (optional, enum: on | off): Optional, on (the default) or off. By default every row gains free SocialCrawl judgments (computed.labels, and computed.relevance on search endpoints), reported in data.labels (mode default) and data.relevance (origin default), each with a status (complete, partial or skipped) and pending: the rows still being judged when the page was sent, which carry null now and are filled on your next call or cached read. Default judgments never add credits, never change an existing field, and never drop or reorder a row. off returns the page exactly as before, with none of those keys. label=none does the same. - dry_run (optional, enum: 1): Optional. When 1, return a cost preview for this labelled or relevance-filtered request without fetching the page or judging any row. data.estimate reports rows_expected, rows_cached, label_credits_min, label_credits_max and base_credits. 0 credits charged. - label_evidence (optional, enum: 1): Optional, only with label=. When 1, every labelled row also carries computed.labels_evidence. = { quote, sentence_index }: the sentence in the row that most clearly shows the label, copied verbatim. Absent or null when no single sentence shows it. - fit (optional, enum: goal): Optional. When goal, keep the rows and fields needed for the goal you pass in goal= (plus any that are uncertain, and the first and last), and replace the rest with a stub. data.held_back lists the held ids and a recall id that re-reads the full page from cache at no extra charge. Without this param the page is unchanged. - goal (optional, string): Required by fit=goal. What you are trying to do, in your own words, up to 300 characters. - fit_tokens (optional, integer): Optional, only with fit=goal. Soft cap on how much of the page to keep, in tokens. Uncertain blocks and the first and last block are kept even if they exceed it. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off the page, and the page that reaches one ends the walk: `next_cursor` is not returned and `pagination.stopped_at` is `since`. Sending it sets the newest-first sort shown in this endpoint's sort parameter (any other sort value is a free 400). The page still costs what a page costs, so you pay for the pages you walk and no more. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/twitter/tweet/replies" \ --data-urlencode "url=https://x.com/News24/status/2093257170653483101" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /twitter/user/media List Twitter user media tweets Credit cost: 1 (standard) Parameters: - handle (required): Twitter username without the @ symbol - cursor (optional, string): Cursor from the previous response's pagination.next_cursor to fetch the next page - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/twitter/user/media" \ --data-urlencode "handle=News24" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /twitter/user/followers List Twitter user followers Credit cost: 1 (standard) Parameters: - handle (required): Twitter username without the @ symbol - cursor (optional, string): Cursor from the previous response's pagination.next_cursor to fetch the next page - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/twitter/user/followers" \ --data-urlencode "handle=News24" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /twitter/user/following List Twitter user following Credit cost: 1 (standard) Parameters: - handle (required): Twitter username without the @ symbol - cursor (optional, string): Cursor from the previous response's pagination.next_cursor to fetch the next page - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/twitter/user/following" \ --data-urlencode "handle=News24" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /twitter/tweet/retweeters List Twitter tweet retweeters Credit cost: 1 (standard) Parameters: - url (required): Full URL of the tweet whose retweeters you want - cursor (optional, string): Cursor from the previous response's pagination.next_cursor to fetch the next page - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/twitter/tweet/retweeters" \ --data-urlencode "url=https://x.com/News24/status/2093257170653483101" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /twitter/search/users Search Twitter users Credit cost: 1 (standard) Parameters: - query (required): Name, handle, or keyword to search accounts for - cursor (optional, string): Cursor from the previous response's pagination.next_cursor to fetch the next page - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/twitter/search/users" \ --data-urlencode "query=news24" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /twitter/profile/full X (Twitter) profile, recent posts, and computed analytics in one call. (oneOf: handle) Credit cost: 5 (override; tier standard) Parameters: - handle (optional, string): X (Twitter) username or handle, with or without a leading @. One of the identity params is required. - posts (optional, integer): How many of the fetched recent posts to return and compute the metrics over (1-100, default 25). One call reads one page, which on X (Twitter) is about 10 to 20 tweets (a page of about 20, less the replies to other accounts), so a larger value returns that page and a `_warnings` note; pass posts_cursor back as cursor for the next page. - cursor (optional, string): Pass a prior response's posts_cursor to read the next page of posts. Each call is a separate 5-credit call whose metrics cover that page only. - include (optional, string): CSV subset of posts,computed (default both). include=computed omits the raw posts[] array from the response to save payload. The metrics are computed from the same fetched posts either way, so they do not change. - Constraint: one of handle (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/twitter/profile/full" \ --data-urlencode "handle=mrbeast" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /twitter/search/hashtag Search X (Twitter) posts carrying a hashtag Credit cost: 0-1 (metered): Metered, 0-1 credits: each call reads one page of /v1/twitter/search/tweets (1 credit), each billed at that endpoint's own price exactly as a direct call is. A page that found nothing or failed costs nothing, and a page served from cache is free. A page is billed when it returned rows, even if none of them carried the hashtag; data.legs shows each page's charge and data.excluded what was left out. Parameters: - hashtag (required): One hashtag, with or without the #: letters, digits and _, no spaces. - cursor (optional, string): data.next_cursor from the previous response, sent back verbatim for the next page. - since (optional, string): Only posts published on or after this date, YYYY-MM-DD (UTC). - until (optional, string): Only posts published on or before this date, YYYY-MM-DD (UTC); that whole day is included. - sort (optional, enum: latest | top): latest (default) for the newest posts first, top for the most popular. ``` curl -G "https://www.socialcrawl.dev/v1/twitter/search/hashtag" \ --data-urlencode "hashtag=farmer" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /twitter/search/mentions Search X (Twitter) posts that mention an account Credit cost: 0-1 (metered): Metered, 0-1 credits: each call reads one page of /v1/twitter/search/tweets (1 credit), each billed at that endpoint's own price exactly as a direct call is. A page that found nothing or failed costs nothing, and a page served from cache is free. A page is billed when it returned rows, even if none of them carried the mention; data.legs shows each page's charge and data.excluded what was left out. Parameters: - handle (required): The account's username, with or without the @, or its profile URL. - cursor (optional, string): data.next_cursor from the previous response, sent back verbatim for the next page. - since (optional, string): Only posts published on or after this date, YYYY-MM-DD (UTC). - until (optional, string): Only posts published on or before this date, YYYY-MM-DD (UTC); that whole day is included. - sort (optional, enum: latest | top): latest (default) for the newest posts first, top for the most popular. - include_self (optional, boolean): When true, also keep posts by the account itself. Default false. ``` curl -G "https://www.socialcrawl.dev/v1/twitter/search/mentions" \ --data-urlencode "handle=nike" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## Us_congress_trades ### GET /us_congress_trades/trades Search US Congress stock trades Credit cost: 1 (standard) Parameters: - ticker (optional, string): Stock ticker. Bare symbols such as AAPL are sent as AAPL:US. Use AAPL:US when the search filter would otherwise miss. - politician (optional, string): Politician last name (case-insensitive). - party (optional, enum: democrat | republican): democrat or republican. - chamber (optional, enum: senate | house): senate or house. - sector (optional, string): Issuer sector slug, for example information-technology or health-care. - tx_type (optional, enum: buy | sell | exchange): buy, sell, or exchange. - start_date (optional, string): Publication date lower bound (YYYY-MM-DD). - end_date (optional, string): Publication date upper bound (YYYY-MM-DD). - sort (optional, enum: pub_date | tx_date | value | reporting_gap): pub_date (default), tx_date, value, or reporting_gap. - order (optional, enum: asc | desc): desc (default) or asc. - limit (optional, integer): Page size, 1 to 100. Default 20. - offset (optional, integer): Rows to skip. Prefer the universal cursor parameter. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl "https://www.socialcrawl.dev/v1/us_congress_trades/trades" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /us_congress_trades/trades/latest Latest US Congress trades (48 hours) Credit cost: 1 (standard) Parameters: - limit (optional, integer): Page size, 1 to 100. Default 50. - offset (optional, integer): Rows to skip. Prefer the universal cursor parameter. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl "https://www.socialcrawl.dev/v1/us_congress_trades/trades/latest" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /us_congress_trades/trades/recent Recent US Congress trades (7 days) Credit cost: 1 (standard) Parameters: - limit (optional, integer): Page size, 1 to 100. Default 50. - offset (optional, integer): Rows to skip. Prefer the universal cursor parameter. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl "https://www.socialcrawl.dev/v1/us_congress_trades/trades/recent" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /us_congress_trades/members List members of Congress who have disclosed trades Credit cost: 1 (standard) Parameters: - party (optional, enum: democrat | republican): democrat or republican. - state (optional, string): Two-letter USPS state, for example CA or TX. - limit (optional, integer): Page size, 1 to 100. Default 50. - offset (optional, integer): Rows to skip. Prefer the universal cursor parameter. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl "https://www.socialcrawl.dev/v1/us_congress_trades/members" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /us_congress_trades/politician Get a politician's trading summary Credit cost: 1 (standard) Parameters: - handle (required): Politician last name, case-insensitive. Example: Pelosi. ``` curl -G "https://www.socialcrawl.dev/v1/us_congress_trades/politician" \ --data-urlencode "handle=Pelosi" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /us_congress_trades/politician/trades List trades for one politician Credit cost: 1 (standard) Parameters: - handle (required): Politician last name, case-insensitive. Example: Pelosi. - sort (optional, enum: pub_date | tx_date | value | reporting_gap): pub_date (default), tx_date, value, or reporting_gap. - order (optional, enum: asc | desc): desc (default) or asc. - limit (optional, integer): Page size, 1 to 100. Default 20. - offset (optional, integer): Rows to skip. Prefer the universal cursor parameter. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/us_congress_trades/politician/trades" \ --data-urlencode "handle=Pelosi" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /us_congress_trades/ticker Get congressional trading stats for a ticker Credit cost: 1 (standard) Parameters: - keyword (required): Stock ticker. AAPL or AAPL:US. ``` curl -G "https://www.socialcrawl.dev/v1/us_congress_trades/ticker" \ --data-urlencode "keyword=AAPL" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /us_congress_trades/ticker/trades List congressional trades for a ticker Credit cost: 1 (standard) Parameters: - keyword (required): Stock ticker. AAPL or AAPL:US. - chamber (optional, enum: senate | house): senate or house. - party (optional, enum: democrat | republican): democrat or republican. - tx_type (optional, enum: buy | sell | exchange): buy, sell, or exchange. - start_date (optional, string): Date lower bound (YYYY-MM-DD). - end_date (optional, string): Date upper bound (YYYY-MM-DD). - limit (optional, integer): Page size, 1 to 100. Default 20. - offset (optional, integer): Rows to skip. Prefer the universal cursor parameter. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/us_congress_trades/ticker/trades" \ --data-urlencode "keyword=NVDA" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /us_congress_trades/state/trades List trades from a state's congressional delegation Credit cost: 1 (standard) Parameters: - state (required): Two-letter USPS state, for example CA or TX. - limit (optional, integer): Page size, 1 to 100. Default 20. - offset (optional, integer): Rows to skip. Prefer the universal cursor parameter. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/us_congress_trades/state/trades" \ --data-urlencode "state=ca" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /us_congress_trades/stats Dataset summary Credit cost: 1 (standard) ``` curl "https://www.socialcrawl.dev/v1/us_congress_trades/stats" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /us_congress_trades/stats/party Compare trade counts by party Credit cost: 1 (standard) ``` curl "https://www.socialcrawl.dev/v1/us_congress_trades/stats/party" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /us_congress_trades/stats/sectors Trade counts by industry sector Credit cost: 1 (standard) ``` curl "https://www.socialcrawl.dev/v1/us_congress_trades/stats/sectors" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /us_congress_trades/stats/issuers Most-traded issuers in Congress Credit cost: 1 (standard) Parameters: - limit (optional, integer): How many issuers to return, 1 to 50. Default 10. ``` curl "https://www.socialcrawl.dev/v1/us_congress_trades/stats/issuers" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /us_congress_trades/stats/politicians Most-active congressional traders Credit cost: 1 (standard) Parameters: - limit (optional, integer): How many politicians to return, 1 to 50. Default 10. ``` curl "https://www.socialcrawl.dev/v1/us_congress_trades/stats/politicians" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /us_congress_trades/stats/tickers Most-traded tickers in Congress Credit cost: 1 (standard) Parameters: - tx_type (optional, enum: buy | sell): buy or sell. Omit for both. - limit (optional, integer): How many tickers to return, 1 to 50. Default 10. ``` curl "https://www.socialcrawl.dev/v1/us_congress_trades/stats/tickers" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /us_congress_trades/stats/volume Congressional trade volume over time Credit cost: 1 (standard) Parameters: - days (optional, integer): Lookback in days, 7 to 3650. Default 365. - period (optional, enum: day | week | month): day, week (default), or month. ``` curl "https://www.socialcrawl.dev/v1/us_congress_trades/stats/volume" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /us_congress_trades/stats/unusual Tickers with unusual congressional activity Credit cost: 1 (standard) Parameters: - days (optional, integer): Recent window in days, 1 to 365. Default 30. - limit (optional, integer): How many tickers to return, 1 to 50. Default 10. ``` curl "https://www.socialcrawl.dev/v1/us_congress_trades/stats/unusual" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /us_congress_trades/stats/buy-sell Congressional buy vs sell ratio Credit cost: 1 (standard) Parameters: - days (optional, integer): Lookback in days, 1 to 3650. Default 365. - party (optional, enum: democrat | republican): democrat or republican. - sector (optional, string): Issuer sector slug, for example information-technology. ``` curl "https://www.socialcrawl.dev/v1/us_congress_trades/stats/buy-sell" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /us_congress_trades/stats/reporting-gaps Politicians ranked by late STOCK Act filings Credit cost: 1 (standard) Parameters: - chamber (optional, enum: senate | house): senate or house. - limit (optional, integer): Page size, 1 to 100. Default 20. - offset (optional, integer): Rows to skip. Prefer the universal cursor parameter. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl "https://www.socialcrawl.dev/v1/us_congress_trades/stats/reporting-gaps" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## Utility ### GET /utility/endpoints List every available endpoint Credit cost: Free (0 credits) Parameters: - platform (optional, string): Filter the catalog to a single platform slug (e.g. tiktok) - search (optional, string): Keyword search over endpoint paths, summaries, parameter names and descriptions, and tags, best match first (data.ranking says keyword) - method (optional, enum: GET | POST | PATCH | DELETE): Filter by HTTP method (GET, POST, PATCH or DELETE) - fingerprint (optional, boolean): Set to 1 to return only the registry fingerprint (a hash of every endpoint's params, prices and contract facts) and the platform and endpoint counts, to check whether a bundled copy of the catalog is current ``` curl "https://www.socialcrawl.dev/v1/utility/endpoints" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /utility/endpoint How to use any endpoint (oneOf: id|url) Credit cost: Free (0 credits) Parameters: - id (optional, string): Endpoint id as platform/resource, e.g. tiktok/profile - url (optional, string): Endpoint path form, e.g. /v1/tiktok/profile (a full https URL also works) - method (optional, enum: GET | POST | PATCH | DELETE): Disambiguates a resource registered under more than one HTTP method, e.g. /v1/web/monitors/{monitor_id} is GET, PATCH and DELETE. Omit it and the first registered method wins - Constraint: one of id, url (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/utility/endpoint" \ --data-urlencode "id=tiktok/profile" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /utility/capabilities List the parameters that work across endpoints Credit cost: Free (0 credits) Parameters: - param (optional, string): Return one capability only, by parameter name (for example label or relevance) ``` curl "https://www.socialcrawl.dev/v1/utility/capabilities" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /utility/plan Plan the calls for a job (oneOf: query|task) Credit cost: Free (0 credits) Parameters: - query (optional, string): The job in plain words, in any language. Up to 600 characters are read. Provide this or task - task (optional, string): Alias for query (utility/find's name). Provide this or query - Constraint: one of query, task (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/utility/plan" \ --data-urlencode "query=Combine @acme's TikTok profile with their recent videos and the comments on them" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /utility/resolve Identify a URL, handle or id (oneOf: input|inputs) Credit cost: Free (0 credits) Parameters: - input (optional, string): One URL, @handle, platform:handle or id. Provide this or inputs, not both - inputs (optional, string): Up to 100 values separated by commas or newlines, answered in order. Provide this or input, not both - Constraint: one of input, inputs (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/utility/resolve" \ --data-urlencode "input=https://www.tiktok.com/@charlidamelio/video/7694438544479128845" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /utility/estimate Quote the credit cost of a call or a plan (oneOf: id|plan) Credit cost: Free (0 credits) Parameters: - id (optional, string): The endpoint to price, as platform/resource (tiktok/post/comments) or its /v1 path, optionally with its query string. Provide this or plan - method (optional, enum: GET | POST | PATCH | DELETE): HTTP method, when the endpoint takes more than one. Defaults to GET when offered - params (optional, string): The endpoint's own params, as a URL-encoded query string (url=...&label=spam) or a JSON object. Optional: they may also be sent directly on this URL; these win on a conflict - body (optional, string): A batch endpoint's JSON body, as JSON or base64url JSON (for example {"urls":[...]} for prism/post-stats) - items (optional, integer): Rows you want. Converted to pages with the endpoint's verified page size; warns pages_unknown when it has none - plan (optional, string): Several calls at once, as base64url JSON (or JSON): {calls:[{id, params, repeat?, items?}], fan_out?:[{from, each, to}]}, or a utility/plan answer's steps. Provide this or id - Constraint: one of id, plan (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/utility/estimate" \ --data-urlencode "id=tiktok/post/comments" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /utility/explain-error Explain a failed API response (oneOf: body|status) Credit cost: Free (0 credits) Parameters: - status (optional, integer): The HTTP status of the failed response. Optional when the body carries error.status - body (optional, string): The pasted response body: the JSON error envelope (URL-encoded or base64url) or plain text. Up to 8,000 characters. Provide this or status - request (optional, string): The request that failed, as a URL or /v1 path with its query string. Lets the answer return a corrected request. Credentials in it are dropped - Constraint: one of body, status (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/utility/explain-error" \ --data-urlencode "body={\"success\":false,\"error\":{\"type\":\"INVALID_REQUEST\",\"message\":\"Unknown query parameter 'handel'. Did you mean 'handle'?\",\"status\":400,\"details\":{\"unknown_parameters\":[\"handel\"],\"suggestions\":{\"handel\":\"handle\"}}}}" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /utility/ai-detection Check how likely a text was written by AI Credit cost: 1 credit per text. A call that cannot be scored right now (503) is not charged. Parameters: - text (required): The text to check: a post, comment or passage, up to 8000 characters (URL-encode it) ``` curl -G "https://www.socialcrawl.dev/v1/utility/ai-detection" \ --data-urlencode "text=I asked my AI employee for a summary of our feedback pile. He came back with a page our standup now opens on." \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /utility/docs-search Search the docs (oneOf: query|q) Credit cost: Free (0 credits) Parameters: - query (optional, string): What to look up, in a few words or a question. Provide this or q - q (optional, string): Short alias for query. Provide this or query - platform (optional, string): Scope the search to one platform slug - limit (optional, integer): Sections to return, 1 to 10 (default 5) - Constraint: one of query, q (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/utility/docs-search" \ --data-urlencode "query=how does cursor pagination work" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /utility/find Find the endpoint for a task (oneOf: task|query) Credit cost: Free (0 credits) Parameters: - task (optional, string): The job in plain words, in any language. Up to 600 characters are read. Provide this or query - query (optional, string): Alias for task. Provide this or task - platform (optional, string): Keep the answer on one platform slug (tiktok, instagram, ...). Omit to search every platform - Constraint: one of task, query (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/utility/find" \ --data-urlencode "task=last 200 comments on https://www.tiktok.com/@stoolpresidente/video/7623818255903329566" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /utility/recipes List task recipes with their cost Credit cost: Free (0 credits) Parameters: - id (optional, string): One recipe id (export-tiktok-comments), which adds its runnable code. Omit for the whole catalog ``` curl "https://www.socialcrawl.dev/v1/utility/recipes" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /utility/quickstart Get started in one call Credit cost: Free (0 credits) Parameters: - platform (optional, string): Tailor the first-call example and links to one platform slug ``` curl "https://www.socialcrawl.dev/v1/utility/quickstart" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /utility/llms AI-agent context payload Credit cost: Free (0 credits) Parameters: - platform (optional, string): Scope the context to one platform slug - format (optional, enum: markdown | json): markdown (default) returns the llms corpus text; json returns a structured context object ``` curl "https://www.socialcrawl.dev/v1/utility/llms" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## Walmart ### GET /walmart/product Get a Walmart product by id Credit cost: 5 (advanced) Parameters: - product_id (required): Walmart product id. Either the numeric item id from the product URL (walmart.com/ip//17835006350) or the alphanumeric catalog id (34J2GQ9D9TFM). Both resolve to the same product. - country (optional, enum: US | CA): Walmart marketplace as a two-letter country code: US (walmart.com, default) or CA (walmart.ca). A product id from one marketplace will not resolve in the other. ``` curl -G "https://www.socialcrawl.dev/v1/walmart/product" \ --data-urlencode "product_id=17835006350" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /walmart/reviews Get Walmart product reviews Credit cost: 5-9 (metered): 5 credits per page. The default labels (sentiment, issue) are free. label=reports, incentivized or injection holds 4 extra credits and refunds down to 1 credit per started 25 reviews that were newly judged (the first 100 reviews of a page are judged): reviews already labelled are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - product_id (required): Walmart product id, numeric or alphanumeric. The same id accepted by GET /v1/walmart/product. - page (optional, integer): Page number, starting at 1. Prefer the universal cursor parameter. - limit (optional, integer): Reviews per page, 1 to 50. Defaults to 10. Honoured exactly. - sort (optional, enum: relevancy | recent | rating_high_low | rating_low_high): Which reviews to return: relevancy (default), recent, rating_high_low, or rating_low_high. Changes the selection but does not guarantee the returned page is ordered; sort client-side if you need a strict order. - rating (optional, integer): Return only reviews with this exact star rating, 1 to 5. Applied exactly. - country (optional, enum: US | CA): Walmart marketplace as a two-letter country code: US (walmart.com, default) or CA (walmart.ca). A product id from one marketplace will not resolve in the other. - label (optional, string): Optional CSV of SocialCrawl labels to add to every review. Without this param every page already carries sentiment and issue, free; label= adds the labels you name to them (the defaults keep running). sentiment and issue are free when asked for too; reports, incentivized and injection add 1 credit per started 25 newly judged reviews. judgments=off (or label=none) turns the default labels off. They read the review's title and text, never its stars. sentiment: how the reviewer feels, on five levels (level 0 to 4, score_0_1, confidence), plus rating_mismatch, true when the words clearly contradict the star rating (a glowing text under 1 star, a furious one under 5), null when the review has no rating. issue: the main problem the review reports (label: product_defect, sizing_or_fit, shipping_or_delivery, customer_service, price_or_value, missing_feature, other, or none, with confidence; label is null when unsure). reports (needs reports=): does this review say that the thing you describe happened (p, 0 to 1). incentivized: does the reviewer say they got the product free, discounted or rewarded for the review (p), and did the text carry a disclosure such as 'in exchange for my honest review', Vine or 체험단 (disclosed). injection flags text that addresses an AI system and tries to direct it (flagged, p); it never drops or rewrites a row. These are signals to read, never a verdict on a review or a reviewer. A review that could not be judged carries labels: null. Reviews already labelled for anyone are free, and so is a cached page. data.labels reports what was judged and billed. - reports (optional, string): Required by label=reports, ignored otherwise. What to look for, in plain words, up to 300 characters, for example reports=the battery drains quickly or reports=배송이 늦음. Every review gains labels.reports.p, the probability that it says so, in any wording or language. Without it label=reports is skipped with the warning label_reports_needs_reports and is not billed. - judgments (optional, enum: on | off): Optional, on (the default) or off. By default every row gains free SocialCrawl judgments (computed.labels, and computed.relevance on search endpoints), reported in data.labels (mode default) and data.relevance (origin default), each with a status (complete, partial or skipped) and pending: the rows still being judged when the page was sent, which carry null now and are filled on your next call or cached read. Default judgments never add credits, never change an existing field, and never drop or reorder a row. off returns the page exactly as before, with none of those keys. label=none does the same. - dry_run (optional, enum: 1): Optional. When 1, return a cost preview for this labelled or relevance-filtered request without fetching the page or judging any row. data.estimate reports rows_expected, rows_cached, label_credits_min, label_credits_max and base_credits. 0 credits charged. - label_evidence (optional, enum: 1): Optional, only with label=. When 1, every labelled row also carries computed.labels_evidence. = { quote, sentence_index }: the sentence in the row that most clearly shows the label, copied verbatim. Absent or null when no single sentence shows it. - fit (optional, enum: goal): Optional. When goal, keep the rows and fields needed for the goal you pass in goal= (plus any that are uncertain, and the first and last), and replace the rest with a stub. data.held_back lists the held ids and a recall id that re-reads the full page from cache at no extra charge. Without this param the page is unchanged. - goal (optional, string): Required by fit=goal. What you are trying to do, in your own words, up to 300 characters. - fit_tokens (optional, integer): Optional, only with fit=goal. Soft cap on how much of the page to keep, in tokens. Uncertain blocks and the first and last block are kept even if they exceed it. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/walmart/reviews" \ --data-urlencode "product_id=17835006350" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /walmart/search Search Walmart products by keyword Credit cost: 5 (advanced) Parameters: - query (required): Search keyword or phrase. - page (optional, integer): Page number, starting at 1. Prefer the universal cursor parameter. - sort_by (optional, enum: best_match | price_low | price_high | best_seller | top_rated): Result ordering: best_match (default), price_low, price_high, best_seller, or top_rated. - min_price (optional, string): Lowest price to include. Must be below max_price when both are set. Applied by Walmart and approximate: it narrows the result set but occasionally returns a row outside the bound, so filter on product.price.current if you need a hard limit. - max_price (optional, string): Highest price to include. Must be above min_price when both are set. Applied by Walmart and approximate: it narrows the result set but occasionally returns a row outside the bound, so filter on product.price.current if you need a hard limit. - zip_code (optional, string): ZIP code used to localise availability, pickup, and shipping options. Walmart pricing and stock are store-level, so results differ by location. - state (optional, string): Two-letter US state code used to localise results (for example CA, NY). - store_id (optional, string): Walmart store id, to restrict results to one physical store's assortment. - facet (optional, string): Opaque refinement token taken from a previous response's facet data (for example a brand or category filter). - country (optional, enum: US | CA): Walmart marketplace as a two-letter country code: US (walmart.com, default) or CA (walmart.ca). A product id from one marketplace will not resolve in the other. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/walmart/search" \ --data-urlencode "query=airpods pro" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /walmart/category Browse Walmart products in a category Credit cost: 5 (advanced) Parameters: - category_id (required): Walmart category id, for example 3944 for Electronics. Category ids and their URLs are returned in the categories array of GET /v1/walmart/product. - page (optional, integer): Page number, starting at 1. Prefer the universal cursor parameter. - sort_by (optional, enum: best_match | price_low | price_high | best_seller | top_rated): Result ordering: best_match (default), price_low, price_high, best_seller, or top_rated. - min_price (optional, string): Lowest price to include. Must be below max_price when both are set. Applied by Walmart and approximate: it narrows the result set but occasionally returns a row outside the bound, so filter on product.price.current if you need a hard limit. - max_price (optional, string): Highest price to include. Must be above min_price when both are set. Applied by Walmart and approximate: it narrows the result set but occasionally returns a row outside the bound, so filter on product.price.current if you need a hard limit. - zip_code (optional, string): ZIP code used to localise availability, pickup, and shipping options. Walmart pricing and stock are store-level, so results differ by location. - state (optional, string): Two-letter US state code used to localise results (for example CA, NY). - store_id (optional, string): Walmart store id, to restrict results to one physical store's assortment. - facet (optional, string): Opaque refinement token taken from a previous response's facet data (for example a brand or category filter). - country (optional, enum: US | CA): Walmart marketplace as a two-letter country code: US (walmart.com, default) or CA (walmart.ca). A product id from one marketplace will not resolve in the other. - limit (optional, integer): Products per page, 1 to 100. Defaults to 40. Enforced by SocialCrawl: the page is trimmed to exactly this many rows. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/walmart/category" \ --data-urlencode "category_id=3944" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /walmart/offers Get every seller offering a Walmart product Credit cost: 5 (advanced) Parameters: - product_id (required): Walmart product id, numeric or alphanumeric. The same id accepted by GET /v1/walmart/product. - country (optional, enum: US | CA): Walmart marketplace as a two-letter country code: US (walmart.com, default) or CA (walmart.ca). A product id from one marketplace will not resolve in the other. ``` curl -G "https://www.socialcrawl.dev/v1/walmart/offers" \ --data-urlencode "product_id=17835006350" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## Wayfair ### GET /wayfair/search Search Wayfair products Credit cost: 5 (advanced) Parameters: - query (required): Free-text product search, for example office chair or velvet sofa. - country (optional, enum: US | CA | GB | IE | DE): Wayfair marketplace as a two-letter country code: US (wayfair.com, default), CA, GB, IE, or DE. A SKU from one marketplace will not always resolve in another. - page (optional, integer): Page number, starting at 1. Prefer the universal cursor parameter. - items_per_page (optional, enum: 24 | 48 | 96): Rows per page: 24, 48 (default), or 96. - sort_by (optional, enum: relevance | price_low | price_high | top_rated | newest): Result ordering: relevance (default), price_low, price_high, top_rated, or newest. - min_price (optional, string): Lowest price to include. Applied by Wayfair and approximate, so filter on product.price.current if you need a hard bound. - max_price (optional, string): Highest price to include. Applied by Wayfair and approximate, so filter on product.price.current if you need a hard bound. - color (optional, string): Restrict results to one colour family, for example grey. - in_stock (optional, boolean): Set true to return only products Wayfair can ship now. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/wayfair/search" \ --data-urlencode "query=office chair" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /wayfair/product Get a Wayfair product by SKU Credit cost: 5 (advanced) Parameters: - sku (required): Wayfair product SKU, the alphanumeric id at the end of a wayfair.com product URL: wayfair.com/furniture/pdp/-w003564166.html is SKU W003564166. SKUs also come back on every row of GET /v1/wayfair/search. - country (optional, enum: US | CA | GB | IE | DE): Wayfair marketplace as a two-letter country code: US (wayfair.com, default), CA, GB, IE, or DE. A SKU from one marketplace will not always resolve in another. ``` curl -G "https://www.socialcrawl.dev/v1/wayfair/product" \ --data-urlencode "sku=W003564166" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /wayfair/reviews Get Wayfair product reviews Credit cost: 5-9 (metered): 5 credits per page. The default labels (sentiment, issue) are free. label=reports, incentivized or injection holds 4 extra credits and refunds down to 1 credit per started 25 reviews that were newly judged (the first 100 reviews of a page are judged): reviews already labelled are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - sku (required): Wayfair product SKU, the alphanumeric id at the end of a wayfair.com product URL: wayfair.com/furniture/pdp/-w003564166.html is SKU W003564166. SKUs also come back on every row of GET /v1/wayfair/search. - country (optional, enum: US | CA | GB | IE | DE): Wayfair marketplace as a two-letter country code: US (wayfair.com, default), CA, GB, IE, or DE. A SKU from one marketplace will not always resolve in another. - page (optional, integer): Page number, starting at 1. Prefer the universal cursor parameter. - sort_order (optional, enum: relevance | helpful | date_ascending | date_descending | rating_ascending | rating_descending): Review ordering: relevance (default), helpful, date_ascending, date_descending, rating_ascending, or rating_descending. - star (optional, integer): Return only reviews with this star rating, 1 to 5. - label (optional, string): Optional CSV of SocialCrawl labels to add to every review. Without this param every page already carries sentiment and issue, free; label= adds the labels you name to them (the defaults keep running). sentiment and issue are free when asked for too; reports, incentivized and injection add 1 credit per started 25 newly judged reviews. judgments=off (or label=none) turns the default labels off. They read the review's title and text, never its stars. sentiment: how the reviewer feels, on five levels (level 0 to 4, score_0_1, confidence), plus rating_mismatch, true when the words clearly contradict the star rating (a glowing text under 1 star, a furious one under 5), null when the review has no rating. issue: the main problem the review reports (label: product_defect, sizing_or_fit, shipping_or_delivery, customer_service, price_or_value, missing_feature, other, or none, with confidence; label is null when unsure). reports (needs reports=): does this review say that the thing you describe happened (p, 0 to 1). incentivized: does the reviewer say they got the product free, discounted or rewarded for the review (p), and did the text carry a disclosure such as 'in exchange for my honest review', Vine or 체험단 (disclosed). injection flags text that addresses an AI system and tries to direct it (flagged, p); it never drops or rewrites a row. These are signals to read, never a verdict on a review or a reviewer. A review that could not be judged carries labels: null. Reviews already labelled for anyone are free, and so is a cached page. data.labels reports what was judged and billed. - reports (optional, string): Required by label=reports, ignored otherwise. What to look for, in plain words, up to 300 characters, for example reports=the battery drains quickly or reports=배송이 늦음. Every review gains labels.reports.p, the probability that it says so, in any wording or language. Without it label=reports is skipped with the warning label_reports_needs_reports and is not billed. - judgments (optional, enum: on | off): Optional, on (the default) or off. By default every row gains free SocialCrawl judgments (computed.labels, and computed.relevance on search endpoints), reported in data.labels (mode default) and data.relevance (origin default), each with a status (complete, partial or skipped) and pending: the rows still being judged when the page was sent, which carry null now and are filled on your next call or cached read. Default judgments never add credits, never change an existing field, and never drop or reorder a row. off returns the page exactly as before, with none of those keys. label=none does the same. - dry_run (optional, enum: 1): Optional. When 1, return a cost preview for this labelled or relevance-filtered request without fetching the page or judging any row. data.estimate reports rows_expected, rows_cached, label_credits_min, label_credits_max and base_credits. 0 credits charged. - label_evidence (optional, enum: 1): Optional, only with label=. When 1, every labelled row also carries computed.labels_evidence. = { quote, sentence_index }: the sentence in the row that most clearly shows the label, copied verbatim. Absent or null when no single sentence shows it. - fit (optional, enum: goal): Optional. When goal, keep the rows and fields needed for the goal you pass in goal= (plus any that are uncertain, and the first and last), and replace the rest with a stub. data.held_back lists the held ids and a recall id that re-reads the full page from cache at no extra charge. Without this param the page is unchanged. - goal (optional, string): Required by fit=goal. What you are trying to do, in your own words, up to 300 characters. - fit_tokens (optional, integer): Optional, only with fit=goal. Soft cap on how much of the page to keep, in tokens. Uncertain blocks and the first and last block are kept even if they exceed it. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/wayfair/reviews" \ --data-urlencode "sku=W003564166" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## Web ### GET /web/scrape Scrape a web page Credit cost: 1-5 (metered): 1 credit for a standard scrape. `proxy=auto`, `proxy=enhanced` and `pdf_parse=true` hold 5 credits and settle to what the fetch actually used, so a page a plain request could read still costs 1. Nothing else moves the price: screenshots, tag filters, `wait_for` and a mobile viewport are included. A page that comes back as the site's own block or error page (HTTP 400 or more), a CAPTCHA, a sign-in wall or an empty shell is returned with `page_state` saying so and a `_warnings` line, and is not charged. When the page scrapers cannot open a LinkedIn, Instagram, Facebook, Reddit, TikTok, YouTube, X or Threads profile or post, the page is read through that platform's own endpoint and costs that endpoint's price instead: 5 credits for a LinkedIn profile, company page or post, 1 credit for the others. The scrape's own charge is refunded to 0, so you pay the platform price once, and Activity Logs show the platform call as its own row Parameters: - url (required): Public URL to fetch. - formats (optional, string): Comma-separated output formats such as markdown,screenshot. A social platform URL is read through its own endpoint only for markdown, html and rawHtml. - only_main_content (optional, boolean): Strip nav, headers, footers and sidebars and keep the article body. Default true; send false when you need the whole page, including the chrome. A markdown-only scrape of a site the primary scraper cannot fetch is served by a second extractor, which returns the whole page. - wait_for (optional, integer): Milliseconds to wait after load before capturing, for pages that render their content in JavaScript. Leave unset unless the page comes back empty or half-built. - mobile (optional, boolean): Render in a mobile viewport with a mobile user agent. Use it when the site serves a different layout to phones. - timeout (optional, integer): Hard cap on the page load in milliseconds, 1 to 30000. A page that exceeds it fails and is refunded rather than returning a partial capture. - max_age (optional, integer): Accept an upstream-cached copy up to this many milliseconds old, which is faster and still one credit. Default 172800000 (48 hours), or 0 (a live fetch) with `proxy=auto` or `proxy=enhanced`; send 0 to force a live fetch. A cached copy of a page that failed (HTTP 400 or more) is never served: it is fetched again, live. - location_country (optional, string): ISO 3166-1 alpha-2 country code to fetch from, e.g. 'us' or 'de'. Use it for geo-varying pages such as pricing or availability. - screenshot_full_page (optional, boolean): When a screenshot format is requested, capture the entire scrollable page instead of the visible viewport. - include_tags (optional, string): CSV of CSS selectors or HTML tags to keep, e.g. 'article,main'. Everything outside them is dropped. Use it to pin extraction to a known container. - exclude_tags (optional, string): CSV of CSS selectors or HTML tags to drop, e.g. 'nav,footer,.cookie-banner'. Applied after include_tags. - proxy (optional, enum: basic | auto | enhanced): Proxy tier: basic (the default), auto (a plain request first, then a stronger one if the site blocks it), or enhanced. Use auto when a plain scrape returns a block or CAPTCHA page. auto and enhanced read the page live unless you set max_age. - pdf_parse (optional, boolean): Parse the target as a PDF and return its extracted text. Send it when the URL points at a PDF rather than an HTML page. - block_ads (optional, boolean): Block ad and tracker requests, which is faster and quieter. Default true. - remove_base64_images (optional, boolean): Drop inline base64-encoded images from the returned content instead of carrying them in the payload. Default true. ``` curl -G "https://www.socialcrawl.dev/v1/web/scrape" \ --data-urlencode "url=https://example.com" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /web/search Search the web Credit cost: 2-120 (metered): 2 credits per 10 results, so `limit` up to 10 costs 2, 11-20 costs 4, and the 100 maximum costs 20. `include_content=true` adds 1 credit per result, because it scrapes each result page as well as reading the SERP row - that is what takes a 100-result content search to the 120-credit ceiling. The hold settles down to the work actually done Parameters: - query (required): Search query. - sources (optional, string): Comma-separated sources: web,news,images. - categories (optional, string): CSV of source categories to restrict the search to, e.g. 'github,research'. Omit for an unrestricted web search. - limit (optional, integer): Results per source, from 1 to 100. - country (optional, string): ISO 3166-1 alpha-2 country code the search is run from, e.g. 'US' (default) or 'GB'. Changes which results rank. - location (optional, string): Free-text locality to bias results towards, e.g. 'London, United Kingdom'. Finer-grained than country. - time_range (optional, string): Recency window as a Google `tbs` value: 'qdr:d' past day, 'qdr:w' past week, 'qdr:m' past month, 'qdr:y' past year. - sort_by_date (optional, boolean): Sort newest-first instead of by relevance. Combines with time_range rather than replacing it. - include_domains (optional, string): CSV of domains to restrict results to, e.g. 'nytimes.com,ft.com'. Cannot be combined with exclude_domains - sending both is a 400 before billing. - exclude_domains (optional, string): CSV of domains to drop from the results. Cannot be combined with include_domains. - include_content (optional, boolean): Also scrape each result and return its page content as markdown, not just the SERP row. This is what makes the call expensive: it scrapes one page per result, which is why the range runs to 120 credits. ``` curl -G "https://www.socialcrawl.dev/v1/web/search" \ --data-urlencode "query=social media data api" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /web/map Map URLs on a site Credit cost: 1 (override; tier standard) Parameters: - url (required): Site URL to map. - search (optional, string): Optional path or keyword filter. - limit (optional, integer): Maximum URLs to return, up to 5000. - sitemap (optional, enum: include | skip | only): How to use the site's sitemap.xml: 'include' (default) to merge it with crawled links, 'only' to trust it alone, 'skip' to ignore it. - include_subdomains (optional, boolean): Follow links onto subdomains of the root URL. Default true. - ignore_query_parameters (optional, boolean): Treat URLs differing only by query string as one URL, which keeps paginated and tracking-tagged duplicates out of the result. Default true. - fresh (optional, boolean): Bypass the upstream cache and re-discover the site now. Slower; use it when the map is known to be stale. - goal (optional, string): Optional. What you are looking for on the site, in your own words. The map still returns every URL, ranked, with skipped_urls listing the ones that look unlikely to hold it (each with p). Without this param the map is unchanged. ``` curl -G "https://www.socialcrawl.dev/v1/web/map" \ --data-urlencode "url=https://example.com" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /web/extract Extract structured data from a web page (oneOf: schema|prompt) Credit cost: 5 (metered): A flat 5 credits. It runs through the metered pricer for consistency with the rest of the web surface, but nothing in your query changes the charge Parameters: - url (required): Public URL to extract from. - schema (optional, string): JSON object schema describing the data to return. - prompt (optional, string): Plain-language extraction instruction. - only_main_content (optional, boolean): Strip nav, headers, footers and sidebars and keep the article body. Default true; send false when you need the whole page, including the chrome. - timeout (optional, integer): Hard cap on each page load in milliseconds, 1 to 30000. A page that exceeds it fails and is refunded rather than returning a partial capture. - max_age (optional, integer): Accept an upstream-cached copy up to this many milliseconds old, which is faster and costs the same. Default 172800000 (48 hours); send 0 to force a live fetch. - proxy (optional, enum: basic | auto | enhanced): Proxy tier: basic, auto, or enhanced. Raise it only when a site blocks the default tier - enhanced is slower. - Constraint: one of schema, prompt (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/web/extract" \ --data-urlencode "url=https://example.com/pricing" \ --data-urlencode "prompt=Extract plan names and prices." \ -H "x-api-key: YOUR_API_KEY" ``` ### POST /web/crawl Start an async web crawl Credit cost: 1-10000 (metered): 1 credit per page crawled. Submitting holds `limit` credits up front (default limit 10, max 10,000) and the unused portion is refunded when the job settles Parameters: - url (required): Root URL to crawl. - limit (optional, integer): Maximum pages to crawl. - max_depth (optional, integer): How many link hops from the root URL to follow. 1 is the root page and the pages it links to. Leave unset to let `limit` alone bound the crawl. - allow_backward_links (optional, boolean): Crawl the whole domain rather than only URLs under the starting path. Send it when the root URL is a deep page but you want the entire site. - allow_external_links (optional, boolean): Follow links off the domain. Off by default, and easy to make expensive: `limit` is the only thing bounding it. - include_paths (optional, string): CSV of URL path patterns to crawl, e.g. '/blog/.*,/docs/.*'. Everything else is skipped, which is the cheapest way to narrow a crawl. - exclude_paths (optional, string): CSV of URL path patterns to skip, e.g. '/tag/.*'. Applied after include_paths. - webhook_url (optional, string): Optional URL the crawl backend calls directly with its own progress events. SocialCrawl does not sign or filter that callback, and its payload carries the backend's own job id, not your job_... id; poll GET /v1/web/jobs/{job_id} for the authoritative status. - formats (optional, string): CSV of output formats for each crawled page, e.g. 'markdown' (default) or 'markdown,html'. - goal (optional, string): Optional. What you are looking for on the site, in your own words. SocialCrawl maps the site, scrapes the URLs that likely hold it (always at least two), and lists every skipped URL with p. Without this param the crawl follows links by depth only. ``` curl -X POST "https://www.socialcrawl.dev/v1/web/crawl" \ -H "x-api-key: YOUR_API_KEY" \ -H "content-type: application/json" \ -d '{"url":"https://example.com"}' ``` ### POST /web/batch-scrape Start an async batch scrape Credit cost: 1 credit per URL submitted, held up front and refunded down to the URLs actually scraped when the job settles Parameters: - urls (required): Array or comma-separated list of public URLs to scrape. - ignore_invalid_urls (optional, boolean): Skip invalid URLs instead of failing the job. - formats (optional, string): CSV of output formats for each URL, e.g. 'markdown' (default) or 'markdown,html'. - only_main_content (optional, boolean): Strip nav, headers, footers and sidebars on every URL and keep the article body. Default true. - proxy (optional, enum: basic | auto | enhanced): Proxy tier applied to every URL: basic, auto, or enhanced. - webhook_url (optional, string): Accepted but not used: a batch-scrape job sends no webhook. Poll GET /v1/web/jobs/{job_id} for its status. ``` curl -X POST "https://www.socialcrawl.dev/v1/web/batch-scrape" \ -H "x-api-key: YOUR_API_KEY" \ -H "content-type: application/json" \ -d '{"urls":["https://example.com/a","https://example.com/b"]}' ``` ### GET /web/jobs List async web jobs Credit cost: 0 (override; tier standard) Parameters: - limit (optional, integer): Page size, capped at 100. - cursor (optional, string): Previous response cursor. ``` curl "https://www.socialcrawl.dev/v1/web/jobs" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /web/jobs/{job_id} Get an async web job Credit cost: 0 (override; tier standard) Parameters: - job_id (required): Job id (job_...) returned by POST /v1/web/crawl, POST /v1/web/batch-scrape, or POST /v1/web/agent as data.job_id. ``` curl "https://www.socialcrawl.dev/v1/web/jobs/job_9f3k2n8d1" \ -H "x-api-key: YOUR_API_KEY" ``` ### DELETE /web/jobs/{job_id} Cancel an async web job Credit cost: 0 (override; tier standard) Parameters: - job_id (required): Job id (job_...) returned by POST /v1/web/crawl, POST /v1/web/batch-scrape, or POST /v1/web/agent as data.job_id. ``` curl -X DELETE "https://www.socialcrawl.dev/v1/web/jobs/job_9f3k2n8d1" \ -H "x-api-key: YOUR_API_KEY" ``` ### POST /web/agent Start an async web agent job Credit cost: 25 (override; tier premium) Parameters: - url (required): Starting URL folded into the agent instruction. - prompt (required): Task instruction. - model (optional, enum: spark-1-mini | spark-1-pro): Agent model: spark-1-mini or spark-1-pro. ``` curl -X POST "https://www.socialcrawl.dev/v1/web/agent" \ -H "x-api-key: YOUR_API_KEY" \ -H "content-type: application/json" \ -d '{"url":"https://example.com","prompt":"Find the pricing page."}' ``` ### POST /web/monitors Create a web monitor Credit cost: 0 credits to create. Each scheduled check then bills its upstream cost plus 1 credit for orchestration, repeating on `cadence_minutes` until you pause or delete the monitor (a 5-minute cadence is 288 checks a day) Parameters: - url (required): URL or root target to monitor. - name (optional, string): Human-readable monitor name. - mode (optional, enum: scrape | search): Monitor target type. - cadence_minutes (optional, integer): Check interval in minutes: 5-60, or a whole number of hours up to 1440 (120, 180, ...). Hour cadences are scheduled as cron. - schedule_text (optional, string): Plain-language schedule, such as every 15 minutes. - schedule_cron (optional, string): Cron schedule. Mutually exclusive with schedule_text. - timezone (optional, string): IANA timezone for the schedule. - webhook_url (optional, string): Optional URL the monitoring backend calls directly with its own check payloads. SocialCrawl does not sign or deliver that callback, and the ids in it are the backend's own, not your wm_... id; read GET /v1/web/monitors/{monitor_id}/checks for the authoritative record. - query (optional, string): Search query the monitor re-runs, used only when mode=search. On a scrape monitor the url is the target instead. - goal (optional, string): Plain-language description of what a meaningful change looks like, used by the judge to decide whether a check is worth notifying about. A search monitor needs this unless judge_enabled is false. - retention_days (optional, integer): How long to keep each check's captured result, 1 to 365 days. Older checks are dropped. - judge_enabled (optional, boolean): Run the LLM judge over each check so you are notified on meaningful changes rather than every byte-level diff. Defaults on, except on a search monitor with no goal. ``` curl -X POST "https://www.socialcrawl.dev/v1/web/monitors" \ -H "x-api-key: YOUR_API_KEY" \ -H "content-type: application/json" \ -d '{"url":"https://example.com"}' ``` ### GET /web/monitors List web monitors Credit cost: 0 (override; tier standard) Parameters: - cursor (optional, string): Pagination cursor. - limit (optional, integer): Page size. ``` curl "https://www.socialcrawl.dev/v1/web/monitors" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /web/monitors/{monitor_id} Get a web monitor Credit cost: 0 (override; tier standard) Parameters: - monitor_id (required): Monitor id (wm_...) returned by POST /v1/web/monitors as data.monitor_id. ``` curl "https://www.socialcrawl.dev/v1/web/monitors/wm_5d1p8s3k7" \ -H "x-api-key: YOUR_API_KEY" ``` ### PATCH /web/monitors/{monitor_id} Update a web monitor Credit cost: 0 (override; tier standard) Parameters: - monitor_id (required): Monitor id (wm_...) returned by POST /v1/web/monitors as data.monitor_id. - status (optional, enum: active | paused): Set to paused to stop the schedule without losing history, or active to resume it. - cadence_minutes (optional, integer): New check interval in minutes: 5-60, or a whole number of hours up to 1440 (120, 180, ...). Hour cadences are scheduled as cron. - schedule_text (optional, string): New plain-language schedule, such as every 15 minutes. Upstream accepts 1-60 minutes in this form; use schedule_cron for anything longer. - schedule_cron (optional, string): New cron schedule. Mutually exclusive with schedule_text. - timezone (optional, string): IANA timezone the new schedule is interpreted in, e.g. 'Europe/London'. Defaults to UTC. ``` curl -X PATCH "https://www.socialcrawl.dev/v1/web/monitors/wm_5d1p8s3k7" \ -H "x-api-key: YOUR_API_KEY" \ -H "content-type: application/json" \ -d '{"status":"paused"}' ``` ### DELETE /web/monitors/{monitor_id} Delete a web monitor Credit cost: 0 (override; tier standard) Parameters: - monitor_id (required): Monitor id (wm_...) returned by POST /v1/web/monitors as data.monitor_id. ``` curl -X DELETE "https://www.socialcrawl.dev/v1/web/monitors/wm_5d1p8s3k7" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /web/monitors/{monitor_id}/checks List web monitor checks Credit cost: 0 (override; tier standard) Parameters: - monitor_id (required): Monitor id (wm_...) returned by POST /v1/web/monitors as data.monitor_id. - limit (optional, integer): How many checks to return, newest first. Page size, capped at 100. ``` curl "https://www.socialcrawl.dev/v1/web/monitors/wm_5d1p8s3k7/checks" \ -H "x-api-key: YOUR_API_KEY" ``` ### POST /web/sessions Create an interactive web session Credit cost: 20 credits per browser-hour, minimum 5. The hold is taken from `ttl_seconds` when the session is created (60s default = 5, the 3,600s maximum = 20) and settled when it closes Parameters: - url (required): URL to open in the session after creation. - ttl_seconds (optional, integer): Session TTL in seconds. - activity_ttl_seconds (optional, integer): Idle timeout in seconds: the session closes this long after the last interaction, even if ttl_seconds has not elapsed. Closing early settles the hold down, so it lowers the bill. - stream_web_view (optional, boolean): Return a live view URL in the response so a human can watch the session drive the page. ``` curl -X POST "https://www.socialcrawl.dev/v1/web/sessions" \ -H "x-api-key: YOUR_API_KEY" \ -H "content-type: application/json" \ -d '{"url":"https://example.com"}' ``` ### GET /web/sessions List interactive web sessions Credit cost: 0 (override; tier standard) Parameters: - limit (optional, integer): Page size. ``` curl "https://www.socialcrawl.dev/v1/web/sessions" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /web/sessions/{session_id} Get an interactive web session Credit cost: 0 (override; tier standard) Parameters: - session_id (required): Session id (ws_...) returned by POST /v1/web/sessions as data.session_id. ``` curl "https://www.socialcrawl.dev/v1/web/sessions/ws_3g7x1v5m2" \ -H "x-api-key: YOUR_API_KEY" ``` ### POST /web/sessions/{session_id}/execute Execute an interaction in a web session Credit cost: 0 (override; tier standard) Parameters: - session_id (required): SocialCrawl session id. - code (required): Code to execute in the browser sandbox. - language (optional, enum: node | python | bash): Execution language: node, python, or bash. - timeout (optional, integer): Execution timeout in seconds, from 1 to 40 (default 30). Larger values are capped at 40 so the call always returns inside the request window; a script still running at the cap returns a 504 UPSTREAM_ERROR envelope. Split longer work across several calls to the same session. ``` curl -X POST "https://www.socialcrawl.dev/v1/web/sessions/ws_3g7x1v5m2/execute" \ -H "x-api-key: YOUR_API_KEY" \ -H "content-type: application/json" \ -d '{"code":"await page.goto(\"https://example.com\"); console.log(await page.title());"}' ``` ### DELETE /web/sessions/{session_id} Close an interactive web session Credit cost: 0 (override; tier standard) Parameters: - session_id (required): Session id (ws_...) returned by POST /v1/web/sessions as data.session_id. ``` curl -X DELETE "https://www.socialcrawl.dev/v1/web/sessions/ws_3g7x1v5m2" \ -H "x-api-key: YOUR_API_KEY" ``` ### POST /web/parse Parse an uploaded document Credit cost: 1 (override; tier standard) Parameters: - file (required): Multipart file field. - filename (optional, string): Name to record for the uploaded file, e.g. 'report.pdf'. Used for the parsed document's title when the file carries none. - mime_type (optional, string): Optional MIME type override. - url (optional, string): Public URL to fetch and parse instead of uploading a file. Send either this or the file field. ``` curl -X POST "https://www.socialcrawl.dev/v1/web/parse" \ -H "x-api-key: YOUR_API_KEY" \ -F "file=@document.pdf" ``` --- ## Xiaohongshu ### GET /xiaohongshu/search Search Xiaohongshu notes Credit cost: 5-100 (metered): 5 credits per row RETURNED. The call holds `limit x 5` up front and settles down to the rows actually delivered. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - query (required): Search keywords, for example 护肤 or camping. - limit (optional, integer): Rows to return, 1 to 20. Default 10. Each row costs 5 credits and you are charged only for rows returned. - cursor (optional, string): Continuation token from a previous response's pagination block. Omit for the first page. - sort (optional, enum: relevance | latest | most_liked | most_commented | most_collected): Result ordering: relevance, latest, most_liked, most_commented, or most_collected. Default relevance. - type (optional, enum: all | image | video): Note type: all, image, or video. Default all. - published (optional, enum: all | day | week | half_year): Recency window: all, day, week, or half_year. Default all. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/xiaohongshu/search" \ --data-urlencode "query=护肤" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /xiaohongshu/trending Get the Xiaohongshu hot search board Credit cost: 5-100 (metered): 5 credits per row RETURNED. The call holds `limit x 5` up front and settles down to the rows actually delivered. Parameters: - limit (optional, integer): Topics to return, 1 to 20. Default 10. Each topic costs 5 credits and you are charged only for topics returned. ``` curl "https://www.socialcrawl.dev/v1/xiaohongshu/trending" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /xiaohongshu/profile Get a Xiaohongshu creator (oneOf: url|id) Credit cost: 5 (advanced) Parameters: - url (optional, string): Xiaohongshu profile URL, for example https://www.xiaohongshu.com/user/profile/6a59f257000000000f03b000. - id (optional, string): User id, the author_user_id on a note. Not the RedNote id. - Constraint: one of url, id (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/xiaohongshu/profile" \ --data-urlencode "url=https://www.xiaohongshu.com/user/profile/6a59f257000000000f03b000" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /xiaohongshu/profile/posts List a Xiaohongshu creator's notes (oneOf: url|id) Credit cost: 5-100 (metered): 5 credits per row RETURNED. The call holds `limit x 5` up front and settles down to the rows actually delivered. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - url (optional, string): Xiaohongshu profile URL. - id (optional, string): User id, the author_user_id on a note. Not the RedNote id. - limit (optional, integer): Rows to return, 1 to 20. Default 10. Each row costs 5 credits and you are charged only for rows returned. - cursor (optional, string): Continuation token from a previous response's pagination block. Omit for the first page. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. - Constraint: one of url, id (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/xiaohongshu/profile/posts" \ --data-urlencode "url=https://www.xiaohongshu.com/user/profile/6a59f257000000000f03b000" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /xiaohongshu/post Get a Xiaohongshu note Credit cost: 5 (advanced) Parameters: - url (required): Note URL, for example https://www.xiaohongshu.com/explore/6aac8143000000002a007389, or the bare note id. ``` curl -G "https://www.socialcrawl.dev/v1/xiaohongshu/post" \ --data-urlencode "url=https://www.xiaohongshu.com/explore/6aac8143000000002a007389" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /xiaohongshu/post/comments Get Xiaohongshu note comments Credit cost: 5-100 (metered): 5 credits per row RETURNED. The call holds `limit x 5` up front and settles down to the rows actually delivered. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - url (required): Note URL or the bare note id the comments sit under. - limit (optional, integer): Rows to return, 1 to 20. Default 10. Each row costs 5 credits and you are charged only for rows returned. - cursor (optional, string): Continuation token from a previous response's pagination block. Omit for the first page. - sort (optional, enum: relevance | latest | most_liked): Comment ordering: relevance, latest, or most_liked. Default relevance. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/xiaohongshu/post/comments" \ --data-urlencode "url=https://www.xiaohongshu.com/explore/6aac8143000000002a007389" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## Yelp ### GET /yelp/business/info Get a Yelp business by encid (oneOf: id|url) Credit cost: 1 (standard) Parameters: - id (optional, string): Yelp business encid, the 22-character opaque id (for example zj8Lq1T8KIC5zwFief15jg for Prince Street Pizza). Alias slugs such as prince-street-pizza-new-york-2 are not accepted on this surface: they come back as not found. - url (optional, string): Yelp business URL. Only URLs whose /biz/ segment is the 22-character encid resolve. Alias URLs such as https://www.yelp.com/biz/prince-street-pizza-new-york-2 come back as not found. - Constraint: one of id, url (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/yelp/business/info" \ --data-urlencode "id=zj8Lq1T8KIC5zwFief15jg" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /yelp/business/reviews Get Yelp reviews for a business (oneOf: id|url) Credit cost: 5-9 (metered): 5 credits per page. The default labels (sentiment, issue) are free. label=reports, incentivized or injection holds 4 extra credits and refunds down to 1 credit per started 25 reviews that were newly judged (the first 100 reviews of a page are judged): reviews already labelled are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - id (optional, string): Yelp business encid, the 22-character opaque id (for example zj8Lq1T8KIC5zwFief15jg for Prince Street Pizza). Alias slugs such as prince-street-pizza-new-york-2 are not accepted on this surface: they come back as not found. - url (optional, string): Yelp business URL. Only URLs whose /biz/ segment is the 22-character encid resolve. Alias URLs such as https://www.yelp.com/biz/prince-street-pizza-new-york-2 come back as not found. - cursor (optional, string): Opaque cursor from the previous page's next_cursor. Omit on page 1. - label (optional, string): Optional CSV of SocialCrawl labels to add to every review. Without this param every page already carries sentiment and issue, free; label= adds the labels you name to them (the defaults keep running). sentiment and issue are free when asked for too; reports, incentivized and injection add 1 credit per started 25 newly judged reviews. judgments=off (or label=none) turns the default labels off. They read the review's title and text, never its stars. sentiment: how the reviewer feels, on five levels (level 0 to 4, score_0_1, confidence), plus rating_mismatch, true when the words clearly contradict the star rating (a glowing text under 1 star, a furious one under 5), null when the review has no rating. issue: the main problem the review reports (label: product_defect, sizing_or_fit, shipping_or_delivery, customer_service, price_or_value, missing_feature, other, or none, with confidence; label is null when unsure). reports (needs reports=): does this review say that the thing you describe happened (p, 0 to 1). incentivized: does the reviewer say they got the product free, discounted or rewarded for the review (p), and did the text carry a disclosure such as 'in exchange for my honest review', Vine or 체험단 (disclosed). injection flags text that addresses an AI system and tries to direct it (flagged, p); it never drops or rewrites a row. These are signals to read, never a verdict on a review or a reviewer. A review that could not be judged carries labels: null. Reviews already labelled for anyone are free, and so is a cached page. data.labels reports what was judged and billed. - reports (optional, string): Required by label=reports, ignored otherwise. What to look for, in plain words, up to 300 characters, for example reports=the battery drains quickly or reports=배송이 늦음. Every review gains labels.reports.p, the probability that it says so, in any wording or language. Without it label=reports is skipped with the warning label_reports_needs_reports and is not billed. - judgments (optional, enum: on | off): Optional, on (the default) or off. By default every row gains free SocialCrawl judgments (computed.labels, and computed.relevance on search endpoints), reported in data.labels (mode default) and data.relevance (origin default), each with a status (complete, partial or skipped) and pending: the rows still being judged when the page was sent, which carry null now and are filled on your next call or cached read. Default judgments never add credits, never change an existing field, and never drop or reorder a row. off returns the page exactly as before, with none of those keys. label=none does the same. - dry_run (optional, enum: 1): Optional. When 1, return a cost preview for this labelled or relevance-filtered request without fetching the page or judging any row. data.estimate reports rows_expected, rows_cached, label_credits_min, label_credits_max and base_credits. 0 credits charged. - label_evidence (optional, enum: 1): Optional, only with label=. When 1, every labelled row also carries computed.labels_evidence. = { quote, sentence_index }: the sentence in the row that most clearly shows the label, copied verbatim. Absent or null when no single sentence shows it. - fit (optional, enum: goal): Optional. When goal, keep the rows and fields needed for the goal you pass in goal= (plus any that are uncertain, and the first and last), and replace the rest with a stub. data.held_back lists the held ids and a recall id that re-reads the full page from cache at no extra charge. Without this param the page is unchanged. - goal (optional, string): Required by fit=goal. What you are trying to do, in your own words, up to 300 characters. - fit_tokens (optional, integer): Optional, only with fit=goal. Soft cap on how much of the page to keep, in tokens. Uncertain blocks and the first and last block are kept even if they exceed it. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. - Constraint: one of id, url (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/yelp/business/reviews" \ --data-urlencode "id=zj8Lq1T8KIC5zwFief15jg" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /yelp/search Search Yelp businesses Credit cost: 1 (standard) Parameters: - query (required): Search term as typed on Yelp, for example Pizza or coffee. - location (required): City or full location string, for example New York, NY or San Francisco, CA, United States. Required with query. - cursor (optional, string): Cursor from the previous page's next_cursor. Omit on page 1. Pages hold 10 regular businesses (plus any sponsored ones when ads=true); the cursor is a numeric offset such as 10. - sort (optional, enum: recommended | rating | review_count): Result order. Only recommended (the default, Yelp's recommended order) is served. rating and review_count are refused with a free 400: no source can order Yelp search by them. To rank by rating or review count, sort the returned items by place.rating.value or place.reviews_count yourself. - ads (optional, boolean): When true, sponsored businesses are added after the page's regular results. They are not marked as sponsored. Default false. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/yelp/search" \ --data-urlencode "query=Pizza" \ --data-urlencode "location=New York, NY" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /yelp/search/full Search Yelp businesses with full cards Credit cost: 1 (standard) Parameters: - query (required): Search term as typed on Yelp, for example Pizza or coffee. - location (required): City or full location string, for example New York, NY or San Francisco, CA, United States. Required with query. - cursor (optional, string): Cursor from the previous page's next_cursor. Omit on page 1. Pages hold 10 regular businesses (plus any sponsored ones when ads=true); the cursor is a numeric offset such as 10. - sort (optional, enum: recommended | rating | review_count): Result order. Only recommended (the default, Yelp's recommended order) is served. rating and review_count are refused with a free 400: no source can order Yelp search by them. To rank by rating or review count, sort the returned items by place.rating.value or place.reviews_count yourself. - ads (optional, boolean): When true, sponsored businesses are added after the page's regular results. They are not marked as sponsored. Default false. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/yelp/search/full" \ --data-urlencode "query=Pizza" \ --data-urlencode "location=New York, NY" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /yelp/search/suggestions Get Yelp search suggestions Credit cost: 1 (standard) Parameters: - query (required): Search term as typed on Yelp, for example Pizza or coffee. - location (required): City or full location string, for example New York, NY or San Francisco, CA, United States. Required with query. ``` curl -G "https://www.socialcrawl.dev/v1/yelp/search/suggestions" \ --data-urlencode "query=Pizza" \ --data-urlencode "location=New York, NY" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## Youtube ### GET /youtube/channel Get YouTube channel info (oneOf: channelId|handle|url) Credit cost: 1 (standard) Parameters: - channelId (optional, string): YouTube channel ID. Can pass a channelId, handle or url - handle (optional, string): YouTube channel handle without the @ symbol - url (optional, string): YouTube channel URL. Can pass a channelId, handle or url - hl (optional, string): Preferred response language for localized text (ISO 639-1, e.g. 'en', 'es', 'fr'). - forUsername (optional, string): Legacy YouTube username (pre-handle) to look up. - contact_email (optional, enum: 1): Optional. When 1, also returns author.ext.contact_email: when the bio lists two or more email addresses, the one the creator gives for contacting them personally, copied verbatim from the bio. Null when there is one address or none, when every address belongs to a manager, agency or brand, or when the choice was not confident. author.ext.public_email is unchanged. No extra credits. - Constraint: one of channelId, handle, url (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/youtube/channel" \ --data-urlencode "handle=MrBeast" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /youtube/channel/about Get a YouTube channel's contact email and country. Try the 1-credit youtube/channel first: it already carries the email for some channels, and you are charged here only when an address is returned. A first read of an address behind the View email address button can take up to 5 minutes, so set your HTTP timeout to at least 300s; on a 503, retry after the Retry-After seconds (oneOf: channelId|handle|url) Credit cost: 25 (override; tier premium) Parameters: - channelId (optional, string): YouTube channel ID. Can pass a channelId, handle or url - handle (optional, string): YouTube channel handle without the @ symbol - url (optional, string): YouTube channel URL. Can pass a channelId, handle or url - contact_email (optional, enum: 1): Optional. When 1, also returns author.ext.contact_email: when the bio lists two or more email addresses, the one the creator gives for contacting them personally, copied verbatim from the bio. Null when there is one address or none, when every address belongs to a manager, agency or brand, or when the choice was not confident. author.ext.public_email is unchanged. No extra credits. - Constraint: one of channelId, handle, url (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/youtube/channel/about" \ --data-urlencode "handle=mkbhd" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /youtube/channel/videos List YouTube channel videos (oneOf: channelId|handle) Credit cost: 1-2 (metered): 1 credit for the page. include=channel adds 1 credit for the single channel lookup the page shares, refunded if the channel could not be read and free when it was looked up recently. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - since (optional, string): Only videos published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Older videos are left off the page, and the page that reaches one ends the walk: `next_cursor` is not returned and `pagination.stopped_at` is `since`. Pinned videos sit out of date order and never end the walk. The page still costs what a page costs, so a daily poll pays for the pages it walks and no more. - stop_at_id (optional, string): The id (`post.id`) or URL (`post.url`) of the newest video you already hold. The page stops just before it: that video and everything after it are left off, `next_cursor` is not returned, and `pagination.stopped_at` is `known_id`. A pinned video never counts as the stop point. If it is not on this page the page is returned in full with its cursor, so keep walking. `pagination.stopped_at` is `end` when the list ran out first and `null` while there is more to walk. - channelId (optional, string): YouTube channel ID - handle (optional, string): YouTube channel handle without the @ symbol - sort (optional, enum: latest | popular): Sort by latest or popular - continuationToken (optional, string): Continuation token to get more videos. Get 'continuationToken' from previous response. - includeExtras (optional, string): Set to `true` to add the like count and comment count (`post.engagement.likes` / `.comments`) and the video description (`post.ext.description`). For the full per-video detail use /v1/youtube/video. Slows the response slightly. - is_paid_promotions (optional, string): Set to 'true' to search YouTube's public paid product placement / sponsorship / endorsement surface: returns normal videos where the creator disclosed a paid promotion. - include (optional, enum: channel): Set to `channel` (one token only) to add the channel's subscriber count to every row (`post.ext.author_followers`, YouTube's own figure, rounded to three significant figures above 1,000) plus `post.author.display_name`, `.avatar_url` and `post.ext.channel_id` where missing, and the country the channel's owner set as an ISO 3166-1 alpha-2 code on `post.ext.author_country` (null when the channel sets none). Every row is the same channel, so this is one channel lookup for the whole page: 1 credit, refunded if the channel could not be read, and free when the channel was looked up recently. Adds well under a second (never more than 8). Read `data.hydration` for what it did. - exact_dates (optional, enum: true | false): Exact publish dates are on by default and free. A row whose date the upstream derived from a relative label such as "4 months ago" (measured up to four months off) is joined to the per-video lookup for its exact publish second, at no extra credit: one lookup per page (two on a page past 50 rows, side by side), adding about 0.2 to 0.7 seconds (never more than 3). The same lookup swaps the listing title, often YouTube's English machine translation, for the creator's own, and `computed.language` follows it; a title not swapped carries `post.ext.title_maybe_translated: true`. Set to `false` to skip the lookup: rows then carry the supplier's date estimate (a day-granular or coarser label truncated to midnight UTC), marked by `post.ext.published_precision` and `post.ext.published_label`, and the listing title, marked by `post.ext.title_maybe_translated`. A row the lookup could not resolve keeps those values and markers, and `_warnings` carries `exact_dates_partial` (some rows) or `exact_dates_unavailable` (none); on `channel/videos`, whose dates are already exact, the warnings are `original_titles_partial` and `original_titles_unavailable`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. - Constraint: one of channelId, handle (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/youtube/channel/videos" \ --data-urlencode "handle=MrBeast" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /youtube/video Get YouTube video details Credit cost: 1 (standard) Parameters: - url (required): Full URL of the YouTube video - language (optional, string): Preferred response language (mapped to Accept-Language header; not guaranteed due to YouTube localization behavior). 2 letter language code, ie 'en', 'es', 'fr' etc. - hl (optional, string): Preferred response language for localized text (ISO 639-1, e.g. 'en', 'es', 'fr'). ``` curl -G "https://www.socialcrawl.dev/v1/youtube/video" \ --data-urlencode "url=https://www.youtube.com/watch?v=dQw4w9WgXcQ" \ -H "x-api-key: YOUR_API_KEY" ``` ### POST /youtube/videos Batch get YouTube video details (up to 1000) Credit cost: 5-100 (metered): 5 credits per 50-id chunk, so a 50-id batch is 5 credits and the 1,000-id maximum is 100. Batching is what makes this cheap: the same 1,000 videos fetched one at a time through GET /v1/youtube/video cost 1,000 credits. A chunk is charged whether or not every id in it resolves, so there is no per-id refund; a call that resolves nothing at all is refunded in full Parameters: - ids (required): JSON array of BARE YouTube video ids (11 characters each, e.g. 'dQw4w9WgXcQ'), 1 to 1000 per request. Unlike GET /v1/youtube/video, full watch URLs are not accepted here. - hl (optional, string): Preferred response language for localized text (ISO 639-1, e.g. 'en', 'ko'). - include_localizations (optional, boolean): When true, adds post.ext.localizations to each item: a map from language code to the localized { title, description }. Present only on videos whose owner localized them. ``` curl -X POST "https://www.socialcrawl.dev/v1/youtube/videos" \ -H "x-api-key: YOUR_API_KEY" \ -H "content-type: application/json" \ -d '{"ids":["dQw4w9WgXcQ","9bZkp7q19f0"]}' ``` ### POST /youtube/channels Batch get YouTube channel details (up to 1000) Credit cost: 5-100 (metered): 5 credits per 50-id chunk, so a 50-id batch is 5 credits and the 1,000-id maximum is 100. Batching is what makes this cheap: the same 1,000 channels fetched one at a time through GET /v1/youtube/channel cost 1,000 credits. A chunk is charged whether or not every id in it resolves, so there is no per-id refund; a call that resolves nothing at all is refunded in full Parameters: - ids (required): JSON array of BARE YouTube channel ids (24 characters, starting with 'UC', e.g. 'UC_x5XG1OV2P6uZZ5FSM9Ttw'), 1 to 1000 per request. Unlike GET /v1/youtube/channel, channel URLs/handles are not accepted here. - hl (optional, string): Preferred response language for localized text (ISO 639-1, e.g. 'en', 'ko'). - include_localizations (optional, boolean): When true, adds author.ext.localizations to each item: a map from language code to the localized { title, description }. Present only on channels whose owner localized them. ``` curl -X POST "https://www.socialcrawl.dev/v1/youtube/channels" \ -H "x-api-key: YOUR_API_KEY" \ -H "content-type: application/json" \ -d '{"ids":["UC_x5XG1OV2P6uZZ5FSM9Ttw","UCX6OQ3DkcsbYNE6H8uQQuVA"]}' ``` ### POST /youtube/transcripts Up to 100 YouTube video ids → one transcript per row, failed ids refunded. Credit cost: 3-300 (metered): 3 credits per successful transcript. Up to 100 ids per call: the call holds ids × 3 up front (a full batch holds 300) and refunds every not_found, errored and deferred row Parameters: - ids (required): JSON array of 1-100 BARE YouTube video ids (11 characters each, e.g. 'dQw4w9WgXcQ'). Full watch URLs are not accepted here. - language (optional, string): Caption-track language (2-letter code, e.g. 'en', 'ko'). It is never swapped for another language: an id with no track in it is tried on the next source and otherwise comes back `not_found`, refunded. When absent, each row is in the video's original spoken language, not a dubbed track. - format (optional, string): `text` (default) returns the whole transcript as one joined string; `segments` returns timestamped `[{start_ms, duration_ms, text}]`. ``` curl -X POST "https://www.socialcrawl.dev/v1/youtube/transcripts" \ -H "x-api-key: YOUR_API_KEY" \ -H "content-type: application/json" \ -d '{"ids":["dQw4w9WgXcQ","9bZkp7q19f0"]}' ``` ### GET /youtube/video/sponsors Detect sponsors of a YouTube video Credit cost: 10 (premium) Parameters: - url (required): Full URL of the YouTube video or short - language (optional, string): 2 letter language code used for transcript lookup, ie 'en', 'es', 'fr' etc. ``` curl -G "https://www.socialcrawl.dev/v1/youtube/video/sponsors" \ --data-urlencode "url=https://www.youtube.com/watch?v=AVO0ifle-OU" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /youtube/video/comments List YouTube video comments Credit cost: 1-5 (metered): 1 credit per page, with the default labels (sentiment, question, purchase_intent, complaint) included free. label=spam, toxic or low_quality holds 4 extra credits and refunds down to 1 credit per started 25 comments that were newly judged: comments already labelled are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - url (required): Full URL of the YouTube video to fetch comments for - continuationToken (optional, string): Continuation token to get more comments. Get 'continuationToken' from previous response. - order (optional, enum: top | newest): Order of comments: 'top' (default, YouTube's relevance ranking) or 'newest' (exact newest-first on `comment.published_at`; each next page continues strictly older with no overlap: the lane for date-window and recency pulls). - searchTerm (optional, string): Filter comments to those containing this search term. - format (optional, enum: html | plainText): Text format for comment bodies: 'html' (default) or 'plainText'. - max_results (optional, integer): Comments per page, 1-100. The default page is already 100, so set this only to shrink a page; out-of-range values are rejected with a free 400 rather than degrading the response. - channel_id (optional, string): YouTube channel id to fetch channel-level community comments for (instead of a video's comments). - label (optional, string): Optional CSV of SocialCrawl labels to add to every comment: sentiment, spam, question, purchase_intent, complaint, toxic, low_quality, injection. Without this param every page already carries sentiment, question, purchase_intent and complaint, free; label= adds the labels you name to them (the defaults keep running). Each comment gains computed.labels with probabilities (0 to 1), so you choose the cut-off; a comment that could not be judged carries labels: null. sentiment, question, purchase_intent and complaint are free when asked for too; spam, toxic, low_quality and injection add 1 credit per started 25 newly judged comments (comments already labelled for anyone are free, and so is a cached page). injection flags text that addresses an AI system and tries to direct it (flagged, p); it never drops or rewrites a row. judgments=off (or label=none) turns the default labels off. data.labels reports what was judged and billed. - exclude (optional, string): Optional CSV (spam, low_quality), only with the same preset in label. Drops comments whose probability is 0.8 or higher and lists their ids in data.labels.dropped_ids. A comment that could not be judged is never dropped. - judgments (optional, enum: on | off): Optional, on (the default) or off. By default every row gains free SocialCrawl judgments (computed.labels, and computed.relevance on search endpoints), reported in data.labels (mode default) and data.relevance (origin default), each with a status (complete, partial or skipped) and pending: the rows still being judged when the page was sent, which carry null now and are filled on your next call or cached read. Default judgments never add credits, never change an existing field, and never drop or reorder a row. off returns the page exactly as before, with none of those keys. label=none does the same. - dry_run (optional, enum: 1): Optional. When 1, return a cost preview for this labelled or relevance-filtered request without fetching the page or judging any row. data.estimate reports rows_expected, rows_cached, label_credits_min, label_credits_max and base_credits. 0 credits charged. - label_evidence (optional, enum: 1): Optional, only with label=. When 1, every labelled row also carries computed.labels_evidence. = { quote, sentence_index }: the sentence in the row that most clearly shows the label, copied verbatim. Absent or null when no single sentence shows it. - fit (optional, enum: goal): Optional. When goal, keep the rows and fields needed for the goal you pass in goal= (plus any that are uncertain, and the first and last), and replace the rest with a stub. data.held_back lists the held ids and a recall id that re-reads the full page from cache at no extra charge. Without this param the page is unchanged. - goal (optional, string): Required by fit=goal. What you are trying to do, in your own words, up to 300 characters. - fit_tokens (optional, integer): Optional, only with fit=goal. Soft cap on how much of the page to keep, in tokens. Uncertain blocks and the first and last block are kept even if they exceed it. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off the page, and the page that reaches one ends the walk: `next_cursor` is not returned and `pagination.stopped_at` is `since`. Sending it sets the newest-first sort shown in this endpoint's sort parameter (any other sort value is a free 400). The page still costs what a page costs, so you pay for the pages you walk and no more. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/youtube/video/comments" \ --data-urlencode "url=https://www.youtube.com/watch?v=dQw4w9WgXcQ" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /youtube/video/comment/replies List YouTube comment replies Credit cost: 1-5 (metered): 1 credit per page, with the default labels (sentiment, question, purchase_intent, complaint) included free. label=spam, toxic or low_quality holds 4 extra credits and refunds down to 1 credit per started 25 comments that were newly judged: comments already labelled are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - continuationToken (required): Where to start. Three values work: a top-level comment id (e.g. the `comment.id` of any row from /v1/youtube/video/comments), that row's `comment.ext.replies_token`, or the `pagination.next_cursor` from a previous replies response. A token that is none of these is rejected. - format (optional, enum: html | plainText): Text format for reply bodies: 'html' (default) or 'plainText'. - label (optional, string): Optional CSV of SocialCrawl labels to add to every comment: sentiment, spam, question, purchase_intent, complaint, toxic, low_quality, injection. Without this param every page already carries sentiment, question, purchase_intent and complaint, free; label= adds the labels you name to them (the defaults keep running). Each comment gains computed.labels with probabilities (0 to 1), so you choose the cut-off; a comment that could not be judged carries labels: null. sentiment, question, purchase_intent and complaint are free when asked for too; spam, toxic, low_quality and injection add 1 credit per started 25 newly judged comments (comments already labelled for anyone are free, and so is a cached page). injection flags text that addresses an AI system and tries to direct it (flagged, p); it never drops or rewrites a row. judgments=off (or label=none) turns the default labels off. data.labels reports what was judged and billed. - exclude (optional, string): Optional CSV (spam, low_quality), only with the same preset in label. Drops comments whose probability is 0.8 or higher and lists their ids in data.labels.dropped_ids. A comment that could not be judged is never dropped. - judgments (optional, enum: on | off): Optional, on (the default) or off. By default every row gains free SocialCrawl judgments (computed.labels, and computed.relevance on search endpoints), reported in data.labels (mode default) and data.relevance (origin default), each with a status (complete, partial or skipped) and pending: the rows still being judged when the page was sent, which carry null now and are filled on your next call or cached read. Default judgments never add credits, never change an existing field, and never drop or reorder a row. off returns the page exactly as before, with none of those keys. label=none does the same. - dry_run (optional, enum: 1): Optional. When 1, return a cost preview for this labelled or relevance-filtered request without fetching the page or judging any row. data.estimate reports rows_expected, rows_cached, label_credits_min, label_credits_max and base_credits. 0 credits charged. - label_evidence (optional, enum: 1): Optional, only with label=. When 1, every labelled row also carries computed.labels_evidence. = { quote, sentence_index }: the sentence in the row that most clearly shows the label, copied verbatim. Absent or null when no single sentence shows it. - fit (optional, enum: goal): Optional. When goal, keep the rows and fields needed for the goal you pass in goal= (plus any that are uncertain, and the first and last), and replace the rest with a stub. data.held_back lists the held ids and a recall id that re-reads the full page from cache at no extra charge. Without this param the page is unchanged. - goal (optional, string): Required by fit=goal. What you are trying to do, in your own words, up to 300 characters. - fit_tokens (optional, integer): Optional, only with fit=goal. Soft cap on how much of the page to keep, in tokens. Uncertain blocks and the first and last block are kept even if they exceed it. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/youtube/video/comment/replies" \ --data-urlencode "continuationToken=Ugzge340dBgB75hWBm54AaABAg" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /youtube/search Search YouTube Credit cost: 1-19 (metered): 1 credit for the page. include=engagement and include=channel each add 1 credit per distinct video or channel filled, at most 5 per join on a page of up to 50 rows, so a page with both is at most 11 credits; ids that could not be filled are refunded, and a repeat of the same call within the cache window costs 0. Relevance against your query (by default, or with relevance=) is free. Adding relevant_to= holds 4 extra credits and refunds down to 1 credit per started 25 rows that were newly judged: rows already judged for the same topic are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. The default labels (sponsored, intent, niche) are free. label=mention, quality, ai_style, ai_written, injection, or label=intent with offer=, holds 4 extra credits and refunds down to 1 credit per started 25 posts that were newly judged: posts already labelled are free, a page where nothing could be judged refunds the whole extra, and a cached page is free. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. With `seen`, a page's price falls with its share of rows you already received under that id; credits for joined data (`include=`) are never discounted. Parameters: - query (required): Search keyword or phrase. - uploadDate (optional, enum: today | this_week | this_month | this_year): Upload date filter. Reliable on its own (no `type`). With `type=shorts`, windows narrower than `this_year` are rejected with a 400 because YouTube returns an empty set for them; `this_year` is accepted but measured to return ~5x fewer unique Shorts across a paginated walk (the upstream re-serves the same items under an advancing `continuationToken`), so the recommended pattern for Shorts recency is no `uploadDate` at all plus `includeExtras=true` and a client-side filter on `post.published_at`. With other `type` values the upstream currently ignores the date filter and may mix content kinds; filter client-side there too. For an EXACT date window rather than these coarse buckets, use `/v1/youtube/search/advanced`, whose `published_after`/`published_before` take RFC-3339 bounds and are genuinely applied. - sortBy (optional, enum: relevance | popular): Sort order: relevance or popular. `popular` cannot be combined with `type=shorts` (rejected with a 400. YouTube serves that combination unreliably); it also caps the page at ~20 results. It is also NOT a strict view-count sort: `popular` is a popularity-weighted ranking, and results do not come back in descending view order. Measured on 4 of 4 test keywords: a `new york` search returned 9.5M, then 502.9M, then 66K views in that order. If you need a view-faithful ordering, use `/v1/youtube/search/advanced` with `order=viewCount` and `includeExtras=true`: its ranking leads with the true top results, and the returned `engagement.views` lets you sort the page exactly client-side. - type (optional, enum: videos | shorts | channels | playlists): Type of content to return. `type=channels` returns one page of about 20 channels, with `has_more: false` and no `next_cursor`, because the provider cannot continue a channel search (measured 25/10/2026); videos, shorts and playlists page normally. NOTE on Shorts results (`type=shorts`, and the Shorts an untyped search mixes in): the Shorts shelf item carries NO channel object upstream (0 of 98 items across 3 keywords on 10/08/2026, 25 of 25 without one on 26/09/2026, raw upstream; `includeExtras=true` does not add it). By default the free per-video lookup that supplies the exact date also fills `post.ext.channel_id` and `post.author.display_name` on those rows, at no extra credit. `post.author.username` (the @handle) and `post.author.avatar_url` stay null on them: send `include=engagement,channel` to fill both and the subscriber count. With `exact_dates=false` no lookup runs and the whole author stays null on Shorts. Video results are unaffected (20/20 carry the full author). `/v1/youtube/search/advanced` also carries `channel_id` on every result at 1 credit, though it has no Shorts filter. - duration (optional, enum: under_3_min | between_3_and_20_min | over_20_min): Video duration filter. KNOWN UPSTREAM ISSUE: currently not applied by the provider and it can degrade the `type` filter; prefer filtering client-side on `post.content.duration_seconds`. - region (optional, string): 2-letter country code of the country to put the proxy in. - continuationToken (optional, string): Continuation token to get more results. Get `continuationToken` from a previous response. - includeExtras (optional, string): Set to `true` to add the like count and comment count (`post.engagement.likes` / `.comments`) and the video description (`post.ext.description`) to each video result. Left off, those three are null. Dates do not need it: `post.published_at` is exact by default either way (see `exact_dates`). For Shorts results it additionally supplies `post.content.duration_seconds`, which those items never carry otherwise. Costs latency and page depth: measured 2387ms and 20 rows without, 9763ms and 16 rows with, on the same query. It does NOT add channel identity to Shorts results (see `type`) and it never adds a subscriber count. For both, send `include=engagement,channel` instead: the video join supplies every row's channel id, and the channel join adds the subscriber count, in this same call. For full per-video details use `/v1/youtube/video`. - include (optional, string): Set to `engagement`, `channel`, or both (`engagement,channel`) to fill more of every row in this one call. `engagement` fills `post.engagement.views`, `.likes` and `.comments`, `post.content.duration_seconds`, `post.ext.channel_id` and `post.author.display_name` where the row lacks them, and replaces a date derived from a label such as "4 months ago" with the exact publish time (removing `post.ext.published_precision`). `channel` puts the channel's subscriber count on `post.ext.author_followers` (YouTube's own figure, which it rounds to three significant figures above 1,000) and fills the channel's `post.author.display_name`, `.avatar_url` and `.username` where missing. It also adds the country the channel's owner set, as an ISO 3166-1 alpha-2 code on `post.ext.author_country` (null when the channel sets none). Send both to cover rows that arrive without a channel id: the video join supplies it and the channel join then uses it. Each join costs 1 credit per distinct video or channel it filled, and never more than 5 per 50; ids that could not be filled are refunded. Channel and playlist result rows are not videos: the video join skips them. Each join adds 0.2 to 0.7 seconds on a fresh page (one lookup per 50 ids, never more than 8 seconds). Read `data.hydration` for the rows, lookups, credits held and kept, and the time. - exact_dates (optional, enum: true | false): Exact publish dates are on by default and free. A row whose date the upstream derived from a relative label such as "4 months ago" (measured up to four months off) is joined to the per-video lookup for its exact publish second, at no extra credit: one lookup per page (two on a page past 50 rows, side by side), adding about 0.2 to 0.7 seconds (never more than 3). The same lookup swaps the listing title, often YouTube's English machine translation, for the creator's own, and `computed.language` follows it; a title not swapped carries `post.ext.title_maybe_translated: true`. Set to `false` to skip the lookup: rows then carry the supplier's date estimate (a day-granular or coarser label truncated to midnight UTC), marked by `post.ext.published_precision` and `post.ext.published_label`, and the listing title, marked by `post.ext.title_maybe_translated`. A row the lookup could not resolve keeps those values and markers, and `_warnings` carries `exact_dates_partial` (some rows) or `exact_dates_unavailable` (none); on `channel/videos`, whose dates are already exact, the warnings are `original_titles_partial` and `original_titles_unavailable`. - relevance (optional, enum: score | filter): Optional. Without this param every row already carries computed.relevance against your query, free ({ p, sense, depth, spam }: is this row about what your query means, or a different thing that shares its words?), and nothing is dropped or reordered. score asks for it explicitly and waits for every row; filter also drops the rows that are not about your query, and lists their ids in data.relevance.dropped_ids. Your query is the topic; nothing to configure. A row that could not be judged is never dropped and carries relevance: null. Pagination is unchanged, so a filtered page can hold fewer rows. Free with your query as the topic; with relevant_to it adds 1 credit per started 25 newly judged rows (rows already judged for the same topic are free, and so is a cached page). - relevance_threshold (optional, string): Optional, only with relevance. The probability (0 to 1) a row must reach to be kept by relevance=filter. Default 0.5. Lower keeps more rows, higher keeps fewer. - relevant_to (optional, string): Optional, only with relevance. Up to 200 characters describing what you mean, used as the topic instead of the query. Use it when the query is ambiguous, for example query=cleopatra with relevant_to=Cleopatra, the IGT slot game. - judgments (optional, enum: on | off): Optional, on (the default) or off. By default every row gains free SocialCrawl judgments (computed.labels, and computed.relevance on search endpoints), reported in data.labels (mode default) and data.relevance (origin default), each with a status (complete, partial or skipped) and pending: the rows still being judged when the page was sent, which carry null now and are filled on your next call or cached read. Default judgments never add credits, never change an existing field, and never drop or reorder a row. off returns the page exactly as before, with none of those keys. label=none does the same. - dry_run (optional, enum: 1): Optional. When 1, return a cost preview for this labelled or relevance-filtered request without fetching the page or judging any row. data.estimate reports rows_expected, rows_cached, label_credits_min, label_credits_max and base_credits. 0 credits charged. - fit (optional, enum: goal): Optional. When goal, keep the rows and fields needed for the goal you pass in goal= (plus any that are uncertain, and the first and last), and replace the rest with a stub. data.held_back lists the held ids and a recall id that re-reads the full page from cache at no extra charge. Without this param the page is unchanged. - goal (optional, string): Required by fit=goal. What you are trying to do, in your own words, up to 300 characters. - fit_tokens (optional, integer): Optional, only with fit=goal. Soft cap on how much of the page to keep, in tokens. Uncertain blocks and the first and last block are kept even if they exceed it. - label (optional, string): Optional CSV of SocialCrawl labels to add to every post. Without this param every page already carries sponsored, intent and niche, free; label= adds the labels you name to them (the defaults keep running). sponsored, intent and niche are free when asked for too; mention, quality, ai_style, intent with offer=, and injection add 1 credit per started 25 newly judged posts. judgments=off (or label=none) turns the default labels off. sponsored: is the post a paid or gifted promotion (p, 0 to 1), did it carry a disclosure marker such as #ad or 광고 (disclosed), is it likely paid with no marker (undisclosed), and which of the accounts it mentions does it promote (brand, or null). These are signals to review, never a finding. mention (needs brand=): is the post about that brand rather than something that shares its name (about_brand, 0 to 1), how it feels about the brand on five levels (sentiment_level 0 to 4 and sentiment_score 0 to 1, null when the post is not about the brand), is it sarcastic, which aspect it talks about (taste_or_quality, price_or_value, availability_or_delivery, health_or_safety, advertising_or_campaign, customer_service, none), and did the author buy or use it (first_hand). intent: what the author is mainly doing (label: asking_for_recommendation, comparing_options, switching_away, complaining, promoting, news_or_discussion, other, or null when unsure, with confidence), whether they read as a potential buyer rather than a seller (buyer, seller), how pressing the need is (urgency, 0 to 3), and, when you pass offer=, whether your offer would plausibly help them (fits_offer). niche: which of the 33 niches of the published taxonomy sc-niche-v1 the caption belongs to, or personal_no_niche, or other (label, confidence, taxonomy), with label null when the caption is too thin to tell or the pick is unsure. It reads the caption only, not the video. quality: how much checkable detail the caption carries (fact_density 0 to 3), whether it mainly asks for likes, replies, shares, follows or tags (engagement_bait, 0 to 1), whether it is written to provoke anger as a way to get engagement (rage_bait, 0 to 1; about the writing, never the side taken), whether it only repeats someone else's news or view (secondhand, 0 to 1), and what the post is mainly doing (post_aim: inform, opinion, sell, entertain, provoke, other). exclude=engagement_bait drops posts whose engagement_bait is 0.8 or higher. ai_style: writing patterns common in AI-generated text, judged on the text alone. It does not say who wrote the post, and a person who writes in this style is flagged too. Six patterns are judged, each a probability (0 to 1): binary_contrast ("This is not X. It's Y."), faux_insight ("Here's what nobody tells you" and throat-clearing openers), colon_reveal ("The best part: it learns."), profound_kicker (ends on an aphorism or mic-drop line), puffery ("marks a pivotal moment", "highlighting the importance of") and rhetorical_setup ("What if I told you", a question the post answers itself, "Not a X. Not a Y. A Z."). Four are counted in the text: slop_words (words and phrases such as delve, leverage, robust, game changer, at the end of the day), em_dashes, emoji_bullets (lines that start with an emoji or a symbol bullet) and one_line_paragraphs (paragraphs that are one short line). pattern_count is how many patterns are present (a judged pattern at 0.8 or higher, slop_words 1 or more, em_dashes 2 or more, emoji_bullets 2 or more, one_line_paragraphs 5 or more) and markers lists their names, comma-separated. A post with less than about eight words of text is only counted, with every judged pattern 0. The judged patterns read the first 400 and the last 150 characters of a long post, and the counts read the whole post. exclude=ai_style drops posts with 2 or more patterns. ai_written: how likely the post was written by an AI model, from SocialCrawl's own text classifier, reading the opening of the post (about 1,000 characters): p (0 to 1, calibrated), level (likely (p 0.95 or higher), possible (p 0.71 or higher) or unlikely) and model (the model name; a new name means a new scale). A post under 8 words is too short to read: p null, level too_short. It is a probability, not proof: a person can be flagged and an AI-written post can pass, and the shorter the post the less certain it is. exclude=ai_written drops likely posts. injection flags text that addresses an AI system and tries to direct it (flagged, p); it never drops or rewrites a row. A post that could not be judged carries labels: null. Posts already labelled for anyone are free, and so is a cached page. data.labels reports what was judged and billed. - exclude (optional, string): Optional CSV (engagement_bait, ai_style, ai_written). engagement_bait, only with label=quality, drops posts whose engagement_bait probability is 0.8 or higher. ai_style, only with label=ai_style, drops posts that show 2 or more of its writing patterns (pattern_count 2 or higher). ai_written, only with label=ai_written, drops posts whose level is likely (p 0.95 or higher), or whose p reaches your ai_written_threshold; a post too short to read is never dropped. Dropped posts are listed in data.labels.dropped_ids. A post that could not be judged is never dropped. - ai_written_threshold (optional, string): Optional, only with label=ai_written and exclude=ai_written. The p (0 to 1) at which exclude=ai_written drops a post. Default 0.95 (level likely). 0.71 also drops possible posts: more AI posts go, and more posts by people with them. - brand (optional, string): Required by label=mention, ignored otherwise. The brand or product the posts are judged against, up to 300 characters, for example brand=Buldak. Without it label=mention is skipped with the warning label_mention_needs_brand and is not billed. - brand_description (optional, string): Optional, with label=mention. One plain sentence saying what the brand is, for example brand_description=Samyang's spicy instant noodle brand. Use it when the name is also an ordinary word or another company's name. - offer (optional, string): Optional, with label=intent. One or two plain sentences saying what you sell, up to 300 characters, for example offer=A web design agency that builds websites for small businesses. Adds fits_offer to every post; without it fits_offer is null. - label_evidence (optional, enum: 1): Optional, only with label=. When 1, every labelled row also carries computed.labels_evidence. = { quote, sentence_index }: the sentence in the row that most clearly shows the label, copied verbatim. Absent or null when no single sentence shows it. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - min_views (optional, integer): Keep only rows with at least this many views (`post.engagement.views`). A row whose view count is unknown is discarded, so every returned row meets the floor. Filtering runs on our side after each page is fetched, so every page walked is billed as usual; `data.walk.discarded` counts what was removed and why. Each page is its own entry in `GET /v1/credits/transactions`, under the page's request id listed in `data.walk.request_ids`. - max_age_days (optional, integer): Keep only rows published within this many days (`post.published_at`), 1 to 3650. A row with no date is discarded. Filtering runs on our side after each page is fetched, so every page walked is billed as usual; `data.walk.discarded` counts what was removed and why. Each page is its own entry in `GET /v1/credits/transactions`, under the page's request id listed in `data.walk.request_ids`. - sort_rows (optional, enum: views): `views`: return the kept rows ordered by view count, highest first, across every page walked. Rows without a view count go last. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them (after any filter). Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows (after any filter), then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. - seen (optional, string): An id you choose (1 to 64 letters, digits, `.`, `_` or `-`). Rows your account already received under the same `seen` id are removed from the page, and the page price falls with the share of repeats: page credits x new rows / rows on the page, rounded up, so a page of nothing but repeats is free. Only the page's own price is discounted: credits for joined data (`include=`) are charged only for rows you have not received, and are never discounted. The memory lasts 24 hours from the last call that used the id, holds row ids only, and is private to your account. Use one id across a set of related searches; `data.walk.repeats` counts what was removed. ``` curl -G "https://www.socialcrawl.dev/v1/youtube/search" \ --data-urlencode "query=javascript tutorial" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /youtube/channel/shorts List YouTube channel shorts (oneOf: channelId|handle) Credit cost: 1-2 (metered): 1 credit for the page. include=channel adds 1 credit for the single channel lookup the page shares, refunded if the channel could not be read and free when it was looked up recently. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - since (optional, string): Only shorts published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Older shorts are left off the page, and the page that reaches one ends the walk: `next_cursor` is not returned and `pagination.stopped_at` is `since`. Pinned shorts sit out of date order and never end the walk. The page still costs what a page costs, so a daily poll pays for the pages it walks and no more. - stop_at_id (optional, string): The id (`post.id`) or URL (`post.url`) of the newest short you already hold. The page stops just before it: that short and everything after it are left off, `next_cursor` is not returned, and `pagination.stopped_at` is `known_id`. A pinned short never counts as the stop point. If it is not on this page the page is returned in full with its cursor, so keep walking. `pagination.stopped_at` is `end` when the list ran out first and `null` while there is more to walk. - handle (optional, string): YouTube channel handle without the @ symbol - channelId (optional, string): Can pass channelId or handle - sort (optional, enum: newest | popular): Sort by newest (descending publish date) or popular (descending view count). Both orderings are genuinely applied, unlike `/v1/youtube/search`'s `popular`, which is a popularity-weighted ranking rather than a view sort. - continuationToken (optional, string): Continuation token to get more videos. Get 'continuationToken' from previous response. - include (optional, enum: channel): Set to `channel` (one token only) to add the channel's subscriber count to every row (`post.ext.author_followers`, YouTube's own figure, rounded to three significant figures above 1,000) plus `post.author.display_name`, `.avatar_url` and `post.ext.channel_id` where missing, and the country the channel's owner set as an ISO 3166-1 alpha-2 code on `post.ext.author_country` (null when the channel sets none). Every row is the same channel, so this is one channel lookup for the whole page: 1 credit, refunded if the channel could not be read, and free when the channel was looked up recently. Adds well under a second (never more than 8). Read `data.hydration` for what it did. - exact_dates (optional, enum: true | false): On by default and free: every row is joined to the per-video lookup, one lookup per page (two on a page past 50 rows, side by side), adding about 0.2 to 0.7 seconds (never more than 3). The same lookup swaps the listing title, often YouTube's English machine translation, for the creator's own, and `computed.language` follows it; a title not swapped carries `post.ext.title_maybe_translated: true`. Set to `false` to skip the lookup and keep the listing title, marked by `post.ext.title_maybe_translated`. A row the lookup could not resolve keeps the listing title and the marker, and `_warnings` carries `original_titles_partial` (some rows) or `original_titles_unavailable` (none). The dates on this endpoint are exact either way. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. - Constraint: one of channelId, handle (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/youtube/channel/shorts" \ --data-urlencode "handle=MrBeast" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /youtube/community-post Get YouTube community post Credit cost: 1 (standard) Parameters: - url (required): Full URL of the YouTube community post ``` curl -G "https://www.socialcrawl.dev/v1/youtube/community-post" \ --data-urlencode "url=https://www.youtube.com/post/Ugkxvj2KoApYAXoqLWnKVr6zZe5JjeHrQeP8" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /youtube/playlist Get YouTube playlist Credit cost: 1-11 (metered): 1 credit for the page. include=engagement and include=channel each add 1 credit per distinct video or channel filled, at most 5 per join on a page of up to 50 rows, so a page with both is at most 11 credits; ids that could not be filled are refunded, and a repeat of the same call within the cache window costs 0. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - playlist_id (required): YouTube playlist ID - cursor (optional, string): Pagination cursor from a previous response: fetches the next page. - channel_id (optional, string): YouTube channel id: pages that channel's uploads instead of a playlist. - include (optional, string): Set to `engagement`, `channel`, or both (`engagement,channel`) to fill more of every row in this one call. `engagement` fills `post.engagement.views`, `.likes` and `.comments`, `post.content.duration_seconds`, `post.ext.channel_id` and `post.author.display_name` where the row lacks them, and replaces a date derived from a label such as "4 months ago" with the exact publish time (removing `post.ext.published_precision`). `channel` puts the channel's subscriber count on `post.ext.author_followers` (YouTube's own figure, which it rounds to three significant figures above 1,000) and fills the channel's `post.author.display_name`, `.avatar_url` and `.username` where missing. It also adds the country the channel's owner set, as an ISO 3166-1 alpha-2 code on `post.ext.author_country` (null when the channel sets none). Send both to cover rows that arrive without a channel id: the video join supplies it and the channel join then uses it. Each join costs 1 credit per distinct video or channel it filled, and never more than 5 per 50; ids that could not be filled are refunded. Each join adds 0.2 to 0.7 seconds on a fresh page (one lookup per 50 ids, never more than 8 seconds). Read `data.hydration` for the rows, lookups, credits held and kept, and the time. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/youtube/playlist" \ --data-urlencode "playlist_id=PLrAXtmErZgOeiKm4sgNOknGvNjby9efdf" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /youtube/search/hashtag Search YouTube by hashtag Credit cost: 1-11 (metered): 1 credit for the page. include=engagement and include=channel each add 1 credit per distinct video or channel filled, at most 5 per join on a page of up to 50 rows, so a page with both is at most 11 credits; ids that could not be filled are refunded, and a repeat of the same call within the cache window costs 0. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. With `seen`, a page's price falls with its share of rows you already received under that id; credits for joined data (`include=`) are never discounted. Parameters: - hashtag (required): Hashtag to search for without the # symbol - continuationToken (optional, string): Continuation token to get more videos. Get 'continuationToken' from previous response. - type (optional, enum: all | shorts): Search for all types of content or only shorts - include (optional, string): Set to `engagement`, `channel`, or both (`engagement,channel`) to fill more of every row in this one call. `engagement` fills `post.engagement.views`, `.likes` and `.comments`, `post.content.duration_seconds`, `post.ext.channel_id` and `post.author.display_name` where the row lacks them, and replaces a date derived from a label such as "4 months ago" with the exact publish time (removing `post.ext.published_precision`). `channel` puts the channel's subscriber count on `post.ext.author_followers` (YouTube's own figure, which it rounds to three significant figures above 1,000) and fills the channel's `post.author.display_name`, `.avatar_url` and `.username` where missing. It also adds the country the channel's owner set, as an ISO 3166-1 alpha-2 code on `post.ext.author_country` (null when the channel sets none). Send both to cover rows that arrive without a channel id: the video join supplies it and the channel join then uses it. Each join costs 1 credit per distinct video or channel it filled, and never more than 5 per 50; ids that could not be filled are refunded. Each join adds 0.2 to 0.7 seconds on a fresh page (one lookup per 50 ids, never more than 8 seconds). Read `data.hydration` for the rows, lookups, credits held and kept, and the time. - exact_dates (optional, enum: true | false): Exact publish dates are on by default and free. A row whose date the upstream derived from a relative label such as "4 months ago" (measured up to four months off) is joined to the per-video lookup for its exact publish second, at no extra credit: one lookup per page (two on a page past 50 rows, side by side), adding about 0.2 to 0.7 seconds (never more than 3). The same lookup swaps the listing title, often YouTube's English machine translation, for the creator's own, and `computed.language` follows it; a title not swapped carries `post.ext.title_maybe_translated: true`. Set to `false` to skip the lookup: rows then carry the supplier's date estimate (a day-granular or coarser label truncated to midnight UTC), marked by `post.ext.published_precision` and `post.ext.published_label`, and the listing title, marked by `post.ext.title_maybe_translated`. A row the lookup could not resolve keeps those values and markers, and `_warnings` carries `exact_dates_partial` (some rows) or `exact_dates_unavailable` (none); on `channel/videos`, whose dates are already exact, the warnings are `original_titles_partial` and `original_titles_unavailable`. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - min_views (optional, integer): Keep only rows with at least this many views (`post.engagement.views`). A row whose view count is unknown is discarded, so every returned row meets the floor. Filtering runs on our side after each page is fetched, so every page walked is billed as usual; `data.walk.discarded` counts what was removed and why. Each page is its own entry in `GET /v1/credits/transactions`, under the page's request id listed in `data.walk.request_ids`. - max_age_days (optional, integer): Keep only rows published within this many days (`post.published_at`), 1 to 3650. A row with no date is discarded. Filtering runs on our side after each page is fetched, so every page walked is billed as usual; `data.walk.discarded` counts what was removed and why. Each page is its own entry in `GET /v1/credits/transactions`, under the page's request id listed in `data.walk.request_ids`. - sort_rows (optional, enum: views): `views`: return the kept rows ordered by view count, highest first, across every page walked. Rows without a view count go last. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them (after any filter). Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows (after any filter), then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. - seen (optional, string): An id you choose (1 to 64 letters, digits, `.`, `_` or `-`). Rows your account already received under the same `seen` id are removed from the page, and the page price falls with the share of repeats: page credits x new rows / rows on the page, rounded up, so a page of nothing but repeats is free. Only the page's own price is discounted: credits for joined data (`include=`) are charged only for rows you have not received, and are never discounted. The memory lasts 24 hours from the last call that used the id, holds row ids only, and is private to your account. Use one id across a set of related searches; `data.walk.repeats` counts what was removed. ``` curl -G "https://www.socialcrawl.dev/v1/youtube/search/hashtag" \ --data-urlencode "hashtag=shorts" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /youtube/video/related Get videos related to a YouTube video Credit cost: 1-11 (metered): 1 credit for the page. include=engagement and include=channel each add 1 credit per distinct video or channel filled, at most 5 per join on a page of up to 50 rows, so a page with both is at most 11 credits; ids that could not be filled are refunded, and a repeat of the same call within the cache window costs 0. Parameters: - url (required): Full URL of the YouTube video or Short - language (optional, string): Preferred language for the listing (mapped to Accept-Language; not guaranteed due to YouTube localization behavior). 2 letter language code, ie 'en', 'es', 'fr' etc. - include (optional, string): Set to `engagement`, `channel`, or both (`engagement,channel`) to fill more of every row in this one call. `engagement` fills `post.engagement.views`, `.likes` and `.comments`, `post.content.duration_seconds`, `post.ext.channel_id` and `post.author.display_name` where the row lacks them, and replaces a date derived from a label such as "4 months ago" with the exact publish time (removing `post.ext.published_precision`). `channel` puts the channel's subscriber count on `post.ext.author_followers` (YouTube's own figure, which it rounds to three significant figures above 1,000) and fills the channel's `post.author.display_name`, `.avatar_url` and `.username` where missing. It also adds the country the channel's owner set, as an ISO 3166-1 alpha-2 code on `post.ext.author_country` (null when the channel sets none). Send both to cover rows that arrive without a channel id: the video join supplies it and the channel join then uses it. Each join costs 1 credit per distinct video or channel it filled, and never more than 5 per 50; ids that could not be filled are refunded. Each join adds 0.2 to 0.7 seconds on a fresh page (one lookup per 50 ids, never more than 8 seconds). Read `data.hydration` for the rows, lookups, credits held and kept, and the time. - exact_dates (optional, enum: true | false): Exact publish dates are on by default and free. A row whose date the upstream derived from a relative label such as "4 months ago" (measured up to four months off) is joined to the per-video lookup for its exact publish second, at no extra credit: one lookup per page (two on a page past 50 rows, side by side), adding about 0.2 to 0.7 seconds (never more than 3). The same lookup swaps the listing title, often YouTube's English machine translation, for the creator's own, and `computed.language` follows it; a title not swapped carries `post.ext.title_maybe_translated: true`. Set to `false` to skip the lookup: rows then carry the supplier's date estimate (a day-granular or coarser label truncated to midnight UTC), marked by `post.ext.published_precision` and `post.ext.published_label`, and the listing title, marked by `post.ext.title_maybe_translated`. A row the lookup could not resolve keeps those values and markers, and `_warnings` carries `exact_dates_partial` (some rows) or `exact_dates_unavailable` (none); on `channel/videos`, whose dates are already exact, the warnings are `original_titles_partial` and `original_titles_unavailable`. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. ``` curl -G "https://www.socialcrawl.dev/v1/youtube/video/related" \ --data-urlencode "url=https://www.youtube.com/watch?v=dQw4w9WgXcQ" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /youtube/shorts/trending Get trending YouTube shorts Credit cost: 5-15 (metered): 5 credits for the feed. include=channel adds 1 credit per distinct channel filled, at most 5 per 50 channels (the feed carries about 70, so at most 10); channels that could not be read are refunded, and a repeat of the same call within the cache window costs 0. Parameters: - include (optional, enum: channel): Set to `channel` (one token only) to add each row's channel stats in this one call. `channel` puts the channel's subscriber count on `post.ext.author_followers` (YouTube's own figure, which it rounds to three significant figures above 1,000) and fills the channel's `post.author.display_name`, `.avatar_url` and `.username` where missing. It also adds the country the channel's owner set, as an ISO 3166-1 alpha-2 code on `post.ext.author_country` (null when the channel sets none). Each join costs 1 credit per distinct video or channel it filled, and never more than 5 per 50; ids that could not be filled are refunded. Each join adds 0.2 to 0.7 seconds on a fresh page (one lookup per 50 ids, never more than 8 seconds). Read `data.hydration` for the rows, lookups, credits held and kept, and the time. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. ``` curl "https://www.socialcrawl.dev/v1/youtube/shorts/trending" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /youtube/video/transcript Get YouTube video transcript Credit cost: 3 (override; tier standard) Parameters: - url (required): Full URL of the YouTube video - language (optional, string): 2 letter language code, ie 'en', 'es', 'fr' etc. If no caption track matches the language you specify, the request returns 404 RESOURCE_NOT_FOUND (reason `no_captions`) at 0 credits. Omit it to get the video's original spoken language. - moments (optional, string): Optional CSV of moment kinds to find in the timestamped transcript: brand, sponsor_read, product_demo, call_to_action. Returns data.moments = [{ kind, start_ms, end_ms, quote }]. Quote is copied from the transcript. Times come from the segments, never from the judge. Price of the extra step is an owner decision; today it does not add credits. - brand (optional, string): Optional, with moments=. The company or product to look for, so a namesake is not counted. - q (optional, string): Optional question about the video, up to 300 characters, for example q=what discount code do they give. Returns data.moments = [{ kind: "answers_query", start_ms, end_ms, quote }] for each stretch of about six transcript lines that states the answer: a stretch that only shares the topic is left out, and a question the video never answers returns an empty list. The quote is one line copied from the transcript and the times come from the segments. Send moments= or q=, not both. If the step cannot run, the transcript is returned without data.moments and data._warnings carries moments_unavailable. Today it does not add credits. ``` curl -G "https://www.socialcrawl.dev/v1/youtube/video/transcript" \ --data-urlencode "url=https://www.youtube.com/watch?v=dQw4w9WgXcQ" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /youtube/channel/playlists List a YouTube channel's playlists (oneOf: channelId|handle) Credit cost: 1 (standard) Parameters: - channelId (optional, string): YouTube channel ID. - handle (optional, string): YouTube channel handle (with or without @). - continuationToken (optional, string): Continuation token from a previous response: fetches the next page. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. - Constraint: one of channelId, handle (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/youtube/channel/playlists" \ --data-urlencode "handle=MrBeast" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /youtube/channel/lives List a YouTube channel's live streams (oneOf: channelId|handle) Credit cost: 1-7 (metered): 1 credit for the page. include=engagement adds 1 credit per distinct stream filled, at most 5 per page; include=channel adds 1 credit for the single channel lookup the page shares. Anything that could not be filled is refunded, so a page with both is at most 7 credits, and a repeat of the same call within the cache window costs 0. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - channelId (optional, string): YouTube channel ID. - handle (optional, string): YouTube channel handle (with or without @). - continuationToken (optional, string): Continuation token from a previous response: fetches the next page. - include (optional, string): Set to `engagement`, `channel`, or both (`engagement,channel`) to fill more of every row in this one call. `engagement` fills `post.engagement.views`, `.likes` and `.comments`, `post.content.duration_seconds`, `post.ext.channel_id` and `post.author.display_name` where the row lacks them, and replaces a date derived from a label such as "4 months ago" with the exact publish time (removing `post.ext.published_precision`). The engagement join costs 1 credit per distinct stream it filled, never more than 5 per page, and streams that could not be filled are refunded. `channel` adds the channel's subscriber count (`post.ext.author_followers`, YouTube's own figure, rounded to three significant figures above 1,000) plus its avatar: every row is the same channel, so it is one lookup, 1 credit, refunded if it could not be read. Each join adds 0.2 to 0.7 seconds on a fresh page (one lookup per 50 ids, never more than 8 seconds). Read `data.hydration` for the rows, lookups, credits held and kept, and the time. - exact_dates (optional, enum: true | false): Exact publish dates are on by default and free. A row whose date the upstream derived from a relative label such as "4 months ago" (measured up to four months off) is joined to the per-video lookup for its exact publish second, at no extra credit: one lookup per page (two on a page past 50 rows, side by side), adding about 0.2 to 0.7 seconds (never more than 3). The same lookup swaps the listing title, often YouTube's English machine translation, for the creator's own, and `computed.language` follows it; a title not swapped carries `post.ext.title_maybe_translated: true`. Set to `false` to skip the lookup: rows then carry the supplier's date estimate (a day-granular or coarser label truncated to midnight UTC), marked by `post.ext.published_precision` and `post.ext.published_label`, and the listing title, marked by `post.ext.title_maybe_translated`. A row the lookup could not resolve keeps those values and markers, and `_warnings` carries `exact_dates_partial` (some rows) or `exact_dates_unavailable` (none); on `channel/videos`, whose dates are already exact, the warnings are `original_titles_partial` and `original_titles_unavailable`. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. - Constraint: one of channelId, handle (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/youtube/channel/lives" \ --data-urlencode "handle=IShowSpeed" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /youtube/channel/community-posts List a YouTube channel's community posts (oneOf: channelId|handle) Credit cost: 1 (standard) Parameters: - channelId (optional, string): YouTube channel ID. - handle (optional, string): YouTube channel handle (with or without @). - continuationToken (optional, string): Continuation token from a previous response: fetches the next page. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. - Constraint: one of channelId, handle (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/youtube/channel/community-posts" \ --data-urlencode "handle=MrBeast" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /youtube/videos/trending Get trending YouTube videos Credit cost: 1-6 (metered): 1 credit for the page. include=channel adds 1 credit per distinct channel filled, at most 5 per 50 channels; channels that could not be read are refunded, and a repeat of the same call within the cache window costs 0. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - region (optional, string): ISO 3166-1 alpha-2 country code (e.g. US, GB, KR). - category (optional, string): YouTube video category id (e.g. 10 = Music, 20 = Gaming), or a name: music, gaming, sports, news. The chart weights the category rather than filtering strictly, so check `post.ext.categoryTitle` on each row. Any other word is a free 400. - language (optional, string): Localization language (ISO 639-1) for titles/metadata. - max_results (optional, integer): Maximum number of videos to return (1-50). - cursor (optional, string): Pagination cursor from a previous response: fetches the next page. - include (optional, enum: channel): Set to `channel` (one token only) to add each row's channel stats in this one call. `channel` puts the channel's subscriber count on `post.ext.author_followers` (YouTube's own figure, which it rounds to three significant figures above 1,000) and fills the channel's `post.author.display_name`, `.avatar_url` and `.username` where missing. It also adds the country the channel's owner set, as an ISO 3166-1 alpha-2 code on `post.ext.author_country` (null when the channel sets none). Each join costs 1 credit per distinct video or channel it filled, and never more than 5 per 50; ids that could not be filled are refunded. Each join adds 0.2 to 0.7 seconds on a fresh page (one lookup per 50 ids, never more than 8 seconds). Read `data.hydration` for the rows, lookups, credits held and kept, and the time. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl "https://www.socialcrawl.dev/v1/youtube/videos/trending" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /youtube/playlist/items List the videos in a YouTube playlist Credit cost: 1-11 (metered): 1 credit for the page. include=engagement and include=channel each add 1 credit per distinct video or channel filled, at most 5 per join on a page of up to 50 rows, so a page with both is at most 11 credits; ids that could not be filled are refunded, and a repeat of the same call within the cache window costs 0. With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. Parameters: - playlist_id (required): YouTube playlist id (the value after `list=` in a playlist URL). - cursor (optional, string): Pagination cursor from a previous response: fetches the next page. - channel_id (optional, string): YouTube channel id: pages that channel's uploads instead of a playlist. - include (optional, string): Set to `engagement`, `channel`, or both (`engagement,channel`) to fill more of every row in this one call. `engagement` fills `post.engagement.views`, `.likes` and `.comments`, `post.content.duration_seconds`, `post.ext.channel_id` and `post.author.display_name` where the row lacks them, and replaces a date derived from a label such as "4 months ago" with the exact publish time (removing `post.ext.published_precision`). `channel` puts the channel's subscriber count on `post.ext.author_followers` (YouTube's own figure, which it rounds to three significant figures above 1,000) and fills the channel's `post.author.display_name`, `.avatar_url` and `.username` where missing. It also adds the country the channel's owner set, as an ISO 3166-1 alpha-2 code on `post.ext.author_country` (null when the channel sets none). Send both to cover rows that arrive without a channel id: the video join supplies it and the channel join then uses it. Each join costs 1 credit per distinct video or channel it filled, and never more than 5 per 50; ids that could not be filled are refunded. Each join adds 0.2 to 0.7 seconds on a fresh page (one lookup per 50 ids, never more than 8 seconds). Read `data.hydration` for the rows, lookups, credits held and kept, and the time. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them. Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows, then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. ``` curl -G "https://www.socialcrawl.dev/v1/youtube/playlist/items" \ --data-urlencode "playlist_id=PLFgquLnL59alCl_2TQvOiD5Vgm1hCaGSI" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /youtube/search/advanced Advanced YouTube video search Credit cost: 1-11 (metered): 1 credit for the search page. `includeExtras=true` adds a flat 5 credits for the single hydration call that fills view, like and comment counts and `duration_seconds` on the whole page. The increment never repeats: the page caps at 50 results, which one hydration call covers, so a hydrated page of 50 costs 6 credits and so does a hydrated page of 5. include=channel adds 1 credit per distinct channel filled, at most 5 per page, with channels that could not be read refunded, so a page with both is at most 11 With `max_pages`, `max_items` or `max_credits`, each page walked is billed at this price (a cached page is free), and `max_credits` caps the total. With `seen`, a page's price falls with its share of rows you already received under that id; credits for joined data (`include=`) are never discounted. Parameters: - query (required): Search query. - order (optional, string): Sort order: date, rating, relevance (default), title, videoCount, viewCount. `order=date` is exact (measured: zero inversions against the timestamps). `order=viewCount` is YouTube's popularity-weighted ranking: the top result is the true maximum but mid-list order is approximate, so for exact ranking add `includeExtras=true` and sort the page on `engagement.views` client-side. Both are far more faithful than `/v1/youtube/search`'s `sortBy=popular`, which is not view-ordered at all. - duration (optional, string): Video length: short (<4m), medium (4-20m), long (>20m), any. Genuinely applied (unlike `/v1/youtube/search`'s `duration`, which the upstream ignores). `short` plus a client-side `post.content.duration_seconds <= 180` filter is the documented Shorts approximation: see the endpoint description for why it is an approximation and not a Shorts filter. - event_type (optional, string): Broadcast type: live, upcoming, completed. - license (optional, string): License filter: creativeCommon, youtube, any. - category (optional, string): YouTube video category id (e.g. 10 = Music). - region (optional, string): ISO 3166-1 alpha-2 country code. - language (optional, string): Preferred result language (ISO 639-1). - published_after (optional, string): RFC-3339 datetime lower bound (e.g. 2026-01-01T00:00:00Z). Exact and genuinely applied. This is the endpoint to use for date-window work. - published_before (optional, string): RFC-3339 datetime upper bound. Exact and genuinely applied. - channel_id (optional, string): Restrict results to a single channel id. - max_results (optional, integer): Maximum number of videos to return (1-50). Honored exactly: a page of 50 returns 50 (live-verified). - cursor (optional, string): Pagination cursor from a previous response: fetches the next page. - safe_search (optional, string): Safe-search filter: none, moderate, strict. - video_caption (optional, string): Caption filter: any, closedCaption, none. - video_definition (optional, string): Quality filter: any, high, standard. - video_dimension (optional, string): Dimension filter: 2d, 3d, any. - video_embeddable (optional, string): Restrict to embeddable videos: true, any. - video_type (optional, string): Type filter: any, episode, movie. - topic_id (optional, string): Restrict to a Freebase topic id (e.g. /m/04rlf for music). - location (optional, string): Latitude,longitude center for a geo search (e.g. 37.42307,-122.08427). Must be used together with location_radius. - location_radius (optional, string): Radius around location with a unit suffix (e.g. 50km, 10mi). Must be used together with location. - includeExtras (optional, string): Set to `true` to add the view, like and comment counts (`post.engagement.views` / `.likes` / `.comments`) and the video length (`post.content.duration_seconds`) to every result. Left off, those four are null. YouTube's search index returns snippets only, so the counts come from a second lookup. Costs +5 credits per page (a page is at most 50 results, so it is a flat +5, not per result) and adds one upstream round-trip. If that lookup fails the page still returns, un-hydrated, with the +5 refunded and `_warnings: ["extras_unavailable"]` on the response. - include (optional, enum: channel): Set to `channel` (one token only) to add each row's channel stats in this one call. `channel` puts the channel's subscriber count on `post.ext.author_followers` (YouTube's own figure, which it rounds to three significant figures above 1,000) and fills the channel's `post.author.display_name`, `.avatar_url` and `.username` where missing. It also adds the country the channel's owner set, as an ISO 3166-1 alpha-2 code on `post.ext.author_country` (null when the channel sets none). Each join costs 1 credit per distinct video or channel it filled, and never more than 5 per 50; ids that could not be filled are refunded. Each join adds 0.2 to 0.7 seconds on a fresh page (one lookup per 50 ids, never more than 8 seconds). Read `data.hydration` for the rows, lookups, credits held and kept, and the time. - exact_dates (optional, enum: true | false): Free views, likes, comments and channel handle are on by default and cost no credit: every result is joined to one per-video lookup (`post.engagement.views` / `.likes` / `.comments`, `post.content.duration_seconds`) and one per-channel lookup (`post.author.username`, `.display_name`, `.avatar_url`), a page at a time, adding about 0.2 to 0.7 seconds each (never more than 3). Dates are exact on this endpoint either way. Set to `false` to skip both lookups and get the snippet-only rows. A row a lookup could not fill keeps its nulls, and `_warnings` carries `stats_partial` or `stats_unavailable` (counts) and `handle_partial` or `handle_unavailable` (handle). The subscriber count stays `include=channel`. - since (optional, string): Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off the page, and the page that reaches one ends the walk: `next_cursor` is not returned and `pagination.stopped_at` is `since`. Sending it sets the newest-first sort shown in this endpoint's sort parameter (any other sort value is a free 400). The page still costs what a page costs, so you pay for the pages you walk and no more. A row with no date is kept. `published_after`, `start_date`, `from_date`, `publish_time` (`yesterday`, `this-week`, `this-month`, `last-3-months`, `last-6-months`) and `max_age_days` are accepted and applied as `since`. - until (optional, string): Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. `published_before`, `end_date` and `to_date` are accepted and applied as `until`. - min_views (optional, integer): Keep only rows with at least this many views (`post.engagement.views`). A row whose view count is unknown is discarded, so every returned row meets the floor. Filtering runs on our side after each page is fetched, so every page walked is billed as usual; `data.walk.discarded` counts what was removed and why. Each page is its own entry in `GET /v1/credits/transactions`, under the page's request id listed in `data.walk.request_ids`. - max_age_days (optional, integer): Keep only rows published within this many days (`post.published_at`), 1 to 3650. A row with no date is discarded. Filtering runs on our side after each page is fetched, so every page walked is billed as usual; `data.walk.discarded` counts what was removed and why. Each page is its own entry in `GET /v1/credits/transactions`, under the page's request id listed in `data.walk.request_ids`. - sort_rows (optional, enum: views): `views`: return the kept rows ordered by view count, highest first, across every page walked. Rows without a view count go last. - max_pages (optional, integer): 1 to 50 (default 1, or 50 when `max_items` or `max_credits` is sent). Walk up to this many pages in one call and return the rows from all of them (after any filter). Each page walked is billed exactly as one call to this endpoint (a cached page is free) and counts against your rate limit; its charge and any refund are recorded under the page's own request id, listed in `data.walk.request_ids`. The walk stops early at the last page, or when the request's time budget runs low; `data.walk.stopped` says why (`end`, `max_pages`, `max_items`, `max_credits`, `time_budget`, `page_error`) and `pagination.next_cursor` continues from where it stopped. - max_items (optional, integer): 1 to 5000. Get more in one call: keep walking pages until the response holds at least this many rows (after any filter), then stop with `data.walk.stopped: "max_items"`. The last page is returned whole (it was billed), so `data.items` can hold up to one page more than this, and `pagination.next_cursor` resumes right after it without skipping a row. Walks up to 50 pages unless `max_pages` says fewer; each page is billed as one call (a cached page is free). Add `max_credits` to cap the spend. A long walk can stop on the time budget first (`"time_budget"`): send the returned cursor with the same params to continue. - max_credits (optional, integer): 1 to 100000. The most this call may spend, across every page it walks. A page starts only while the credits already spent plus that page's price (or, when higher, the most any page of this walk has cost) still fit, so the call never goes past it; the walk then stops with `data.walk.stopped: "max_credits"` and the cursor to resume. Sent alone, it walks as far as the budget goes (up to 50 pages). A value below the price of one page is a free 400 whose `error.details.page_credits` names that price. Credits a row join (`include=`) adds are known only once a page has run, so page 1 is checked against its own price. - seen (optional, string): An id you choose (1 to 64 letters, digits, `.`, `_` or `-`). Rows your account already received under the same `seen` id are removed from the page, and the page price falls with the share of repeats: page credits x new rows / rows on the page, rounded up, so a page of nothing but repeats is free. Only the page's own price is discounted: credits for joined data (`include=`) are charged only for rows you have not received, and are never discounted. The memory lasts 24 hours from the last call that used the id, holds row ids only, and is private to your account. Use one id across a set of related searches; `data.walk.repeats` counts what was removed. ``` curl -G "https://www.socialcrawl.dev/v1/youtube/search/advanced" \ --data-urlencode "query=lofi hip hop" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /youtube/search/suggestions Get YouTube search suggestions Credit cost: 1 (standard) Parameters: - query (required): Partial search query to autocomplete. - region (optional, string): ISO 3166-1 alpha-2 country code to localize suggestions. ``` curl -G "https://www.socialcrawl.dev/v1/youtube/search/suggestions" \ --data-urlencode "query=lofi" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /youtube/video/audio Get a YouTube video's audio file streams Credit cost: 5 (advanced) Parameters: - url (required): Full URL of the YouTube video. ``` curl -G "https://www.socialcrawl.dev/v1/youtube/video/audio" \ --data-urlencode "url=https://www.youtube.com/watch?v=dQw4w9WgXcQ" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /youtube/video/files Get a YouTube video's video file streams Credit cost: 5 (advanced) Parameters: - url (required): Full URL of the YouTube video. ``` curl -G "https://www.socialcrawl.dev/v1/youtube/video/files" \ --data-urlencode "url=https://www.youtube.com/watch?v=dQw4w9WgXcQ" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /youtube/video/subtitles Get a YouTube video's subtitle files Credit cost: 1 (standard) Parameters: - url (required): Full URL of the YouTube video. - format (optional, string): Subtitle file format filter (e.g. srt, vtt, ttml, json3, srv1). ``` curl -G "https://www.socialcrawl.dev/v1/youtube/video/subtitles" \ --data-urlencode "url=https://www.youtube.com/watch?v=dQw4w9WgXcQ" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /youtube/video/thumbnails Get a YouTube video's thumbnail files Credit cost: 1 (standard) Parameters: - url (required): Full URL of the YouTube video. ``` curl -G "https://www.socialcrawl.dev/v1/youtube/video/thumbnails" \ --data-urlencode "url=https://www.youtube.com/watch?v=dQw4w9WgXcQ" \ -H "x-api-key: YOUR_API_KEY" ``` ### GET /youtube/profile/full YouTube profile, recent posts, and computed analytics in one call. (oneOf: handle|channelId|url) Credit cost: 5 (override; tier standard) Parameters: - handle (optional, string): YouTube username or handle, with or without a leading @. One of the identity params is required. - channelId (optional, string): YouTube channel id, the 24-character string starting with UC. One of the identity params is required. - url (optional, string): Full YouTube profile or page URL. One of the identity params is required. - posts (optional, integer): How many of the fetched recent posts to return and compute the metrics over (1-100, default 25). One call reads one page, which on YouTube is up to 30 videos, so a larger value returns that page and a `_warnings` note; pass posts_cursor back as cursor for the next page. - cursor (optional, string): Pass a prior response's posts_cursor to read the next page of posts. Each call is a separate 5-credit call whose metrics cover that page only. - include (optional, string): CSV subset of posts,computed (default both). include=computed omits the raw posts[] array from the response to save payload. The metrics are computed from the same fetched posts either way, so they do not change. - Constraint: one of handle, channelId, url (at least one required) ``` curl -G "https://www.socialcrawl.dev/v1/youtube/profile/full" \ --data-urlencode "handle=mrbeast" \ -H "x-api-key: YOUR_API_KEY" ``` --- ## Error Codes | Code | Status | Description | |---|---|---| | MISSING_API_KEY | 401 | No API key on the request: send x-api-key or Authorization: Bearer | | INVALID_API_KEY | 401 | API key is malformed or not found | | INSUFFICIENT_CREDITS | 402 | Not enough credits: purchase more at https://www.socialcrawl.dev | | INVALID_REQUEST | 400 | Missing or invalid parameters: `error.details.next_step` says what to send, and `error.details.fields` names a required one-of group when none of it was sent | | ENDPOINT_NOT_FOUND | 404 | Unknown platform or resource | | RESOURCE_NOT_FOUND | 404 | The requested item was not found on the platform | | RATE_LIMITED | 429 | More than 600 requests per minute on this API key | | CONCURRENCY_LIMIT | 429 | Too many simultaneous requests (max 50 per key) | | UPSTREAM_ERROR | 502, 504 | Platform returned an error, or the call reached the platform time limit: credits refunded | | SERVICE_UNAVAILABLE | 503 | Circuit breaker open: try again shortly, credits refunded | | INTERNAL_ERROR | 500 | Unexpected error: credits refunded |