> ## 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.

# Instagram — trial reels

> <span class="ss-credit"><strong>1 credit</strong> per request</span>

Scan for trial reels with detection signals and coverage. This is an estimate from public signals. Check each item's trial_signals.confidence and the scan's coverage before drawing conclusions. A partial scan or an empty page does not prove that an account has no trial reels. Continue with cursor while has_more is true. Platform field names are preserved inside `data`.



## OpenAPI

````yaml /openapi/scraper.yaml post /scrape/instagram/trial-reels
openapi: 3.0.4
info:
  title: SideShift Scraper API
  version: 1.3.0
  description: >
    One scraper API with canonical `/scrape/{platform}/{resource}` paths.
    Normalized

    profile, posts, post, and audience resources remain stable; focused TikTok

    and Instagram operations preserve platform fields when their schemas are
    genuinely

    different. Standard completed lookups cost one credit except TikTok and
    Instagram audience,

    which costs 25. The authenticated Scraper dashboard shows any
    account-specific rates.


    ## Quickstart


    Every endpoint is a POST with a JSON body and your key in the `x-api-key`
    header.


    ```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": [
          {
            "id": "7670566355153833246",
            "title": "The last one was hard 😴",
            "views": 25081146,
            "likes": 2543164,
            "comments": 85606,
            "shares": 47126,
            "bookmarks": 126013,
            "uploadedAt": 1785942939,          // unix seconds
            "postPage": "https://www.tiktok.com/@mrbeast/video/7670566355153833246",
            "creator": "mrbeast",
            "videoUrl": "https://…",           // direct media URL, short-lived
            "thumbnail": "https://…",
            "platform": "tiktok",
            "hashtags": []
          }
          // … the rest of the page
        ],
        "profile_pictures": { "mrbeast": "https://…" },
        "next_cursor": "1768430796798"       // pass back as "cursor" for page 2
      },
      "request_id": "req_k4XjoVoiYdzZ1ibg",
      "upstream_calls": 1,
      "meta": { "credits_charged": 1, "credits_remaining": 104987 }
    }

    ```


    ## 1. Authentication


    Create a key in the [Scraper dashboard](https://app.sideshift.app/scraper)
    under **Keys**.

    The full key is shown once. Send it in the `x-api-key` header on every
    request, and revoke

    it from the dashboard whenever you need to.


    Scraper keys start with `scrape_live_`. Integration API keys (`sk_live_*`,
    `sk_test_*`) are

    rejected with `401` on scraper routes, and scraper keys are rejected on
    integration routes.


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


    ## 2. Credits and billing


    Standard lookups cost one credit. You buy a credit balance up front; there
    is no subscription.

    The authenticated Scraper dashboard shows the exact `post`, `posts`,
    `profile`, and `advanced`

    rates for your account; TikTok and Instagram audience are always 25. You are
    charged when we complete the

    work, and refunded when we can't.


    | Outcome | Charged |

    |---|---|

    | Successful scrape (each page of a listing counts as one) | The account's
    resource rate; standard 1 credit |

    | Confirmed not-found, private or restricted: the platform answered, and the
    account or post isn't there or is hidden from logged-out viewers (`404`) |
    Full rate for the resource: 1 credit, or 25 for audience |

    | TikTok or Instagram audience demographics
    (`/scrape/{tiktok|instagram|youtube}/audience`) | 25 credits |

    | Rejected before we start work: bad input, bad key, empty balance,
    oversized body, over your rate limit (`400` `401` `402` `413` `429`) | Free
    |

    | Our problem or the platform's: errors and timeouts (`500` `502` `504`) |
    Free, auto-refunded |


    A full catalog costs `ceil(total_posts / page_size)` credits, one per page.
    A 300-video

    TikTok account is about 10.


    Instagram `hashtag-search` and `profile-search` page-cursor requests are
    capped at page 10

    by default. Accounts with deep-page access can page to 40, and pages past 10
    bill a

    multiple of the account's `advanced` rate: pages 1-10 at 1x, pages 11-20 at
    2x, and pages

    21-40 at 3x.


    `meta.credits_charged` and `meta.credits_remaining` come back on every
    success. Error bodies

    have no `meta` block, so read the `X-Scraper-Credits-Charged` header to see
    what a billed

    error cost you. Refunds are automatic; you never have to claim one. Top up
    in the

    [Scraper dashboard](https://app.sideshift.app/scraper).


    ## 3. Resources


    | Resource | Path | Returns |

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

    | **Profile** | `POST /scrape/{platform}/profile` | Display name, bio,
    follower/following counts, avatar, post count. |

    | **Posts** | `POST /scrape/{platform}/posts` | One page of the creator's
    recent posts, plus a `next_cursor` where the platform supports one. |

    | **Single Post** | `POST /scrape/{platform}/post` | One post, by URL. |

    | **Audience** | `POST /scrape/{tiktok|instagram|youtube}/audience` |
    Audience distribution by country. 25 credits. |


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


    Normalized profile, posts, post, and TikTok audience endpoints read their
    documented fields

    and ignore the rest. Instagram audience and focused TikTok and Instagram
    operations reject unknown or misspelled

    fields with the JSON error envelope before billing. Bodies over 32 KB return

    `413 PAYLOAD_TOO_LARGE`. Unknown TikTok or Instagram operations return a
    JSON

    `ENDPOINT_NOT_FOUND` response; paths outside the documented platform
    namespaces may return

    the framework's standard HTTP `404`.


    ## 4. Pagination, page size, and speed


    Same request, same response shape everywhere. What differs is how much each
    platform gives

    you per call:


    | Platform | Posts per page | More pages? | `videoUrl` in listings | Speed |

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

    | TikTok | 30 | Yes, cursor | Yes | 2–4 s |

    | Instagram | 12 | Yes, cursor | Yes | 8–15 s |

    | YouTube | 30 | Yes, cursor | No; fetch the single post | 15–45 s |

    | Facebook | Up to 30 | Yes, cursor | Yes | 10–30 s |

    | Snapchat | Up to 30 | No; single page | Yes | 4–10 s |

    | X | 20 | Yes, cursor | Videos only | 3–8 s |

    | LinkedIn | 1–11 | No; single page | Videos only | 4–10 s |


    Listings return video-style content: reels on Instagram and Facebook, Shorts
    by default on

    YouTube (`contentType: video` switches to longform), Spotlights on Snapchat.


    Set your client timeout to 60 seconds, and 90 for YouTube and Facebook. Run
    scrapes from a

    background job, not inside a user request.


    ### Walking a full catalog


    Pass each response's `next_cursor` back as `cursor`. Stop on the cursor, not
    the page size:

    the first page can overshoot, and short pages turn up mid-walk.


    ```js

    const posts = [];

    let cursor;

    do {
      const res = await fetch("https://app.sideshift.app/api/v1/scrape/tiktok/posts", {
        method: "POST",
        headers: { "x-api-key": KEY, "Content-Type": "application/json" },
        body: JSON.stringify(cursor ? { username: "mrbeast", cursor } : { username: "mrbeast" }),
      });
      const { data } = await res.json();
      posts.push(...data.posts);
      cursor = data.next_cursor;
    } while (cursor);

    ```


    Cursors are opaque: pass one back verbatim, and never construct or derive
    one. A stale or

    unrecognised cursor is not an error, so you get a billed page that is not
    where you meant to

    be, and a loop that trusts it may never terminate.


    Snapchat and LinkedIn return a single page and never issue a cursor, so
    their listings are

    only what the platform exposes, not the account's full history. A creator
    with no retrievable

    posts returns `200` with an empty `posts` array, not a `404`, and is billed.


    New posts arrive at the front of page one. After the first backfill, poll
    page one on your

    schedule and keep what you haven't seen; re-fetch known posts only when you
    want updated

    metrics.


    ## 5. Reading the response


    Metrics are cumulative totals as of the moment you called. `uploadedAt` is
    unix seconds.

    `id` is the platform's own post id and is stable, which makes it the right
    key for storing

    and de-duplicating posts. On TikTok, Instagram, and Facebook, `videoUrl` and
    `thumbnail` are

    signed URLs that expire within hours: download the media when you receive
    it, and never

    store the URL. On TikTok photo posts, `videoUrl` points at the post's audio
    track rather

    than a video and nothing in the response flags it, so check the response
    `Content-Type`

    before treating the bytes as video.


    Two fields need care if you work across platforms:


    - `creator` is a lowercase handle on TikTok, Instagram, and LinkedIn,
    canonical case on X,
      and the account's **display name** on YouTube and Facebook (`Coca-Cola`, not `cocacola`).
      Join accounts on the identifier you requested, never on `creator`.
    - `postPage` on a single-post lookup echoes the URL you sent, so the same
    post can produce
      several different `postPage` values. Deduplicate on `id` plus `platform`, never on
      `postPage`.

    Single-post responses carry three keys that listings omit entirely,
    `transcript`,

    `topComments`, and `pinnedComments`, so read them defensively. They are
    filled only when you

    ask for them with `include_transcript` or `include_comments`. Transcripts
    come back on

    **TikTok, YouTube, and Facebook**; comments on **TikTok, YouTube, Facebook,
    and Instagram**.

    Pin state (`isPinned`, `pinnedComments`) is resolved on **YouTube** only and
    is `null`

    elsewhere, never `false`. Snapchat, X, and LinkedIn accept the flags and
    return `null`.

    Neither flag costs extra credits. To check a pin without fetching the post,
    use

    `POST /scrape/youtube/pinned-comment` or `POST
    /scrape/instagram/pinned-comment`.


    Profile responses return `username`, `display_name`, and `follower_count` on
    all seven

    platforms. Everything else depends on what the source exposes, and
    unavailable fields are

    usually omitted rather than returned as `null`, so test for a usable value
    rather than for

    key presence.


    Field-level types, per-platform gaps, and the metrics that are structurally
    always `0` are

    in the `UnifiedPost` and `ScrapeProfileResponse` schemas below.


    ## 6. Errors and refunds


    Every error is JSON with an `error` code, a human-readable `message`, and a
    `request_id` to

    quote at support.


    | Code | What happened | What to do |

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

    | `PROFILE_NOT_FOUND`<br>`POST_NOT_FOUND` | The platform confirmed it
    doesn't exist. Billed, because it was a real lookup. | Remove the identifier
    from your schedule. Retrying it costs a credit every time. |

    | `PROFILE_PRIVATE` | The account exists, but its owner made it private. It
    is not deleted. Billed, because it was a real lookup. | Do not treat it as
    deleted. Read it through the creator's own platform login, or check again
    after the owner makes it public. Retrying soon gives the same answer and
    costs a credit. |

    | `PROFILE_RESTRICTED` | The account exists, but the platform hides it from
    logged-out viewers, for example by age or region. It is not deleted. Billed,
    because it was a real lookup. | Do not treat it as deleted. Read it through
    the creator's own platform login, or check again on a later day. Retrying
    soon gives the same answer and costs a credit. |

    | `POST_PRIVATE`<br>`POST_RESTRICTED` | The post exists, but its account is
    private (`POST_PRIVATE`) or the platform hides it from logged-out viewers,
    for example by age (`POST_RESTRICTED`). It is not deleted. Billed, because
    it was a real lookup. Returned by Instagram post lookups. | Do not treat it
    as deleted. Tell the creator the post is not public. Retrying soon gives the
    same answer and costs a credit. |

    | `AUDIENCE_UNAVAILABLE` | `audience` only. The account exists, but its
    public engagement is not enough to estimate an audience. It is not deleted.
    Billed when the estimate ran and the account has too little engagement;
    refunded when the signals were inconclusive (the `message` says so). | Do
    not treat it as deleted. Check again after the account has more recent
    activity, for example in a week. Retrying soon gives the same answer. |

    | `UPSTREAM_ERROR`<br>`UPSTREAM_TIMEOUT` | The platform failed or timed out.
    Refunded. | Retry with backoff: 30 s, then a few minutes. YouTube and
    Facebook throw these the most. |

    | `SCRAPER_RATE_LIMITED` | One of *your* limits is exhausted. The
    `X-Scraper-RateLimit-*` headers report the one that denied you. | Wait for
    `Retry-After`. If you hit this steadily, ask us to raise your limit. |

    | `SCRAPER_SYSTEM_BUSY` | *Our* capacity, nothing about your account.
    Refunded, and it does not consume your rate limit. | Wait for `Retry-After`
    and retry. |

    | `INSUFFICIENT_SCRAPER_CREDITS` | Balance is empty. Nothing charged. | Top
    up in the dashboard. `meta.credits_remaining` is on every success for
    alerting. |

    | `INVALID_INPUT` | The body failed validation. The `message` names the
    field and why. | Fix and resend. |


    The retry rule in one line: **retry refunded errors, never retry billed ones
    on a loop, and

    always honour `Retry-After`.**


    Not every `5xx` body is JSON. A request that outruns the roughly 120-second
    server ceiling

    is terminated by the gateway and returns a bare `504` with a `text/plain`
    body; its

    reservation is reclaimed within about ten minutes rather than at the moment
    it fails.


    ### A retry wrapper you can copy


    The wrapper below is the retry rule as code: it honours `Retry-After` on a
    `429`, backs

    off with jitter on refunded `5xx` errors, and gives up immediately on
    everything else.

    Route every call through it and both kinds of transient failure stop
    reaching your code.


    ```js

    async function scrape(url, body, attempts = 5) {
      for (let attempt = 1; ; attempt++) {
        const res = await fetch(url, {
          method: "POST",
          headers: { "x-api-key": KEY, "Content-Type": "application/json" },
          body: JSON.stringify(body),
        });
        if (res.ok) return (await res.json()).data;

        // 4xx other than 429 is deterministic (bad input, bad key, billed not-found):
        // retrying repeats the same answer, and on a 404 it repeats the same charge.
        const retriable = res.status === 429 || res.status >= 500;
        if (!retriable || attempt === attempts) throw new Error(await res.text());

        // A 429 says exactly when to come back. Refunded 5xx errors don't, so back off:
        // 30 s, 60 s, 2 min, 4 min. Jitter keeps parallel workers from retrying in step.
        const retryAfter = Number(res.headers.get("Retry-After"));
        const waitMs = retryAfter > 0
          ? retryAfter * 1000
          : Math.min(30_000 * 2 ** (attempt - 1), 300_000);
        await new Promise((resolve) => setTimeout(resolve, waitMs + Math.random() * 1000));
      }
    }


    const profile = await scrape(
      "https://app.sideshift.app/api/v1/scrape/tiktok/profile",
      { username: "mrbeast" },
    );

    ```


    It slots straight into the catalog walk in section 4: swap the raw `fetch`
    for

    `scrape(...)` and the loop rides out rate limits and platform hiccups
    unattended.


    ## 7. Rate limits


    New accounts start at 120 requests per minute across all endpoints. You may
    additionally have a

    per-endpoint limit. The account total is shared, so a request can be denied
    by the total

    while its own endpoint still has allowance. Both are token buckets: you can
    spend a minute's

    allowance in one burst, and it refills continuously rather than at a window
    edge.


    `X-Scraper-RateLimit-Limit`, `-Remaining`, and `-Reset` come back on every
    `200` and every

    `429`, reporting whichever limit is closest to being reached. Prefer
    `Retry-After` on a

    `429`: `-Reset` is computed when your request is admitted, so on a slow
    platform it can

    already be in the past by the time you read it.


    In almost all cases your limit is exactly what you get: stay under your rpm
    and your

    requests are admitted. The exception is the moment the scraping system as a
    whole is

    saturated across all customers, when a request can be turned away with

    `SCRAPER_SYSTEM_BUSY` even though your own buckets still have tokens. That
    answer is

    free, refunded, and carries a `Retry-After`; the wrapper in section 6
    absorbs it without

    any extra code. Treat it as something your client retries automatically, not
    an outage.


    For production volume, contact support to raise your limits.
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.
  - name: TikTok · Creator
    description: TikTok · Creator focused platform operations.
  - name: TikTok · Sound
    description: TikTok · Sound focused platform operations.
  - name: TikTok · Hashtag
    description: TikTok · Hashtag focused platform operations.
  - name: TikTok · Playlist
    description: TikTok · Playlist focused platform operations.
  - name: TikTok · Collection
    description: TikTok · Collection focused platform operations.
  - name: TikTok · Discovery
    description: TikTok · Discovery focused platform operations.
  - name: TikTok · Comments
    description: TikTok · Comments focused platform operations.
  - name: TikTok · Media
    description: TikTok · Media focused platform operations.
  - name: TikTok · Ads
    description: TikTok · Ads focused platform operations.
  - name: Instagram · Profile
    description: Instagram · Profile focused platform operations.
  - name: Instagram · Posts
    description: Instagram · Posts focused platform operations.
  - name: Instagram · Comments
    description: Instagram · Comments focused platform operations.
  - name: Instagram · Discovery
    description: Instagram · Discovery focused platform operations.
  - name: Instagram · Audio
    description: Instagram · Audio focused platform operations.
  - name: Instagram · Stories
    description: Instagram · Stories focused platform operations.
  - name: Instagram · Transcript
    description: Instagram · Transcript focused platform operations.
  - name: Instagram · Trial reels
    description: Instagram · Trial reels focused platform operations.
paths:
  /scrape/instagram/trial-reels:
    post:
      tags:
        - Instagram · Trial reels
      summary: Instagram — trial reels
      description: >-
        <span class="ss-credit"><strong>1 credit</strong> per request</span>


        Scan for trial reels with detection signals and coverage. This is an
        estimate from public signals. Check each item's trial_signals.confidence
        and the scan's coverage before drawing conclusions. A partial scan or an
        empty page does not prove that an account has no trial reels. Continue
        with cursor while has_more is true. Platform field names are preserved
        inside `data`.
      operationId: scrapeInstagramTrialReels
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PlatformInstagramTrialReelsRequest'
            example:
              username: jakecohen
      responses:
        '200':
          description: >-
            Completed lookup. Standard cost: 1 SideShift credit;
            account-specific pricing, when present, is shown in the Scraper
            dashboard and returned in the billing headers.
          headers:
            X-Scraper-Credits-Charged:
              description: Credits debited for this completed lookup (normally 1).
              schema:
                type: integer
                example: 1
            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/PlatformInstagramTrialReelsResponse'
              examples:
                live:
                  summary: Live data captured 2026-09-15
                  description: >-
                    The data object comes from a successful live request using
                    the documented input. Arrays are shortened to representative
                    items, long strings are trimmed, contact fields are
                    redacted, and transient URL query strings are removed. The
                    SideShift envelope values are representative.
                  value:
                    data:
                      success: true
                      mode: only
                      user:
                        pk: '35005786'
                        username: jakecohen
                        full_name: Jake Cohen
                        is_private: false
                      items:
                        - id: 3986802981985706920_35005786
                          pk: 3986802981985707000
                          code: DdT-DBqueOo
                          url: https://www.instagram.com/reel/DdT-DBqueOo/
                          taken_at: 1789484190
                          media_type: 2
                          product_type: clips
                          like_count: 541
                          comment_count: 14
                          play_count: 30096
                          ig_play_count: 30096
                          view_count: null
                          like_and_view_counts_disabled: false
                          caption: >-
                            BLACK AND WHITES WAITING FOR THEIR SPRINKLES!!!
                            Something sweet to break the fast, shipping
                            nationwide on @goldbelly!!! Makes a greatttttt host
                            gift!
                          hashtags: []
                          mentions:
                            - goldbelly
                          tagged_users:
                            - goldbelly
                          coauthors: []
                          video_url: >-
                            https://scontent-phl2-1.cdninstagram.com/o1/v/t16/f2/m84/AQMXBONawUJpCySBO-WqDMK5UMJCisUDwkLYXAb3p8w8sFBpob2gZG-m7gISB7j9cG4-PU2kT-yC6y6RCccZM4cSV8MlCwJcerjsbwA.mp4
                          video_duration: 12.071999549865723
                          display_url: >-
                            https://scontent-phl2-1.cdninstagram.com/v/t51.82787-15/811616505_18630929578061787_2137977985873584899_n.jpg
                          width: 1080
                          height: 1920
                          has_audio: true
                          music:
                            title: bubble fountain
                            artist: frutiger pm
                            audio_cluster_id: '816677584255468'
                            audio_url: >-
                              https://scontent-phl2-1.cdninstagram.com/o1/v/t2/f2/m69/AQOHMrravWfUYYiHB4gmB9gbvfDwvNoAHamQcJSuDMf1FhM_LvXsgQw-sz3LCGQQreZrWi53RhUcHdZmFavAn8oh.mp4
                          original_audio_title: null
                          location_name: null
                          location_id: null
                          is_paid_partnership: false
                          sponsor_usernames: []
                          comments_disabled: null
                          is_pinned: false
                          owner:
                            pk: 35005786
                            username: jakecohen
                            full_name: Jake Cohen
                            is_verified: true
                            is_private: false
                            profile_pic_url: >-
                              https://scontent-phl2-1.cdninstagram.com/v/t51.82787-19/532388261_18524592955061787_5989065863176088367_n.jpg
                          is_trial_reel: true
                          trial_reel_reason: absent_from_profile_grid
                          trial_signals:
                            absent_from_profile_grid: true
                            instagram_awaiting_distribution: true
                            confidence: high
                            grid_insertion_drift: null
                            added_to_grid_late: null
                      num_results: 6
                      trial_reel_count: 6
                      reels_examined: 36
                      coverage:
                        status: complete
                        reason: null
                        reels_examined: 36
                        reels_unverified: 0
                        reels_indeterminate: 0
                        graduated_from_trial_candidates: 0
                        reels_tab_pages: 3
                        profile_grid_pages: 5
                        profile_grid_exhausted: false
                        profile_grid_verified_through: 1779632703
                        oldest_reel_in_window: 1781629183
                        profile_grid_usage:
                          reels_on_grid: 20
                          reels_off_grid: 6
                          cross_post_rate: 0.769
                          grid_is_informative: true
                      has_more: true
                      cursor: >-
                        eJxdkltvGkEMhf9KtM998G3GdqW8UEElJJByYaGtIkSBJqTctASSUPW_x9DdNurbzDfW8fEZ_yoOxccL-nBR7HfzaryYxa3gBJDUchF4ulxsd-PpvtptqtPbVdn5dAsPm367J1_AXwfr_uHrqtOdlMvR7ai7_f7zqRyRHXtlv9NbT6l37OyvHsvR9WNrP-Du8Wr18DJfth5v2vfHYbuby_bD6Gb4shqsnmk26pY97LZuhtun4ect9277_RLs9fr58vLk5b5azN5ZYXNKRJyVkydCBhy_t34un23W8yj-MVnu5n8lNod5NZ-N9-unxTIeUdUzkwKfBp5U1WvAbwU7ekJIRk7KOaP6STaw5ZwpiauSuGiqsTBldM85xcGlwZgVScVZSJI5NziFroHl6A1mTTWoxNUhoYgo0x8MaupEKNGBYkRuMLhGKYcbZmescWbJEYNJds4EVuOUwdkwaULT6FVjwSysWRVd3KDBfI4SxSiSBmpESFGTMWM4kYSNQYwxTDSCIiJhbzCDkkW0lCLAJGds7pbCCAgxEoHmGmvkypGcR9MIQBsclYhwDp6lNmgef4CZmUOZ8V91SmFDGAUVslujLRDtjNTCdAKCGv-_QsVd8O1ivat3wBjjO6NpbIOGRC7ufr8BF1HAew==
                      reels_tab_exhausted: false
                    request_id: req_8f3c9a2b1d4e6f70
                    upstream_calls: 1
                    meta:
                      credits_charged: 1
                      credits_remaining: 9998
        '400':
          $ref: '#/components/responses/PlatformOperationBadRequest'
        '401':
          $ref: '#/components/responses/PlatformOperationUnauthorized'
        '402':
          $ref: '#/components/responses/PlatformOperationInsufficientCredits'
        '404':
          $ref: '#/components/responses/PlatformOperationNotFound'
        '413':
          $ref: '#/components/responses/PlatformOperationPayloadTooLarge'
        '429':
          $ref: '#/components/responses/PlatformOperationRateLimited'
        '500':
          $ref: '#/components/responses/PlatformOperationServerError'
        '502':
          $ref: '#/components/responses/PlatformOperationDataSourceError'
        '504':
          $ref: '#/components/responses/PlatformOperationDataSourceTimeout'
components:
  schemas:
    PlatformInstagramTrialReelsRequest:
      type: object
      additionalProperties: false
      properties:
        username:
          type: string
          description: Instagram username without a profile URL.
        cursor:
          type: string
          description: Opaque continuation token returned by the previous scan.
          maxLength: 32768
      required:
        - username
    PlatformInstagramTrialReelsResponse:
      type: object
      required:
        - data
        - request_id
        - upstream_calls
        - meta
      properties:
        data:
          $ref: '#/components/schemas/PlatformInstagramTrialReelsData'
        request_id:
          type: string
          description: SideShift request id. Include it in support requests.
        upstream_calls:
          type: integer
          enum:
            - 1
          description: Number of data-source lookups performed.
        meta:
          $ref: '#/components/schemas/CreditsMeta'
    PlatformInstagramTrialReelsData:
      type: object
      description: Instagram platform data payload. Field names and nesting are preserved.
      additionalProperties: true
      properties:
        success:
          type: boolean
          description: Whether the scan succeeded.
        mode:
          type: string
          description: The scan mode returned by the data source.
        user:
          type: object
          description: Account identity.
          additionalProperties: true
          properties:
            full_name:
              type: string
              nullable: true
            is_private:
              type: boolean
              nullable: true
            pk:
              type: string
            username:
              type: string
        items:
          type: array
          description: Reels, metrics, trial flags, reasons, and confidence signals.
          items:
            type: object
            additionalProperties: true
            properties:
              caption:
                type: string
              coauthors:
                type: array
                items:
                  type: string
              code:
                type: string
              comment_count:
                type: number
              comments_disabled:
                nullable: true
              display_url:
                type: string
              has_audio:
                type: boolean
              hashtags:
                type: array
                items: {}
              height:
                type: number
              id:
                type: string
              ig_play_count:
                type: number
              is_paid_partnership:
                type: boolean
              is_pinned:
                type: boolean
              is_trial_reel:
                type: boolean
              like_and_view_counts_disabled:
                type: boolean
              like_count:
                type: number
              location_id:
                nullable: true
              location_name:
                nullable: true
              media_type:
                type: number
              mentions:
                type: array
                items:
                  type: string
              music:
                type: object
                additionalProperties: true
                properties:
                  artist:
                    type: string
                  audio_cluster_id:
                    type: string
                  audio_url:
                    type: string
                  title:
                    type: string
                nullable: true
              original_audio_title:
                type: string
                nullable: true
              owner:
                type: object
                additionalProperties: true
                properties:
                  full_name:
                    type: string
                  is_private:
                    type: boolean
                  is_verified:
                    type: boolean
                  pk:
                    type: number
                  profile_pic_url:
                    type: string
                  username:
                    type: string
              pk:
                type: number
              play_count:
                type: number
              product_type:
                type: string
              sponsor_usernames:
                type: array
                items: {}
              tagged_users:
                type: array
                items:
                  type: string
              taken_at:
                type: number
              trial_reel_reason:
                type: string
              trial_signals:
                type: object
                additionalProperties: true
                properties:
                  absent_from_profile_grid:
                    type: boolean
                  added_to_grid_late:
                    nullable: true
                  confidence:
                    type: string
                  grid_insertion_drift:
                    nullable: true
                  instagram_awaiting_distribution:
                    type: boolean
              url:
                type: string
              video_duration:
                type: number
              video_url:
                type: string
              view_count:
                nullable: true
              width:
                type: number
        num_results:
          type: integer
          description: Number of results in this page.
        trial_reel_count:
          type: integer
          description: Trial reels found in this scan.
        reels_examined:
          type: integer
          description: Reels checked in this scan.
        coverage:
          type: object
          description: Scan completeness and grid comparison evidence.
          additionalProperties: true
          properties:
            graduated_from_trial_candidates:
              type: number
            oldest_reel_in_window:
              type: number
            profile_grid_exhausted:
              type: boolean
            profile_grid_pages:
              type: number
            profile_grid_usage:
              type: object
              additionalProperties: true
              properties:
                cross_post_rate:
                  type: number
                grid_is_informative:
                  type: boolean
                reels_off_grid:
                  type: number
                reels_on_grid:
                  type: number
            profile_grid_verified_through:
              type: number
            reason:
              nullable: true
            reels_examined:
              type: number
            reels_indeterminate:
              type: number
            reels_tab_pages:
              type: number
            reels_unverified:
              type: number
            status:
              type: string
        has_more:
          type: boolean
          description: Whether more pages can be requested.
        cursor:
          type: string
          description: Opaque continuation token.
        reels_tab_exhausted:
          type: boolean
          description: Whether the reels tab was exhausted.
    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
            - PROFILE_PRIVATE
            - PROFILE_RESTRICTED
            - AUDIENCE_UNAVAILABLE
            - POST_NOT_FOUND
            - POST_PRIVATE
            - POST_RESTRICTED
            - RESOURCE_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:
    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. Computed when the request is admitted, not when the
        response is written, so on a success it reads about a minute out MINUS
        the call's latency — on a slow platform it can already be in the past by
        the time you read it. 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.
    CreditsCharged:
      schema:
        type: integer
      description: Credits debited for this request.
  responses:
    PlatformOperationBadRequest:
      description: Invalid JSON or request fields. No credits are charged.
      headers:
        X-Scraper-Credits-Charged:
          $ref: '#/components/headers/CreditsCharged'
        X-Scraper-Credits-Remaining:
          $ref: '#/components/headers/CreditsRemaining'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ScrapeError'
          example:
            error: INVALID_INPUT
            message: Invalid request body.
            request_id: req_8f3c9a2b1d4e6f70
    PlatformOperationUnauthorized:
      description: Missing or invalid SideShift scraper key. No credits are charged.
      headers:
        X-Scraper-Credits-Charged:
          $ref: '#/components/headers/CreditsCharged'
        X-Scraper-Credits-Remaining:
          $ref: '#/components/headers/CreditsRemaining'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ScrapeError'
          example:
            error: UNAUTHORIZED
            message: Invalid scraper API key.
            request_id: req_8f3c9a2b1d4e6f70
    PlatformOperationInsufficientCredits:
      description: Insufficient scraper credits. No credits are charged.
      headers:
        X-Scraper-Credits-Charged:
          $ref: '#/components/headers/CreditsCharged'
        X-Scraper-Credits-Remaining:
          $ref: '#/components/headers/CreditsRemaining'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ScrapeError'
          example:
            error: INSUFFICIENT_SCRAPER_CREDITS
            message: Insufficient scraper credits.
            request_id: req_8f3c9a2b1d4e6f70
    PlatformOperationNotFound:
      description: >-
        The requested public resource was not found or is unavailable
        (`RESOURCE_NOT_FOUND`), or it exists but is not public:
        `PROFILE_PRIVATE`, `PROFILE_RESTRICTED`, `POST_PRIVATE` or
        `POST_RESTRICTED` (not deleted). A completed lookup is billed at the
        endpoint rate.
      headers:
        X-Scraper-Credits-Charged:
          $ref: '#/components/headers/CreditsCharged'
        X-Scraper-Credits-Remaining:
          $ref: '#/components/headers/CreditsRemaining'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ScrapeError'
          example:
            error: RESOURCE_NOT_FOUND
            message: Requested resource not found or unavailable.
            request_id: req_8f3c9a2b1d4e6f70
    PlatformOperationPayloadTooLarge:
      description: Request body exceeds 32 KB. No credits are charged.
      headers:
        X-Scraper-Credits-Charged:
          $ref: '#/components/headers/CreditsCharged'
        X-Scraper-Credits-Remaining:
          $ref: '#/components/headers/CreditsRemaining'
      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
    PlatformOperationRateLimited:
      description: >-
        Rate limit or system-capacity limit. No credits are charged; honor
        Retry-After.
      headers:
        X-Scraper-Credits-Charged:
          $ref: '#/components/headers/CreditsCharged'
        X-Scraper-Credits-Remaining:
          $ref: '#/components/headers/CreditsRemaining'
        Retry-After:
          description: Seconds to wait before retrying.
          schema:
            type: integer
            minimum: 1
        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.
            request_id: req_8f3c9a2b1d4e6f70
    PlatformOperationServerError:
      description: Unexpected SideShift error. Credits are refunded.
      headers:
        X-Scraper-Credits-Charged:
          $ref: '#/components/headers/CreditsCharged'
        X-Scraper-Credits-Remaining:
          $ref: '#/components/headers/CreditsRemaining'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ScrapeError'
          example:
            error: INTERNAL_ERROR
            message: Request failed. Your credits were refunded.
            request_id: req_8f3c9a2b1d4e6f70
    PlatformOperationDataSourceError:
      description: The data source failed. Credits are refunded.
      headers:
        X-Scraper-Credits-Charged:
          $ref: '#/components/headers/CreditsCharged'
        X-Scraper-Credits-Remaining:
          $ref: '#/components/headers/CreditsRemaining'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ScrapeError'
          example:
            error: UPSTREAM_ERROR
            message: Data source failed. Your credits were refunded.
            request_id: req_8f3c9a2b1d4e6f70
    PlatformOperationDataSourceTimeout:
      description: The data source timed out. Credits are refunded.
      headers:
        X-Scraper-Credits-Charged:
          $ref: '#/components/headers/CreditsCharged'
        X-Scraper-Credits-Remaining:
          $ref: '#/components/headers/CreditsRemaining'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ScrapeError'
          example:
            error: UPSTREAM_TIMEOUT
            message: Data source timed out. Your credits were refunded.
            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.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.