> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sideshift.app/llms.txt
> Use this file to discover all available pages before exploring further.

# TikTok — profile info

> Display name, bio, follower/following counts, profile picture, and post count for a creator. 1 credit.



## OpenAPI

````yaml /openapi/scraper.yaml post /scrape/tiktok/profile
openapi: 3.0.4
info:
  title: SideShift Scraper API
  version: 1.1.0
  description: >
    Scrape profiles, posts, and single posts from seven major social platforms —

    **TikTok, Instagram, YouTube, Facebook, Snapchat, X, and LinkedIn** —
    through one consistent,

    credit-billed API. Every endpoint returns the same unified shape, so you
    write one

    parser for all platforms. (The same data engine powers SideShift's own
    analytics, so

    the output matches our internal pipeline byte-for-byte.)


    ## Quickstart


    ```bash

    curl -X POST https://app.sideshift.app/api/v1/scrape/tiktok/posts \
      -H "x-api-key: scrape_live_YOUR_KEY" \
      -H "Content-Type: application/json" \
      -d '{ "username": "mrbeast" }'
    ```


    ```jsonc

    {
      "data": {
        "posts": [ /* ~27 unified Post objects (one page) */ ],
        "profile_pictures": { "mrbeast": "https://…" },
        "next_cursor": "1781491401000"
      },
      "request_id": "req_8f3c9a2b1d4e6f70",
      "upstream_calls": 1,
      "meta": { "credits_charged": 1, "credits_remaining": 9999 }
    }

    ```


    ## 1. Authentication


    Every request is authenticated with an independent **Scraper API key** sent
    in the

    `x-api-key` header.


    1. Open [Scraper → API Keys](https://app.sideshift.app/scraper) and click
    **Create key**.

    2. Copy it immediately — the full key is shown only once. Stored previews
    are **not** usable secrets.

    3. Send it on every request:


    ```http

    POST /api/v1/scrape/instagram/post HTTP/1.1

    x-api-key: scrape_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

    Content-Type: application/json

    ```


    - Scraper keys use the `scrape_live_*` prefix.

    - Integration API keys (`sk_live_*` / `sk_test_*`) are **rejected with
    `401`** on scraper routes, and vice-versa — the two key families are
    isolated.

    - Keys are stored as SHA-256 hashes; revoke a key any time from the
    dashboard.


    > **Call from your backend only.** Never embed a scraper key in client-side
    code.


    ## 2. Credits & billing


    Scraping is **metered in credits** — you pre-purchase a credit balance and
    each call

    debits it. There is no per-month subscription for the scraper; you pay only
    for what you use.


    - **Cost:** profile, one page of posts, and single-post calls cost **1
    credit**. TikTok
      audience calls cost **25 credits**. The exact charge is always returned in
      `meta.credits_charged` and the `X-Scraper-Credits-Charged` header.
    - **Pagination cost:** each page of a posts listing is a separate billable
    call — paginating a
      300-video TikTok account (~27/page) costs ~12 credits, one per page.
    - **Volume pricing:** credits are cheaper per thousand as you buy more (e.g.
    150¢/1k →
      100¢/1k → 80¢/1k). See live pricing and your balance in the dashboard.
    - **Buying credits:** purchase from your SideShift wallet balance or by
    card/crypto
      checkout in the [Scraper dashboard](https://app.sideshift.app/scraper).
    - **You are never charged for:** authentication failures, input validation
    errors,
      rate-limit blocks, insufficient-balance blocks, oversized bodies, or upstream
      errors/timeouts (`502`/`504`) — those refund the reservation in full.
    - **You are charged for:** successful scrapes and confirmed not-found
    results (`404`),
      because the data source bills SideShift for a completed lookup. The error envelope's
      message tells you whether credits were refunded.

    ## 3. Resources


    Three core resources are available for every platform. TikTok also supports
    audience

    location data:


    | Resource | Path | Returns |

    |---|---|---|

    | **Profile** | `POST /scrape/{platform}/profile` | Profile-level info for a
    creator: display name, bio, follower/following counts, profile picture, post
    count. |

    | **Posts** | `POST /scrape/{platform}/posts` | One page of the creator's
    recent posts + profile picture (+ `next_cursor` where supported). |

    | **Single Post** | `POST /scrape/{platform}/post` | One post, looked up by
    its URL. Set `include_transcript: true` to request a transcript where
    supported. |

    | **Audience** | `POST /scrape/tiktok/audience` | TikTok audience locations
    for a creator. Costs 25 credits. |


    Platforms: `tiktok`, `instagram`, `youtube`, `facebook`, `snapchat`,
    `twitter` (X), `linkedin`.


    > **Naming note:** `profile` returns the *account's own* stats (bio,
    follower count, avatar).

    > It is **not** audience demographics — there is no age/gender/location
    breakdown. Use

    > `posts` to list what a creator has published, and `post` to fetch one post
    by URL.


    ## 4. Pagination & page size


    A `posts` call returns **one page** of recent posts. Page size and
    pagination support are

    platform-determined (measured against large accounts — MrBeast, NASA, Nike):


    | Platform | Posts per page | Pagination |

    |---|---|---|

    | TikTok | ~27 | ✅ `next_cursor` (short numeric token) |

    | Instagram | ~12 | ✅ `next_cursor` (base64 token, ~200+ chars) |

    | YouTube | ~30 | ✅ `next_cursor` (large opaque token) |

    | Facebook | ~6 | ❌ single page (no `next_cursor`) |

    | Snapchat | ~7 | ❌ single page (no `next_cursor`) |

    | X | Platform-determined | Cursor when returned |

    | LinkedIn | Platform-determined | Cursor when returned |


    When a `next_cursor` is present, pass it back as `cursor` to fetch the next
    page; the field

    is **omitted** on the last page (and on Facebook/Snapchat, which return a
    single page). Each

    page is one billable call.


    ```bash

    curl -X POST https://app.sideshift.app/api/v1/scrape/tiktok/posts \
      -H "x-api-key: scrape_live_YOUR_KEY" \
      -d '{ "username": "mrbeast", "cursor": "1781491401000" }'
    ```


    `youtube/posts` also accepts `contentType` (`shorts` — default — or
    `video`).


    ## 5. The unified Post object


    Returned by `posts` and `post`, identical across every platform:


    | Field | Type | Notes |

    |---|---|---|

    | `id` | string | Platform-native post id. (Facebook reels are canonicalised
    to the numeric reel id.) |

    | `title` | string | Caption / description text. |

    | `creator` | string | Creator handle (no leading `@`). |

    | `platform` | enum | `tiktok` \| `instagram` \| `youtube` \| `facebook` \|
    `snapchat` \| `twitter` \| `linkedin`. |

    | `postPage` | string | Canonical URL of the post. |

    | `views`, `likes`, `comments`, `shares`, `bookmarks` | integer |
    Engagement. `comments` is the **total comment count**, not the comment text
    — for the text see `topComments`. **`0`** when the platform doesn't expose a
    metric (e.g. Instagram/YouTube `shares`). Never `null`. |

    | `uploadedAt` | integer \| null | Unix **seconds**; `null` if unknown. |

    | `uploadedAtFormatted` | string | ISO-8601 UTC of `uploadedAt`, e.g.
    `2026-06-15T02:43:21` (no fractional seconds, no trailing `Z`); empty if
    unknown. |

    | `thumbnail` | string | Cover image URL (may be empty). |

    | `videoUrl` | string | Best-quality direct video URL. Populated for
    TikTok/Instagram/Facebook/Snapchat. For **YouTube** it is **empty in posts
    listings** and only populated by the single-post endpoint
    (`/scrape/youtube/post`), which returns a short-lived `googlevideo.com`
    playback URL. |

    | `hashtags` | string[] | Lowercased, leading `#` stripped (Unicode-aware;
    parsed from the caption when the post has no structured tags). |

    | `region` | string \| null | ISO 3166-1 alpha-2 content region (e.g. `US`).
    **TikTok only** — `null` on every other platform. |

    | `transcript` | string \| null | Available only from a single-post request
    with `include_transcript: true`, when the target site provides one. **Not
    supported for Instagram**, and `null` when unavailable or not requested. |

    | `topComments` | object[] \| null | A ranked **sample** of the post's
    comments from a single-post request with `include_comments: true` (**TikTok,
    YouTube, Facebook**). Each entry is `{ id, author, text, likeCount,
    replyCount, timestamp }`. `null` when not requested, unavailable, or the
    platform is Instagram/Snapchat/X/LinkedIn; `[]` when requested and there
    genuinely are none. |


    > Media URLs (`thumbnail`, `videoUrl`, `profile_picture`,
    `profile_pictures`) are short-lived

    > signed CDN links — fetch/cache them promptly.


    ### Getting full YouTube detail (comment count + video URL)


    YouTube's `posts` listing is the fast, cheap path, but it **omits `videoUrl`
    and can omit the

    `comments` count**, and a channel's newest (sub-day) uploads can come back
    with

    `uploadedAt: null` and `views: 0` until the upstream listing ages them (~a
    day).


    To get the **complete** record for a specific video — the `comments` count,
    the short-lived

    `googlevideo.com` `videoUrl`, and the freshest `views`/`likes` — call

    **`POST /scrape/youtube/post`** with the video's `postPage` URL (1 credit).
    (For the comment

    *text*, add `include_comments: true` — see below.)


    ```bash

    # 1) list a channel's recent Shorts (ids, views, thumbnails, dates)

    curl -X POST https://app.sideshift.app/api/v1/scrape/youtube/posts \
      -H "x-api-key: scrape_live_YOUR_KEY" -d '{ "username": "@MrBeast" }'

    # 2) enrich a specific video — adds the comments count + videoUrl + freshest
    stats

    curl -X POST https://app.sideshift.app/api/v1/scrape/youtube/post \
      -H "x-api-key: scrape_live_YOUR_KEY" -d '{ "url": "https://www.youtube.com/shorts/r9aWeGqp43s" }'
    ```


    This is the **same enrichment SideShift's internal pipeline runs
    automatically**; the public

    API keeps it as an explicit per-video call so your credit cost stays
    predictable (auto-enriching

    a whole page could cost dozens of credits). This extra step is
    YouTube-specific.


    ### Requesting a transcript


    Single-post details requests for TikTok, YouTube, Facebook, and Snapchat can
    request a

    transcript by adding `include_transcript: true`:


    ```bash

    curl -X POST https://app.sideshift.app/api/v1/scrape/tiktok/post \
      -H "x-api-key: scrape_live_YOUR_KEY" \
      -H "Content-Type: application/json" \
      -d '{ "url": "https://www.tiktok.com/@mrbeast/video/7651447222449556767", "include_transcript": true }'
    ```


    The result is returned as `data.posts[0].transcript`. It is `null` if the
    target site does

    not provide a transcript. Instagram does not support transcript retrieval;
    its single-post

    response has `transcript: null` even when the flag is supplied.


    ### Requesting comments


    Single-post requests for **TikTok, YouTube, and Facebook** can return
    comment text by adding

    `include_comments: true`:


    ```bash

    curl -X POST https://app.sideshift.app/api/v1/scrape/tiktok/post \
      -H "x-api-key: scrape_live_YOUR_KEY" \
      -H "Content-Type: application/json" \
      -d '{ "url": "https://www.tiktok.com/@mrbeast/video/7651447222449556767", "include_comments": true }'
    ```


    The result is `data.posts[0].topComments`, an array of

    `{ id, author, text, likeCount, replyCount, timestamp }`. It costs no extra
    credits.


    > **This is a sample, not the whole thread.** You get roughly 10-50
    top-ranked comments no

    > matter how many the post has — a video with 11,000 comments returns about
    20 — and there is

    > no way to page past them. Use it to read the tenor of a post's response,
    not to collect a

    > complete comment dataset. `comments` remains the true total.


    **Instagram, Snapchat, X, and LinkedIn are not supported** and always return

    `topComments: null`, even when the flag is supplied.


    ## 6. The Profile object


    Returned by `profile`. Field availability varies by platform — only
    `username` is guaranteed:


    | Field | Type | Notes |

    |---|---|---|

    | `username` | string | The handle that was requested (leading `@`
    stripped). Always present. |

    | `display_name` | string | Human-readable account name. |

    | `bio` | string | Profile bio / description / channel "about". |

    | `follower_count` | integer | Followers. **On YouTube this is the
    subscriber count.** |

    | `following_count` | integer | Accounts this creator follows. Often absent
    on YouTube/Snapchat. |

    | `profile_picture` | string | Avatar URL (short-lived signed CDN link).
    Often absent on YouTube. |

    | `post_count` | integer | Total posts/videos on the account. Often absent
    on YouTube/Snapchat. |


    ## 7. Errors & refunds


    Errors return a SideShift-owned envelope (`error`, `message`, `request_id`,
    and

    `platform`/`identifier`/`field`/`reason` when known). See each endpoint's
    responses and

    the `ScrapeError` schema for the full list. `404` not-found is billed;
    `502`/`504`

    upstream failures are refunded in full; `500` server errors refund the
    reservation.


    **Transient not-founds.** Under heavy anti-scraping pressure (Instagram
    especially), the

    data source can occasionally return `404 PROFILE_NOT_FOUND` for a handle
    that genuinely

    exists. If a `404` is unexpected for a known-good profile, retry once — it
    usually

    succeeds. (We don't auto-retry `404`s server-side, so a real not-found isn't
    masked and

    you're never charged twice for one logical lookup.)


    ## 8. Rate limits


    Your account has a **total** limit in requests per minute (120 by default),
    and

    may additionally have a **per-endpoint** limit that applies to each endpoint
    on

    its own. Both are token buckets: you can spend a full minute's allowance in
    one

    burst, and it then refills continuously at your per-minute rate rather than

    resetting on a clock edge. A request must pay for itself out of every limit
    that

    applies to it.


    Because the per-endpoint limit is independent, exhausting one endpoint does
    not

    affect the others — draining `tiktok/posts` leaves your `instagram/posts`

    allowance untouched. Ask us if you need a per-endpoint limit set.


    Every response carries `X-Scraper-RateLimit-Limit`,

    `X-Scraper-RateLimit-Remaining`, and `X-Scraper-RateLimit-Reset` (Unix
    seconds),

    reporting whichever limit is closest to being reached. Exceeding one returns

    `429` `SCRAPER_RATE_LIMITED`, with a `Retry-After` header telling you how
    many

    seconds until the request would succeed, and no credit charge.


    Separately, and rarely, the scraping system as a whole can be at capacity.
    That

    returns `429` `SCRAPER_SYSTEM_BUSY` with a `Retry-After` header (seconds)
    and no

    credit charge — your reservation is refunded in full, no lookup is
    performed, and

    the request does not count against your rate limits either. It is not about
    your

    account or your bucket, so the fix is simply to retry after the interval
    rather

    than to reduce your request rate.


    ## 9. Performance & latency


    A single scrape is one live upstream lookup, so **response time is dominated
    by the

    source platform** — not by SideShift. The table below is measured against
    large real

    accounts (MrBeast, NASA, Nike), 5 samples per endpoint, as **median / p95**
    wall-clock

    seconds (last measured **2026-07-09**). Treat these as rough guidance, **not
    an SLA** —

    latency varies with account size, the platform's anti-bot pressure, and time
    of day.


    | Platform | Profile | Posts (one page) | Single Post |

    |---|---|---|---|

    | **TikTok** | 1.9 s / 2.2 s | 1.9 s / 2.8 s | **0.9 s** / 1.1 s |

    | **Instagram** | 7.9 s / 15.1 s | 8.0 s / 10.1 s | 8.2 s / 10.8 s |

    | **Snapchat** | 9.5 s / 11.8 s | 3.2 s / 11.8 s | 4.5 s / 7.3 s |

    | **Facebook** | 10.9 s / 26.7 s | 18.1 s / 27.1 s | 7.3 s / 20.4 s |

    | **YouTube** | 17.5 s / 29.4 s | 18.4 s / 27.5 s | 15.6 s / 23.5 s |


    - **TikTok is the fastest** platform across the board (single-post lookups
    under a second).

    - **YouTube and Facebook are the slowest and most variable** — their
    upstream listings are
      heavy, and p95 can approach 30 s.
    - The public HTTP endpoint adds only a small, roughly-constant credit
    reservation/finalize
      overhead (a few Firestore writes, typically tens of milliseconds) on top of these numbers.

    > **Set generous client timeouts.** Use **≥ 30 s** per request (YouTube and
    Facebook can

    > occasionally need it). Scrapes are not cheap round-trips — run them from a
    backend job,

    > and parallelise across handles rather than blocking a user-facing request
    on one call.
servers:
  - url: https://app.sideshift.app/api/v1
    description: Production
security:
  - apiKeyAuth: []
tags:
  - name: Profile
    description: >-
      Profile-level info for a creator (display name, bio, follower/following
      counts, avatar, post count).
  - name: Posts
    description: One page of a creator's recent posts.
  - name: Single Post
    description: One post, looked up by its URL.
  - name: Audience
    description: Audience location data for a creator.
paths:
  /scrape/tiktok/profile:
    post:
      tags:
        - Profile
      summary: TikTok — profile info
      description: >-
        Display name, bio, follower/following counts, profile picture, and post
        count for a creator. 1 credit.
      operationId: scrapeTiktokProfile
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ProfileRequest'
            example:
              username: mrbeast
      responses:
        '200':
          description: Profile info.
          headers:
            X-Scraper-Credits-Charged:
              $ref: '#/components/headers/CreditsCharged'
            X-Scraper-Credits-Remaining:
              $ref: '#/components/headers/CreditsRemaining'
            X-Scraper-RateLimit-Limit:
              $ref: '#/components/headers/RateLimitLimit'
            X-Scraper-RateLimit-Remaining:
              $ref: '#/components/headers/RateLimitRemaining'
            X-Scraper-RateLimit-Reset:
              $ref: '#/components/headers/RateLimitReset'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ScrapeProfileResponse'
              examples:
                sample:
                  $ref: '#/components/examples/TiktokProfileExample'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/InsufficientCredits'
        '404':
          $ref: '#/components/responses/ProfileNotFound'
        '413':
          $ref: '#/components/responses/PayloadTooLarge'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/ServerError'
        '502':
          $ref: '#/components/responses/UpstreamError'
        '504':
          $ref: '#/components/responses/UpstreamTimeout'
components:
  schemas:
    ProfileRequest:
      type: object
      required:
        - username
      properties:
        username:
          type: string
          description: >-
            Creator handle (with or without a leading `@`). YouTube accepts a
            `@handle` or `UC…` channel id.
          example: mrbeast
    ScrapeProfileResponse:
      type: object
      required:
        - data
        - request_id
        - upstream_calls
      properties:
        data:
          type: object
          description: >-
            Profile-level info. Field availability varies by platform — only
            `username` is guaranteed.
          properties:
            username:
              type: string
            display_name:
              type: string
            bio:
              type: string
            follower_count:
              type: integer
              description: On YouTube this is the subscriber count.
            following_count:
              type: integer
            profile_picture:
              type: string
              description: Short-lived signed CDN link.
            post_count:
              type: integer
        request_id:
          type: string
        upstream_calls:
          type: integer
        meta:
          $ref: '#/components/schemas/CreditsMeta'
    CreditsMeta:
      type: object
      description: >-
        Per-request credit accounting (also surfaced in the X-Scraper-Credits-*
        response headers).
      properties:
        credits_charged:
          type: integer
        credits_remaining:
          type: integer
    ScrapeError:
      type: object
      required:
        - error
        - message
        - request_id
      properties:
        error:
          type: string
          enum:
            - INVALID_JSON
            - INVALID_INPUT
            - PAYLOAD_TOO_LARGE
            - UNAUTHORIZED
            - INSUFFICIENT_SCRAPER_CREDITS
            - PROFILE_NOT_FOUND
            - POST_NOT_FOUND
            - SCRAPER_RATE_LIMITED
            - SCRAPER_SYSTEM_BUSY
            - UPSTREAM_ERROR
            - UPSTREAM_TIMEOUT
            - SCRAPER_USAGE_FINALIZE_FAILED
            - INTERNAL_ERROR
        message:
          type: string
          description: Human-readable, SideShift-owned message.
        platform:
          type: string
          enum:
            - tiktok
            - instagram
            - youtube
            - facebook
            - snapchat
            - twitter
            - linkedin
        identifier:
          type: string
          description: The username or URL that was requested, when known.
        field:
          type: string
          description: Offending field on a validation error.
        reason:
          type: string
          description: Validation reason.
        request_id:
          type: string
  headers:
    CreditsCharged:
      schema:
        type: integer
      description: Credits debited for this request.
    CreditsRemaining:
      schema:
        type: integer
      description: Account credit balance after this request.
    RateLimitLimit:
      schema:
        type: integer
      description: >-
        Requests allowed per minute by the limit closest to being reached — your
        account total, or the per-endpoint limit for this endpoint if you have
        one.
    RateLimitRemaining:
      schema:
        type: integer
      description: >-
        Requests you can still make right now against that same limit. It is a
        token bucket, so this refills continuously at the per-minute rate rather
        than jumping back to the full limit at a window edge.
    RateLimitReset:
      schema:
        type: integer
      description: >-
        Unix seconds. On a successful request, one minute out — the bucket's
        cadence. On a 429, when the denied limit will have refilled enough to
        serve the request; prefer the `Retry-After` header, which says the same
        thing in seconds from now.
  examples:
    TiktokProfileExample:
      summary: TikTok profile — @mrbeast
      value:
        data:
          username: mrbeast
          display_name: MrBeast
          bio: Watch my latest video! 👇
          follower_count: 128646264
          following_count: 351
          profile_picture: https://p19-common-sign.tiktokcdn-us.com/…/avatar.webp
          post_count: 458
        request_id: req_0d1e2f3041526374
        upstream_calls: 1
        meta:
          credits_charged: 1
          credits_remaining: 9989
  responses:
    BadRequest:
      description: Invalid JSON or input. No credits are charged.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ScrapeError'
          example:
            error: INVALID_INPUT
            message: 'Invalid username: must match @?[a-zA-Z0-9._-]{1,100}'
            field: username
            reason: must match @?[a-zA-Z0-9._-]{1,100}
            request_id: req_8f3c9a2b1d4e6f70
    Unauthorized:
      description: >-
        Missing or invalid scraper key (or an Integration key was used). No
        credits are charged.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ScrapeError'
          example:
            error: UNAUTHORIZED
            message: >-
              Scraper routes require an independent scraper key from the Scraper
              API dashboard.
            request_id: req_8f3c9a2b1d4e6f70
    InsufficientCredits:
      description: >-
        Not enough scraper credits to run (or finalize) the request. No net
        credits are charged.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ScrapeError'
          example:
            error: INSUFFICIENT_SCRAPER_CREDITS
            message: Insufficient scraper credits
            platform: tiktok
            request_id: req_8f3c9a2b1d4e6f70
    ProfileNotFound:
      description: >-
        The profile is missing, private, restricted, or geo-blocked. Returned by
        the `profile` and `posts` resources. Billed only if the data source
        confirmed the lookup; the message states whether credits were refunded.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ScrapeError'
          example:
            error: PROFILE_NOT_FOUND
            message: Profile not found or unavailable.
            platform: instagram
            identifier: someuser
            request_id: req_8f3c9a2b1d4e6f70
    PayloadTooLarge:
      description: Request body exceeds 32 KB. Rejected before any credit reservation.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ScrapeError'
          example:
            error: PAYLOAD_TOO_LARGE
            message: Request body must be 32KB or smaller
            request_id: req_8f3c9a2b1d4e6f70
    RateLimited:
      description: >
        Two distinct conditions share this status, and the `error` code tells
        them apart.


        `SCRAPER_RATE_LIMITED` — one of YOUR limits is exhausted: either your
        account

        total or, if you have one, this endpoint's own per-minute limit. The

        X-Scraper-RateLimit-* headers report whichever one denied you. Slow
        down, or

        spread the load across endpoints if it was the per-endpoint limit.


        `SCRAPER_SYSTEM_BUSY` — the scraping system is at capacity across all
        customers.

        Nothing about your account is wrong; retry after the `Retry-After`
        interval.


        Neither charges credits: no lookup is performed and the reservation is
        refunded

        in full. `SCRAPER_SYSTEM_BUSY` additionally does not consume your rate
        limit.
      headers:
        Retry-After:
          description: Seconds to wait before retrying. Sent with both 429 codes.
          required: false
          schema:
            type: integer
            minimum: 1
            example: 3
        X-Scraper-RateLimit-Limit:
          $ref: '#/components/headers/RateLimitLimit'
        X-Scraper-RateLimit-Remaining:
          $ref: '#/components/headers/RateLimitRemaining'
        X-Scraper-RateLimit-Reset:
          $ref: '#/components/headers/RateLimitReset'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ScrapeError'
          example:
            error: SCRAPER_RATE_LIMITED
            message: Scraper API rate limit exceeded
            platform: tiktok
            request_id: req_8f3c9a2b1d4e6f70
    ServerError:
      description: >
        An unexpected server error, or the usage could not be finalized after a
        successful

        scrape. When finalization fails, the reservation is refunded in full

        (`SCRAPER_USAGE_FINALIZE_FAILED`); a bare `INTERNAL_ERROR` reflects an
        unexpected

        fault and no net credit change.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ScrapeError'
          example:
            error: SCRAPER_USAGE_FINALIZE_FAILED
            message: Request could not be finalized. Your credits were refunded.
            platform: tiktok
            request_id: req_8f3c9a2b1d4e6f70
    UpstreamError:
      description: The data source failed. The reservation is refunded in full.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ScrapeError'
          example:
            error: UPSTREAM_ERROR
            message: Data source failed. Your credits were refunded.
            platform: youtube
            request_id: req_8f3c9a2b1d4e6f70
    UpstreamTimeout:
      description: The data source timed out. The reservation is refunded in full.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ScrapeError'
          example:
            error: UPSTREAM_TIMEOUT
            message: Data source timed out. Your credits were refunded.
            platform: facebook
            request_id: req_8f3c9a2b1d4e6f70
  securitySchemes:
    apiKeyAuth:
      type: apiKey
      in: header
      name: x-api-key
      description: >-
        Independent Scraper API key (`scrape_live_*`). Generate one in the
        Scraper dashboard.

````