---
title: "Up to 50 (platform, handle) pairs → one canonical Author per row, failed handles refunded."
method: POST
path: "/prism/profiles"
tags: ["prism"]
---

# Up to 50 (platform, handle) pairs → one canonical Author per row, failed handles refunded.

`POST /prism/profiles`

## Request

- Base URL: `https://www.socialcrawl.dev/v1`
- URL: `https://www.socialcrawl.dev/v1/prism/profiles`
- Auth: API key in header `x-api-key`

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.

## Headers

- `Cache-Control` string
- `Idempotency-Key` string

## Request body

- object
  - `items` string, required
  - `include` string
  - `since` string
  - `posts_limit` integer
  - `comments_top_posts` integer
  - `comments_per_post` integer
  - `comments_select_by` 'comments' | 'views'

## Response `200`

Successful response

- object
  - `success` boolean, required: Whether the request succeeded
  - `platform` string, required: Platform name
  - `endpoint` string, required: API endpoint path
  - `data` object, required: Platform-specific response data
    - `metrics` object: Key performance metrics
      - `total_views` integer: Total view count
      - `total_likes` integer: Total like count
      - `total_comments` integer: Total comment count
      - `engagement_rate` number: Computed engagement rate (0-1)
    - `period` string, nullable: Time period for the analytics data
    - `breakdown` object[], nullable: Per-item or per-period breakdown
    - `_warnings` string[]: Non-fatal notices about this response (field-map drift, clamped computed values). Advisory only: its presence never means the request failed. Omitted entirely when there is nothing to report, so treat absent as 'no warnings'.
  - `credits_used` integer, required: Number of credits consumed
  - `credits_remaining` integer, nullable, required: Current account balance, or null when this response could not establish one. Free cache hits always return null because they do not establish an authoritative post-request balance. An idempotency replay also returns null when its current-balance lookup fails. It is never a fabricated zero, so a zero here means the account really is empty. The authoritative reading is the one on a billed response, which comes straight from the deduction.
  - `request_id` string, required: Unique request identifier for support
  - `cached` boolean, required: Whether the response was served from cache
  - `idempotent_replay` boolean: True only when this response is an idempotency replay
  - `meta` object: Optional. Present only when this call completed a pattern that one other call does more cheaply or in one step, at most once per pattern per API key per day. Send the header `X-SocialCrawl-Hints: off` to never receive it.
    - `hint` object: One suggestion: what to call instead and why, with the price when the suggested call costs more than the calls it replaces.
      - `code` string: creator_card | batch_profiles | search_multi | incremental_sync | comment_labels | search_repeat
      - `message` string: One sentence for a person or an agent to read.
      - `method` string: GET or POST.
      - `path` string: The call the hint points to.

## Other responses

- `400`: Invalid request - missing or malformed parameters
- `401`: Unauthorized - missing or invalid API key
- `402`: Payment required - the account balance is too low, or the calling key has spent its own per-key credit limit
- `404`: Not found - the endpoint does not exist, or the requested resource was not found upstream
- `405`: Method not allowed - wrong HTTP verb for this endpoint
- `409`: Conflict - an idempotent request is still in flight, its completed body exceeded the replay limit, or the cohort resource is not in a state that allows this operation
- `410`: Gone - the discover run's retention passed and its rows were deleted; start a new run
- `413`: Payload too large: the JSON request body exceeds the 1 MB size limit and is rejected before parsing, or a single cohort result cannot fit beneath the 1 MB response-page ceiling
- `422`: Idempotency payload mismatch - this Idempotency-Key was already used with a different request payload; or a discover request with async=false is over what one call can answer (NEEDS_ASYNC)
- `429`: Rate or concurrency limit exceeded. `RATE_LIMITED`: more than 600 requests in a 1-minute sliding window on this API key (headers `X-RateLimit-Limit`/`Remaining`/`Reset`; `Retry-After` is seconds until the window resets). `CONCURRENCY_LIMIT`: more than 50 simultaneous in-flight requests (headers `X-Concurrency-Limit`/`Remaining`; short static `Retry-After`). Both are unbilled. Honor `Retry-After`, then back off with jitter. See /docs/rate-limits.
- `500`: Internal server error - an immediate refund is attempted; credits_used reports the committed net charge and exact compensation is queued if that refund cannot commit
- `502`: Upstream error - the platform returned an error; an immediate refund is attempted and credits_used reports the committed net charge
- `503`: Service unavailable - the request is unbilled when rejected before deduction, otherwise an immediate refund is attempted and credits_used reports the committed net charge
- `504`: Gateway timeout - the request was still running close to the platform time limit and is answered as UPSTREAM_ERROR, the same code as 502. An immediate refund is attempted and credits_used reports the committed net charge. When details.retry_with is set, retryable is false and there is no Retry-After

---

[API](https://skmtc.dev/socialcrawl/apis/socialcrawl-api.md) · [All operations](https://skmtc.dev/socialcrawl/apis/socialcrawl-api/llms.txt) · [OpenAPI document](https://skmtc.dev/socialcrawl/apis/socialcrawl-api/revisions/a0ad2511004a?raw)
