Prism Profiles API
Returns a profile for each platform and handle pair you send, in the order you sent them, with a status per row and the same shape as the single profile call.
POST /v1/prism/profilesLast updated November 2026Maintained by the SocialCrawl team
When to use the Prism Profiles API
Use it to vet many handles at once instead of calling the profile endpoint per handle; creator-card is for one handle across several platforms.
Not for one handle across several platforms (prism/creator-card), or a single profile where its own endpoint is simpler (tiktok/profile, instagram/profile).
From the API reference
Batch profile lookup for handle vetting. POST a JSON body with an items array of 1-50 { platform, handle, custom_id? } objects (handle accepts an @handle, a bare handle, or a pasted profile URL); returns one row per item: the canonical Author object (same shape as the single GET profile endpoint), a status of ok / not_found / unsupported / error / deferred, the caller's custom_id echoed, and the credits that row cost: in input order. Per-item isolation: one dead or private handle never fails the batch. Billing is per item at each platform's own credit tier (most platforms 1 credit; LinkedIn 5); only successful rows are charged, everything else is refunded. Supported platforms: instagram, tiktok, youtube, twitter, threads, twitch, snapchat, truthsocial, bluesky, kwai, reddit, telegram, linkedin (a member, or a company page as a linkedin.com/company/… URL), facebook. A LinkedIn lookup that is rate limited is retried once and then answered deferred at no charge: re-request it. Never cached. Optionally streams Server-Sent Events when the client sends Accept: text/event-stream. Send include: "posts" (up to 25 items) to add each handle's first page of posts beside its profile: every ok row gains a posts block with the same post rows, next_cursor and has_more as that handle's own post list endpoint (endpoint names it: instagram profile/posts, tiktok profile/videos, youtube channel/videos, twitter user/tweets, threads user/posts, facebook profile/posts, reddit profile/posts, telegram profile/posts, linkedin profile/posts or company/posts; other platforms answer posts.status: "unsupported"). since on the body and stop_at_id on an item stop each page at a date or at a post you already hold, as on those endpoints, and posts.stopped_at says which. Add posts_limit (1 to 100) to walk each handle's post list until that many posts are held: posts.pages is the pages charged and posts.stopped says why the walk ended (limit, end, window, page_cap). include: "posts,about" also returns activity (last_post_at, posts_30d, posts_90d, media_mix) and stats (avg_likes, avg_comments, engagement_rate) on every row, computed from the posts held. Add comments to include (it implies posts) to read the comments of each handle's top posts: the comments_top_posts posts (default 3) with the most comments among the posts walked (a post without a comment count is ranked by views after them, or rank all by views with comments_select_by: "views") each gain comments with items (the same comment rows as that platform's comment endpoint: instagram, tiktok and facebook post/comments, youtube video/comments, twitter tweet/replies, linkedin post/comments, reddit post/comments, threads post/comments), total (the post's comment count), truncated and next_cursor, up to comments_per_post (default 20) per post; the row's comments block gives status (ok, partial, failed, deferred, empty, unsupported) and the credits. One post that fails never drops the row. The profile in data is unchanged.
Searching 69 platforms in parallel
What can you do with the Profiles API?
The Profiles endpoint gives you structured Prism data with computed fields in a single request. No scraping infrastructure to build or maintain.
Example Request
# POST /v1/prism/profiles: Metered: holds 1 credit per call up front and refunds what the call does not use. Cache hits are free and failed calls are refunded.
# Check the price first for 0 credits: add dry_run=1 to the query. Nothing is fetched or charged.
# --max-time 60: the API's own time limit for this endpoint.
# --retry 3: retries timeouts, 429 and 5xx, honoring Retry-After.
# -o response.json keeps only the final attempt's body.
# One Idempotency-Key per request, reused on every retry, so a retried call is never charged twice.
IDEMPOTENCY_KEY=$(od -An -N16 -tx1 /dev/urandom | tr -d ' \n')
curl -sS --max-time 60 --retry 3 -o response.json -X POST \
-H "x-api-key: YOUR_API_KEY" \
-H "Idempotency-Key: $IDEMPOTENCY_KEY" \
-H "Content-Type: application/json" \
-d '{"items":[{"platform":"tiktok","handle":"@scout2015"},{"platform":"linkedin","handle":"williamhgates","custom_id":"vet-1"}]}' \
"https://www.socialcrawl.dev/v1/prism/profiles" && cat response.json# POST /v1/prism/profiles: Metered: holds 1 credit per call up front and refunds what the call does not use. Cache hits are free and failed calls are refunded.
# Check the price first for 0 credits: add dry_run=1 to the query. Nothing is fetched or charged.
import os
import random
import time
import uuid
import requests
API_KEY = os.environ.get("SOCIALCRAWL_API_KEY", "YOUR_API_KEY")
TIMEOUT_S = 60 # the API's own time limit for this endpoint.
MAX_RETRIES = 3
URL = "https://www.socialcrawl.dev/v1/prism/profiles"
def backoff(attempt):
# Exponential backoff with jitter: about 1 s, 2 s, then 4 s.
return 2 ** attempt * (0.5 + random.random() / 2)
def socialcrawl(payload):
# One Idempotency-Key per request, reused on every retry, so a retried call is never charged twice.
idempotency_key = str(uuid.uuid4())
for attempt in range(MAX_RETRIES + 1):
try:
res = requests.post(
URL,
json=payload,
headers={"x-api-key": API_KEY, "Idempotency-Key": idempotency_key},
timeout=TIMEOUT_S,
)
body = res.json()
except (requests.RequestException, ValueError):
# A timeout, a dropped connection or a non-JSON gateway page: retry with the same key.
if attempt == MAX_RETRIES:
raise
time.sleep(backoff(attempt))
continue
if body["success"]:
return body
error = body["error"]
# Branch on error["type"], never on the message text.
if error["type"] == "RESOURCE_NOT_FOUND":
return None # refunded: nothing to read
retry_after = res.headers.get("Retry-After")
# SERVICE_UNAVAILABLE without Retry-After is a withdrawn endpoint: permanent.
permanent = error["type"] == "SERVICE_UNAVAILABLE" and retry_after is None
if not error.get("retryable") or permanent or attempt == MAX_RETRIES:
raise RuntimeError(f"{error['type']}: {error['message']} (request_id {body['request_id']})")
# Retry-After (seconds) wins; otherwise back off.
time.sleep(float(retry_after) if retry_after else backoff(attempt))
payload = {
"items": [{"platform": "tiktok", "handle": "@scout2015"}, {"platform": "linkedin", "handle": "williamhgates", "custom_id": "vet-1"}],
}
body = socialcrawl(payload)
if body is not None:
print(body["data"])// POST /v1/prism/profiles: Metered: holds 1 credit per call up front and refunds what the call does not use. Cache hits are free and failed calls are refunded.
// Check the price first for 0 credits: add dry_run=1 to the query. Nothing is fetched or charged.
const API_KEY = process.env.SOCIALCRAWL_API_KEY ?? "YOUR_API_KEY";
const TIMEOUT_MS = 60_000; // the API's own time limit for this endpoint.
const MAX_RETRIES = 3;
type Page = { next_cursor: string | null; has_more: boolean };
type SocialCrawlResponse =
| { success: true; data: any; pagination?: Page; credits_used: number; request_id: string }
| { success: false; error: { type: string; message: string; retryable: boolean }; request_id: string };
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
// Exponential backoff with jitter: about 1 s, 2 s, then 4 s.
const backoffMs = (attempt: number) => 2 ** attempt * 1000 * (0.5 + Math.random() / 2);
async function socialcrawl(url: string, payload: Record<string, unknown>) {
// One Idempotency-Key per request, reused on every retry, so a retried call is never charged twice.
const idempotencyKey = crypto.randomUUID();
for (let attempt = 0; ; attempt++) {
let res: Response;
let body: SocialCrawlResponse;
try {
res = await fetch(url, {
method: "POST",
headers: {
"x-api-key": API_KEY,
"Content-Type": "application/json",
"Idempotency-Key": idempotencyKey,
},
body: JSON.stringify(payload),
signal: AbortSignal.timeout(TIMEOUT_MS),
});
body = (await res.json()) as SocialCrawlResponse;
} catch (error) {
// A timeout, a dropped connection or a non-JSON gateway page: retry with the same key.
if (attempt >= MAX_RETRIES) throw error;
await sleep(backoffMs(attempt));
continue;
}
if (body.success) return body;
// Branch on error.type, never on the message text.
if (body.error.type === "RESOURCE_NOT_FOUND") return null; // refunded: nothing to read
const retryAfter = res.headers.get("Retry-After");
// SERVICE_UNAVAILABLE without Retry-After is a withdrawn endpoint: permanent.
const permanent = body.error.type === "SERVICE_UNAVAILABLE" && retryAfter === null;
if (!body.error.retryable || permanent || attempt >= MAX_RETRIES) {
throw new Error(`${body.error.type}: ${body.error.message} (request_id ${body.request_id})`);
}
// Retry-After (seconds) wins; otherwise back off.
await sleep(retryAfter ? Number(retryAfter) * 1000 : backoffMs(attempt));
}
}
const body = await socialcrawl("https://www.socialcrawl.dev/v1/prism/profiles", {
items: [{ platform: "tiktok", handle: "@scout2015" }, { platform: "linkedin", handle: "williamhgates", custom_id: "vet-1" }],
});
if (body) console.log(body.data);Parameters
| Parameter | Required | Description |
|---|---|---|
| items | Yes | 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 | No | 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 | No | 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 | No | 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 | No | 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 | No | 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 | No | 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. (comments | views) |
What does the Prism Profiles API return?
Every response follows one unified schema. Here is a real, unmodified response body, so you can see the exact fields you get back before spending a credit.
Example response
{
"success": true,
"platform": "instagram",
"endpoint": "/v1/instagram/engagement",
"data": {
"engagement_rate_percentages": 38.33,
"recent_posts": 12,
"followers": 87608035,
"comments": 528912,
"likes": 33049046,
"recent_posts_explanation": "Statistics based on the last 12 posts",
"id_user": "2278169415",
"username": "mrbeast",
"is_private": false,
"posts_details": [
{
"likes": 5636982,
"comments": 69484,
"taken_at": 1781457954,
"datetime": "2026-06-14 20:25:54",
"hours_since_post": 461,
"time_ago": "19 days ago",
"likes_per_hour": 12228,
"comments_per_hour": 151
},
{
"likes": 20000768,
"comments": 223510,
"taken_at": 1732824650,
"datetime": "2024-11-28 23:10:50",
"hours_since_post": 13971,
"time_ago": "2 years ago",
"likes_per_hour": 1432,
"comments_per_hour": 16
},
{
"likes": 929226,
"comments": 30633,
"taken_at": 1782232475,
"datetime": "2026-06-23 19:34:35",
"hours_since_post": 246,
"time_ago": "10 days ago",
"likes_per_hour": 3777,
"comments_per_hour": 125
},
{
"likes": 487761,
"comments": 22482,
"taken_at": 1781799425,
"datetime": "2026-06-18 19:17:05",
"hours_since_post": 366,
"time_ago": "15 days ago",
"likes_per_hour": 1333,
"comments_per_hour": 61
},
{
"likes": 712265,
"comments": 15716,
"taken_at": 1781366405,
"datetime": "2026-06-13 19:00:05",
"hours_since_post": 487,
"time_ago": "20 days ago",
"likes_per_hour": 1463,
"comments_per_hour": 32
},
{
"likes": 1475116,
"comments": 35386,
"taken_at": 1781277094,
"datetime": "2026-06-12 18:11:34",
"hours_since_post": 512,
"time_ago": "21 days ago",
"likes_per_hour": 2881,
"comments_per_hour": 69
},
{
"likes": 1108220,
"comments": 26632,
"taken_at": 1780160249,
"datetime": "2026-05-30 19:57:29",
"hours_since_post": 822,
"time_ago": "1 months ago",
"likes_per_hour": 1348,
"comments_per_hour": 32
},
{
"likes": 542948,
"comments": 28476,
"taken_at": 1779375582,
"datetime": "2026-05-21 17:59:42",
"hours_since_post": 1040,
"time_ago": "1 months ago",
"likes_per_hour": 522,
"comments_per_hour": 27
},
{
"likes": 698514,
"comments": 24401,
"taken_at": 1779120014,
"datetime": "2026-05-18 19:00:14",
"hours_since_post": 1111,
"time_ago": "2 months ago",
"likes_per_hour": 629,
"comments_per_hour": 22
},
{
"likes": 468000,
"comments": 13548,
"taken_at": 1778947209,
"datetime": "2026-05-16 19:00:09",
"hours_since_post": 1159,
"time_ago": "2 months ago",
"likes_per_hour": 404,
"comments_per_hour": 12
},
{
"likes": 526594,
"comments": 24411,
"taken_at": 1777737719,
"datetime": "2026-05-02 19:01:59",
"hours_since_post": 1495,
"time_ago": "2 months ago",
"likes_per_hour": 352,
"comments_per_hour": 16
},
{
"likes": 462652,
"comments": 14233,
"taken_at": 1777580305,
"datetime": "2026-04-30 23:18:25",
"hours_since_post": 1538,
"time_ago": "2 months ago",
"likes_per_hour": 301,
"comments_per_hour": 9
}
]
},
"credits_used": 5,
"credits_remaining": 9999,
"request_id": "req-8Kq2ZmR4vT9xLb3P",
"cached": false,
"redacted": true
}Example captured from the Instagram API. Every SocialCrawl endpoint returns this same unified schema, so your Prism Profiles response has the same fields.
How does the Prism Profiles API work?
Send a GET request with your API key and get back clean, structured JSON in our unified schema. Supported computed fields are populated when the source provides the required inputs.
Method
POST
Response
JSON
How do you scrape social media data in seconds?
The fastest social media scraping API for developers. Scrape profiles, posts, comments, and analytics from 69 platforms covering 10B+ monthly active users.
One schema, every platform
Query 69 platforms with identical response structures. Write your integration once.
Computed fields, not just scraped
When an endpoint supports these metrics and the source provides the required inputs, the normalized record includes engagement_rate, estimated_reach, content_category, and language. Ready to use.
See your data before you code
Visual Data Explorer. Paste any URL, get rich result cards, sortable tables, CSV export.
import requests
response = requests.get(
'https://www.socialcrawl.dev/v1/tiktok/profile',
params={'handle': 'charlidamelio'},
headers={'x-api-key': 'sc_YOUR_API_KEY'}
)
data = response.json(){
"success": true,
"platform": "tiktok",
"data": {
"author": {
"username": "charlidamelio",
"followers": 152400000
},
"engagement": {
"likes": 12400000000,
"engagement_rate": 0.087
},
"metadata": {
"language": "en",
"content_category": "lifestyle"
}
}
}Have a question? We got answers
Find answers to frequently asked questions about SocialCrawl's API, pricing, and capabilities.
Contact usWhat does the Prism Profiles API return?
When should I use the Prism Profiles endpoint?
How much does the Prism Profiles API cost?
What parameters does the Prism Profiles API need?
How do I call the Prism Profiles API?
Ask AI about SocialCrawl
Ready to scrape Prism Profiles data?
Get your API key and start pulling Prism data in under 60 seconds.
