# Break down what works for a set of Creators

> For the complete documentation index, see [llms.txt](https://www.shortsintel.com/llms.txt). Every docs page is also available as markdown: add .md to its URL. To connect, read [agents.md](https://www.shortsintel.com/agents.md).

POST `/v1/breakdowns`

Research on Creators, not keywords: analyses each Creator's latest `videos_per_creator` Videos into one Digest of what works for them. A Video's `outlier` is its views over its Creator's median in the Run; lift tables use those ratios (`lift_tables.basis: creator_median`), so size does not skew them. Keyword fields are refused. Read the Run at `/v1/research/{id}`.

[Open in Playground](https://www.shortsintel.com/console/playground?op=createBreakdown&videos_per_creator=25) (no API Key needed)

## Price

9 credits per Creator plus 20 credits per analysed Video, fresh or stored; at most 4,090 credits. Estimate and 402 check assume every Video is analysed; failed ones are free.

Supports `"estimate": true` in the body: the reply is the price in Credits and whether it would be `cached`, with no Run created and nothing charged.

A cached result costs the same as a fresh one; `cached` says which you got.

## Parameters

| Name | In | Type | Required | Default | Description |
| --- | --- | --- | --- | --- | --- |
| `Idempotency-Key` | header | `string` | Optional | — | 1-255 printable ASCII characters. Replaying this key with the same payload returns the original Run (202 while it is still running); a different payload, or a different operation, is a 409 `idempotency_conflict`. |

## Request body

`application/json`

object

- `creators` array of any of 2 shapes items 1 to 10 1 to 10 Creators of any Platforms, each named once. Or `collection_id`.
  - Option 1 object
    - `platform` string required one of `tiktok`, `instagram`, `youtube`, `facebook` The Creator's Platform.
    - `handle` string required length 1 to 200 The Creator's handle, with or without `@`.
  - Option 2 string A profile URL.
- `collection_id` string Or a Creator Collection, frozen at Run start.
- `videos_per_creator` integer value 1 to 50 Default `25` Recent Videos to read and analyse per Creator. Default 25, max 50; Creators × videos_per_creator at most 200.
- `intent` string length 1 to 1000 Drops off-intent Videos read, unanalysed and free. None is read in their place. At most 1000 characters.
- `wait` number value 0 to 120 Default `30` Seconds to hold the response open before returning a running Run to poll. Default 30, max 120.
- `max_credits` integer value at least 0 As in the query.
- `estimate` boolean If a source the call would read is unavailable and the cache cannot answer, the estimate adds `unavailable` (the `{ platform, capability }` parts it cannot reach), or is a 503 `source_unavailable` when it can reach none.

## Responses

### 200 OK

The Run finished: a research result with `mode: creators`.

any of 2 shapes

- Option 1 object The envelope every charged operation answers with: `{ run }` while the Run is running (plus `poll_url` on the 202), joined by `result` once it completes or `error` once it fails.
  - `run` object required Every charged operation creates a Run. The envelope is identical whether the result came back inline or was polled.
    - `id` string required
    - `kind` string required What was run, e.g. `video_lookup`.
    - `status` string required one of `running`, `completed`, `partial`, `failed` `partial` keeps the completed items and charges for them, up to what the Balance covers; `failed` charges nothing.
    - `created_at` string required RFC 3339.
    - `completed_at` string required nullable Null while the Run is still running.
    - `charge_credits` integer required What this Run actually cost, in Credits. A stored answer costs the same as a fresh one.
    - `list_price_credits` integer required The full price of the work asked for, in Credits. Equal to `charge_credits` unless the Run charged for only part of it.
    - `cached` boolean required True when a stored result answered without a fresh fetch.
    - `progress` object What a running Research has done so far, refreshed as it works: its `stage` and the counts known by then, such as `analysed` of `shortlisted`. A count is absent until its stage produced it. Absent once the Run settles, and on every other kind.
      - `stage` string required one of `keywords`, `searching`, `reading_creators`, `filtering`, `analysing`, `writing_digest`
      - `keywords` integer value at least 0
      - `creators` integer value at least 0
      - `creators_read` integer value at least 0
      - `searches` integer value at least 0
      - `found` integer value at least 0
      - `judged` integer value at least 0
      - `kept` integer value at least 0
      - `dropped_off_topic` integer value at least 0
      - `dropped_unsure` integer value at least 0
      - `shortlisted` integer value at least 0
      - `analysed` integer value at least 0
      - `updated_at` string required
  - `result` object required
    - `digest_version` integer required
    - `mode` string one of `keywords`, `creators` What the research was seeded with: `keywords` (an intent) or `creators` (a breakdown). Absent means `keywords`.
    - `intent` string
    - `keywords` array of string
    - `keyword_rationale` string
    - `coverage` map of string required
    - `digest` object required
      - `summary` string required Two or three sentences answering the intent directly: what is working on this topic right now, across the videos analysed.
      - `themes` array of object required items 1 to 8
        - `name` string required Short label, three or four words.
        - `share` number required value 0 to 1 Fraction of the analysed videos that belong to this theme, 0-1.
        - `description` string required What the videos in this theme have in common.
        - `video_ids` array of string required `video_id` keys (platform:id) of the analysed videos that belong to this theme, exactly as given in the evidence.
      - `hooks` array of object required items at most 12
        - `text` string required The opening line, quoted VERBATIM from the evidence. Never paraphrase, never invent.
        - `video_id` string required `video_id` key (platform:id) of the video this hook is quoted from, exactly as given in the evidence.
        - `hook_type` string required The kind of hook, e.g. question, claim, negation, stat.
        - `why_it_works` string required One sentence.
        - `template` string length at least 1 The verbatim hook with its specifics (product, number, mistake, outcome, audience) replaced by short bracketed slots, every other word kept as spoken, e.g. "Stop [mistake] if you want [outcome]".
        - `lift` object nullable The `lift_tables.hook_type` row for the quoted video's analysed hook type, attached in code (never written by the model). Null when that video has no analysed hook type.
          - `value` string required The analysed hook type, or format + hook type, the row is keyed by.
          - `video_count` integer required
          - `median_views` number required nullable
          - `median_engagement_rate` number required nullable
          - `views_lift` number required nullable
          - `engagement_lift` number required nullable
          - `low_confidence` boolean required
      - `sentiment` object required
        - `positive` number required value 0 to 1
        - `neutral` number required value 0 to 1
        - `negative` number required value 0 to 1
        - `note` string required One sentence on what drives the distribution.
      - `angles` array of object required items 1 to 8
        - `angle` string required A content angle worth trying.
        - `rationale` string required Why the evidence supports it, referencing what was seen.
      - `formulas` array of object items at most 5
        - `name` string required Short label, three to five words.
        - `recipe` string required One line saying how to make it: the format, how it opens, and the order of its beats.
        - `visual_format` string required The visual format, copied exactly as it appears in `lift_tables.visual_format`.
        - `hook_type` string required The hook type, copied exactly as it appears in `lift_tables.hook_type`.
        - `structure` string required The beat structure the example videos share, as short beats joined by arrows, e.g. "problem → demo → result → CTA".
        - `video_ids` array of string required Up to 3 `video_id` keys (platform:id) of analysed videos that follow this formula, best performing first, exactly as given in the evidence.
        - `lift` object required nullable The `lift_tables.format_hook` row for this formula's visual format and hook type, attached in code (never written by the model): its `video_count` and lifts. The lift is for the format + hook type pair only; the structure is not bucketed. Null when no analysed video has that pair.
          - `value` string required The analysed hook type, or format + hook type, the row is keyed by.
          - `video_count` integer required
          - `median_views` number required nullable
          - `median_engagement_rate` number required nullable
          - `views_lift` number required nullable
          - `engagement_lift` number required nullable
          - `low_confidence` boolean required
      - `gaps` array of object items at most 5 Up to 5 things the audience asks for in the top 10 videos' comments that no analysed video addresses, each with verbatim quoted comments (and the video they came from) and a suggested angle. Empty when no comments were available. Absent on research recorded before gaps existed.
        - `ask` string required What the audience asks for or complains about, in one sentence, in your own words.
        - `comments` array of object required items 1 to 3
          - `text` string required A comment quoted VERBATIM from <comments>, character for character. Never paraphrase, never invent.
          - `video_id` string required `video_id` key (platform:id) of the video this comment was left under, exactly as given in <comments>.
        - `angle` string required A video someone could make that answers this ask, in one sentence.
    - `lift_tables` object Lift tables computed in code over the analysed videos: per visual format, hook type and length bucket, the video count, median views and engagement rate, and lift versus the whole set's median. The Digest quotes lifts only from these. Absent on research recorded before they existed.
      - `basis` string one of `set_median`, `creator_median`
      - `set` object required
        - `video_count` integer required
        - `median_views` number required nullable
        - `median_engagement_rate` number required nullable
      - `visual_format` array of object required
        - `value` string required The visual format, hook type, length bucket, or `<format> + <hook type>`.
        - `video_count` integer required
        - `median_views` number required nullable
        - `median_engagement_rate` number required nullable (likes + comments + shares + saves) / views.
        - `views_lift` number required nullable Median views of this row over the whole set's median views. Null when the set median is 0 or unknown.
        - `engagement_lift` number required nullable
        - `low_confidence` boolean required True when the row rests on fewer than 3 videos.
      - `hook_type` array of object required
        - `value` string required The visual format, hook type, length bucket, or `<format> + <hook type>`.
        - `video_count` integer required
        - `median_views` number required nullable
        - `median_engagement_rate` number required nullable (likes + comments + shares + saves) / views.
        - `views_lift` number required nullable Median views of this row over the whole set's median views. Null when the set median is 0 or unknown.
        - `engagement_lift` number required nullable
        - `low_confidence` boolean required True when the row rests on fewer than 3 videos.
      - `length` array of object required
        - `value` string required The visual format, hook type, length bucket, or `<format> + <hook type>`.
        - `video_count` integer required
        - `median_views` number required nullable
        - `median_engagement_rate` number required nullable (likes + comments + shares + saves) / views.
        - `views_lift` number required nullable Median views of this row over the whole set's median views. Null when the set median is 0 or unknown.
        - `engagement_lift` number required nullable
        - `low_confidence` boolean required True when the row rests on fewer than 3 videos.
      - `format_hook` array of object Visual format and hook type together, `value` as `<format> + <hook type>`: the rows the Digest's formulas take their lift from. Absent on research recorded before it existed.
        - `value` string required The visual format, hook type, length bucket, or `<format> + <hook type>`.
        - `video_count` integer required
        - `median_views` number required nullable
        - `median_engagement_rate` number required nullable (likes + comments + shares + saves) / views.
        - `views_lift` number required nullable Median views of this row over the whole set's median views. Null when the set median is 0 or unknown.
        - `engagement_lift` number required nullable
        - `low_confidence` boolean required True when the row rests on fewer than 3 videos.
    - `breakdown` object Breakdown only: the Creators read and how many Videos each returned, and those that could not be read.
      - `videos_per_creator` integer required Recent Videos asked for per Creator.
      - `creators` array of object required Every Creator read, in request order, each charged its fee.
        - `platform` string required
        - `handle` string required
        - `id` string required
        - `videos_read` integer required Videos read; fewer than `videos_per_creator` when `ended`.
        - `ended` boolean required True when the Creator's feed ran out before `videos_per_creator`.
      - `failed` array of object Creators that could not be read, charged 0, each with its problem `code` and `detail`. The Run is then partial.
        - `platform` string required
        - `handle` string required
        - `code` string required
        - `detail` string required
      - `lift_videos` integer Analysed Videos with a view count, of `analysed_videos`: the ranking and lift basis. The rest are unscored.
      - `analysed_videos` integer Videos analysed, of all the Creators read.
    - `videos` array of object required The top 25 videos of the ranked shortlist, in rank order. Every video the research delivered pages, with filters and sorting, at `GET /v1/research/{id}/videos`.
      - `platform` string required
      - `id` string required
      - `url` string required nullable
      - `description` string required
      - `posted_at` string required nullable
      - `duration_seconds` number required nullable
      - `thumbnail_url` string required nullable
      - `hashtags` array of string required
      - `sound` object required nullable
        - `id` string required
        - `title` string required nullable
        - `author` string required nullable Who made the sound, as the platform names them. Null when the platform did not say (Facebook never does).
        - `is_original` boolean required nullable True for the creator's own audio (a voiceover or original sound), false for a catalogue or library track. Null when the platform did not say, never false for unknown.
      - `creator` object required nullable
        - `id` string required
        - `handle` string required nullable
        - `name` string required
        - `follower_count` number required nullable
        - `avatar_url` string required nullable Our archived copy when there is one, else the platform's URL, which may be signed and expire. Download it rather than storing the link.
        - `verified` boolean required nullable The creator's verified badge, from their latest profile read, else from this video's read. Null when neither reported it.
      - `region` string required nullable Where the video was posted, as the platform reports it (a country code on TikTok). Null when not reported.
      - `language` string required nullable Platform-declared caption language, e.g. `en` (TikTok). Null when not reported.
      - `title` string required nullable The video's own title (YouTube, Facebook). `description` still starts with it on YouTube, as before. Null when the platform shows none or did not report it.
      - `commerce` object required nullable Platform-declared commerce: what the Platform itself says, never inferred from the content. Each flag is null when not reported, never false. The object is null when nothing in it was reported.
        - `is_ad` boolean required nullable The Platform marks the video as an ad (TikTok, Instagram).
        - `paid_partnership` boolean required nullable The Platform marks a paid partnership (TikTok, Instagram; on YouTube, its "Includes paid promotion" label).
        - `affiliate` boolean required nullable The Platform marks affiliate content (Instagram).
        - `shop_url` string required nullable The product link the video carries (TikTok Shop).
        - `anchors` array of object required The links the video carries under its caption (an editing template, a product, a place). Empty when none were reported.
          - `kind` string required The Platform's own anchor type id, e.g. `54`.
          - `label` string required What the anchor shows, e.g. `CapCut · Editing made easy`.
      - `people` object required nullable Platform-declared people: accounts the Video itself mentions or names. On TikTok `mentions` are the caption's mentions; on Instagram they are the users the Video tags, then the accounts its caption names. Null when it named none.
        - `mentions` array of string required Handles the caption mentions (lowercased).
        - `collaborators` array of object required Co-authors the Platform names on the Video (an Instagram collab, a Facebook collaborator).
          - `handle` string required nullable
          - `name` string required nullable
      - `location` object required nullable The place the Video names (Instagram). Null when it named none or the read did not say.
        - `id` string required nullable The Platform's own place id. Null when not sent.
        - `name` string required The place, e.g. `Central Park, New York`.
      - `topics` object required nullable Platform-declared topics. Null when the Platform reported none.
        - `keywords` array of string required The creator's own tags (YouTube).
        - `genre` string required nullable The Platform's category, e.g. `Education` (YouTube).
        - `labels` array of string required The Platform's own category labels (TikTok).
      - `chapters` array of object required nullable The description's chapters, in order (YouTube). Null when the video has none or they were not reported.
        - `title` string required
        - `start_seconds` number required Where the chapter starts, in seconds from the start.
      - `ai_generated` boolean required nullable The Platform labels the video AI-generated (TikTok). Null when not reported.
      - `snapshot` object required nullable
        - `at` string required
        - `views` number required
        - `likes` number required
        - `comments` number required
        - `shares` number required
        - `saves` number required
      - `scores` object required nullable Null when the creator has no baseline yet.
        - `outlier` number required nullable Views over the creator's median views.
        - `outlier_p90` number required nullable
        - `percentile` number required nullable Where this video sits in the creator's own sample, 0-100.
        - `velocity` object required nullable
          - `views_per_hour` number required
          - `window` string required
        - `reason` string required nullable always `aged_out` `aged_out` when the video is over 30 days old and Scores are null.
        - `baseline` object required The yardstick every Score on this creator is measured against.
          - `median_views` number required
          - `p90_views` number required
          - `engagement_rate` number required nullable
          - `posts_per_week` number required nullable
          - `sample` integer required
          - `confidence` string required one of `high`, `low`
          - `source` string required one of `videos`, `follower_tier`
          - `computed_at` string required nullable
          - `stale` boolean required True past 7 days; the next lookup refreshes it.
      - `coverage` string always `partial` Present only where the platform's index is best-effort.
    - `creators` array of object required The top 25 creators, by best outlier multiple, then views. Every creator the research recorded pages, with filters and sorting, at `GET /v1/research/{id}/creators`.
      - `platform` string required
      - `id` string required
      - `handle` string required nullable
      - `name` string required
      - `follower_count` number required nullable
      - `avatar_url` string required nullable The creator's avatar as their video carried it at collection time; may be a signed URL that expires. Null when not recorded.
      - `video_count` integer required
      - `best_outlier` number required nullable
      - `median_views` number required nullable The Creator baseline median views `best_outlier` was measured against, frozen at collection time. Null, never zero, when no video of theirs carried Scores.
      - `total_views` number required
      - `video_ids` array of string required
      - `top_video_id` string nullable Breakdown only: this Creator's top Video against their own median (`platform:id`). Null when none was analysed.
      - `posts_per_week` number nullable Breakdown only: posts per week over the Videos read. Null when their dates are unknown.
      - `videos_read` integer Breakdown only: Videos read for this Creator.
      - `videos_analysed` integer Breakdown only: Videos analysed of those read; 0 when the intent dropped all of them or every analysis failed.
    - `hashtags` array of object required The top 25 hashtags, by video count, then views. Every hashtag the research recorded pages, with filters and sorting, at `GET /v1/research/{id}/hashtags`.
      - `value` string required
      - `title` string nullable
      - `platforms` array of string required Every platform this tag was observed on in the shortlist. One rollup spans platforms; `video_ids` are `platform:id` if you want the split.
      - `video_count` integer required
      - `total_views` number required
      - `video_ids` array of string required
    - `sounds` array of object required The top 25 sounds, by video count, then views. Every sound the research recorded pages, with filters and sorting, at `GET /v1/research/{id}/sounds`.
      - `value` string required
      - `title` string nullable
      - `platforms` array of string required Every platform this tag was observed on in the shortlist. One rollup spans platforms; `video_ids` are `platform:id` if you want the split.
      - `video_count` integer required
      - `total_views` number required
      - `video_ids` array of string required
      - `author` string nullable Who made the sound, from the first video that named them. Null when none did; absent on research recorded before it existed.
      - `is_original` boolean nullable True for original audio, false for a catalogue track, so a trend sound can be told from a voiceover. Null when no video said; absent on research recorded before it existed.
    - `research` object required
      - `requested` integer required
      - `completed` integer required
      - `failed` integer required
      - `cached` integer required
      - `fresh` integer required
      - `max_videos` integer required
      - `included_videos` integer required
      - `shortlisted` map of integer Shortlist places per requested platform: a guaranteed part each, then the rest by how many on-topic candidates each had.
      - `dropped_off_topic` integer Candidates judged off-topic and dropped before the shortlist; never charged.
      - `dropped_unsure` integer Candidates whose relevance was unsure and that ranked too low to keep; never charged.
      - `dropped_sample` array of object Up to 10 dropped candidates, best-ranked first, with why each was dropped. Absent on research recorded before it existed.
        - `video_id` string required `platform:id` of the dropped video.
        - `verdict` string required one of `off_topic`, `unsure`
        - `reason` string required The relevance check's reason, short.
- Option 2 object Returned instead of a Run when `estimate=true`. Nothing is charged. Operations with more to say about a quote add members; the two above are always present.
  - `estimate_credits` integer required What the call would be charged, in Credits.
  - `cached` boolean required Whether a stored result can already answer. It does not change the price.

200 · The Run finished: a research result with `mode: creators`.

```json
{
  "run": {
    "id": "run_8f2c1ad04b",
    "kind": "research",
    "status": "completed",
    "created_at": "2026-09-08T09:14:02.418Z",
    "completed_at": "2026-09-08T09:14:04.930Z",
    "charge_credits": 1018,
    "list_price_credits": 1018,
    "cached": false
  },
  "result": {
    "digest_version": 5,
    "mode": "creators",
    "coverage": {
      "tiktok": "complete"
    },
    "digest": {
      "summary": "Cold plunge videos are travelling on measured personal diaries rather than on science explainers. The videos that break out open on an unimpressive first number and pay it off with a later one, and keep the discomfort on camera instead of cutting around it. Sentiment is broadly positive, with the negative share concentrated on the cost of equipment.",
      "themes": [
        {
          "name": "30-day diaries",
          "share": 0.45,
          "description": "One creator, one tub, a counter on screen, and the change stated as a number rather than a feeling.",
          "video_ids": [
            "tiktok:7300000000000000000"
          ]
        },
        {
          "name": "Before-workout plunging",
          "share": 0.2,
          "description": "Shorter clips arguing about timing relative to training, usually with a gym setting.",
          "video_ids": [
            "tiktok:7300000000000000042"
          ]
        }
      ],
      "hooks": [
        {
          "text": "Day one I lasted eleven seconds.",
          "video_id": "tiktok:7300000000000000000",
          "hook_type": "stat",
          "why_it_works": "An unimpressive concrete number sets up a payoff the viewer stays for.",
          "template": "Day one I lasted [small number] [unit].",
          "lift": {
            "value": "STORY",
            "video_count": 9,
            "median_views": 1840000,
            "median_engagement_rate": 0.0912,
            "views_lift": 1.6,
            "engagement_lift": 1.21,
            "low_confidence": false
          }
        }
      ],
      "sentiment": {
        "positive": 0.62,
        "neutral": 0.23,
        "negative": 0.15,
        "note": "Enthusiasm for the routine itself; the negative share is almost entirely the price of a tub."
      },
      "angles": [
        {
          "angle": "Open on the day-30 number and work backwards to day one.",
          "rationale": "Every diary in the shortlist that led with the outcome outperformed the ones that led with the method."
        }
      ],
      "gaps": [
        {
          "ask": "Viewers want a cold plunge setup that does not cost hundreds.",
          "comments": [
            {
              "text": "ok but what do I do if I can't afford a 600 dollar tub",
              "video_id": "tiktok:7300000000000000000"
            }
          ],
          "angle": "A 30-day diary on a chest freezer or bathtub build, with the receipt on screen."
        }
      ]
    },
    "videos": [
      {
        "platform": "tiktok",
        "id": "7300000000000000000",
        "url": "https://www.tiktok.com/@someone/video/7300000000000000000",
        "description": "3 minutes at 4°C every morning for 30 days. Here is what actually changed. #coldplunge #recovery",
        "posted_at": "2026-09-05T06:32:11.000Z",
        "duration_seconds": 41,
        "thumbnail_url": "https://p16-sign.tiktokcdn.com/obj/tos-cover-7300000000000000000.jpeg",
        "hashtags": [
          "coldplunge",
          "recovery",
          "morningroutine"
        ],
        "sound": {
          "id": "7180000000000000000",
          "title": "original sound - someone",
          "author": "someone",
          "is_original": true
        },
        "creator": {
          "id": "cre_9d4b2a7f1c",
          "handle": "someone",
          "name": "Someone",
          "follower_count": 184300,
          "avatar_url": "https://p16-sign.tiktokcdn.com/obj/tos-avatar-someone.jpeg",
          "verified": false
        },
        "region": "US",
        "language": "en",
        "title": null,
        "commerce": {
          "is_ad": false,
          "paid_partnership": null,
          "affiliate": null,
          "shop_url": null,
          "anchors": [
            {
              "kind": "54",
              "label": "CapCut · Editing made easy"
            }
          ]
        },
        "people": {
          "mentions": [
            "coldplungeco"
          ],
          "collaborators": []
        },
        "location": null,
        "topics": {
          "keywords": [],
          "genre": null,
          "labels": [
            "Fitness",
            "Health"
          ]
        },
        "chapters": null,
        "ai_generated": false,
        "snapshot": {
          "at": "2026-09-08T09:14:04.219Z",
          "views": 1284000,
          "likes": 143200,
          "comments": 2810,
          "shares": 9040,
          "saves": 21600
        },
        "scores": {
          "outlier": 4.21,
          "outlier_p90": 1.6,
          "percentile": 95,
          "velocity": {
            "views_per_hour": 6698.6,
            "window": "snapshot"
          },
          "reason": null,
          "baseline": {
            "median_views": 305000,
            "p90_views": 802000,
            "engagement_rate": 0.1342,
            "posts_per_week": 4.1,
            "sample": 20,
            "confidence": "high",
            "source": "videos",
            "computed_at": "2026-09-08T09:14:03.902Z",
            "stale": false
          }
        }
      },
      {
        "platform": "tiktok",
        "id": "7300000000000000042",
        "url": "https://www.tiktok.com/@anotherone/video/7300000000000000042",
        "description": "I tried cold plunging before every workout for a week. #coldplunge",
        "posted_at": "2026-09-06T13:04:52.000Z",
        "duration_seconds": 28,
        "thumbnail_url": "https://p16-sign.tiktokcdn.com/obj/tos-cover-7300000000000000042.jpeg",
        "hashtags": [
          "coldplunge",
          "gym"
        ],
        "sound": {
          "id": "7180000000000000099",
          "title": "Winter Air",
          "author": "Northbound",
          "is_original": false
        },
        "creator": {
          "id": "cre_1a77c0b3de",
          "handle": "anotherone",
          "name": "Another One",
          "follower_count": 21400,
          "avatar_url": null,
          "verified": null
        },
        "region": "GB",
        "language": "en",
        "title": null,
        "commerce": {
          "is_ad": false,
          "paid_partnership": null,
          "affiliate": null,
          "shop_url": null,
          "anchors": []
        },
        "people": null,
        "location": null,
        "topics": null,
        "chapters": null,
        "ai_generated": null,
        "snapshot": {
          "at": "2026-09-08T09:14:04.219Z",
          "views": 96200,
          "likes": 8140,
          "comments": 212,
          "shares": 401,
          "saves": 1180
        },
        "scores": null
      }
    ],
    "creators": [
      {
        "platform": "tiktok",
        "id": "cre_9d4b2a7f1c",
        "handle": "someone",
        "name": "Someone",
        "follower_count": 184300,
        "avatar_url": "https://p16-sign.tiktokcdn.com/obj/tos-avatar-someone.jpeg",
        "video_count": 3,
        "best_outlier": 4.21,
        "median_views": 305000,
        "total_views": 1611400,
        "video_ids": [
          "tiktok:7300000000000000000"
        ]
      }
    ],
    "hashtags": [
      {
        "value": "coldplunge",
        "platforms": [
          "instagram",
          "tiktok"
        ],
        "video_count": 34,
        "total_views": 18402000,
        "video_ids": [
          "tiktok:7300000000000000000",
          "instagram:C9xExampleShortcode"
        ]
      }
    ],
    "sounds": [
      {
        "value": "7180000000000000000",
        "title": "original sound - someone",
        "author": "someone",
        "is_original": true,
        "platforms": [
          "tiktok"
        ],
        "video_count": 4,
        "total_views": 2140000,
        "video_ids": [
          "tiktok:7300000000000000000"
        ]
      }
    ],
    "breakdown": {
      "videos_per_creator": 25,
      "creators": [
        {
          "platform": "tiktok",
          "handle": "glowwithmaya",
          "id": "6800000000000000101",
          "videos_read": 25,
          "ended": false
        },
        {
          "platform": "tiktok",
          "handle": "skinbyjo",
          "id": "6800000000000000102",
          "videos_read": 25,
          "ended": false
        }
      ]
    },
    "research": {
      "requested": 50,
      "completed": 50,
      "failed": 0,
      "cached": 6,
      "fresh": 44,
      "max_videos": 50,
      "included_videos": 0
    }
  }
}
```

200 · With `estimate=true`: the price, no Run, no charge.

```json
{
  "estimate_credits": 1018,
  "cached": false
}
```

### 202 Accepted

Still running: `{ run }`. Poll `GET /v1/runs/{id}`.

object

Still running. Poll `poll_url` (`GET /v1/runs/{id}`), waiting the 202's `Retry-After` seconds between polls.

- `run` object required Every charged operation creates a Run. The envelope is identical whether the result came back inline or was polled. Its fields are on [Poll or re-read a run](https://www.shortsintel.com/docs/reference/runs/get-run#response-200).
- `poll_url` string required format `uri` Absolute URL of `GET /v1/runs/{id}` for this Run, the body's copy of the 202's `Location` header for callers that never see headers (MCP). Present only while the Run is `running`.

202 · Example

```json
{
  "run": {
    "id": "run_8f2c1ad04b",
    "kind": "research",
    "status": "running",
    "created_at": "2026-09-08T09:14:02.418Z",
    "completed_at": null,
    "charge_credits": 0,
    "list_price_credits": 0,
    "cached": false
  },
  "poll_url": "https://www.shortsintel.com/v1/runs/run_8f2c1ad04b"
}
```

### 400 Bad Request

`invalid_request` (a malformed id, `wait`, `estimate` or Idempotency-Key) or `unsupported_platform`, refused before any Run opens. Nothing is charged.

Codes: `invalid_request`, `unsupported_platform`. The problem body and every code are on [Errors](https://www.shortsintel.com/docs/reference/errors).

### 401 Unauthorized

Codes: `missing_authorization`, `invalid_key`, `key_revoked`, `key_expired`, `invalid_token`. The problem body and every code are on [Errors](https://www.shortsintel.com/docs/reference/errors).

### 402 Payment Required

`insufficient_balance` — the Balance cannot cover the quoted price (the worst case for Research and enriched search). No Run is created and nothing is charged. A multi-item Run whose Balance runs short later charges its completed items up to the Balance and ends `partial`.

Code: `insufficient_balance`. The problem body and every code are on [Errors](https://www.shortsintel.com/docs/reference/errors).

### 409 Conflict

`idempotency_conflict` — the same Idempotency-Key with a different payload.

Code: `idempotency_conflict`. The problem body and every code are on [Errors](https://www.shortsintel.com/docs/reference/errors).

### 429 Too Many Requests

`rate_limit_exceeded` or `concurrency_limit_exceeded`, with `Retry-After`.

Codes: `rate_limit_exceeded`, `concurrency_limit_exceeded`. The problem body and every code are on [Errors](https://www.shortsintel.com/docs/reference/errors).

### 502 Bad Gateway

A Run that opened and then failed is answered as `application/problem+json` with its problem's status — `vendor_error` here, `not_found` as 404 — and the Run envelope alongside, so the Run id is kept. A failed Run is never charged.

Code: `vendor_error`. The problem body and every code are on [Errors](https://www.shortsintel.com/docs/reference/errors).

### 503 Service Unavailable

`source_unavailable`: every source this Run reads failed or is switched off. The Run fails uncharged, with its envelope alongside; `endpoints` names the source. Wait `retry_after_seconds` (also sent as `Retry-After`) and retry rather than treating it as empty.

Code: `source_unavailable`. The problem body and every code are on [Errors](https://www.shortsintel.com/docs/reference/errors).

## Request samples

curl:

```bash
curl -X POST "https://www.shortsintel.com/v1/breakdowns" \
  -H "Authorization: Bearer sk_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"creators":[{"platform":"tiktok","handle":"glowwithmaya"},{"platform":"tiktok","handle":"skinbyjo"}],"videos_per_creator":25}'
```

TypeScript:

```typescript
const response = await fetch(
  "https://www.shortsintel.com/v1/breakdowns",
  {
    method: "POST",
    headers: {
      "Authorization": "Bearer sk_live_YOUR_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      "creators": [
        {
          "platform": "tiktok",
          "handle": "glowwithmaya"
        },
        {
          "platform": "tiktok",
          "handle": "skinbyjo"
        }
      ],
      "videos_per_creator": 25
    }),
  },
);

if (!response.ok) {
  throw new Error(`ShortsIntel ${response.status}`);
}

const data = await response.json();
```

Python:

```python
import requests

response = requests.post(
    "https://www.shortsintel.com/v1/breakdowns",
    headers={
        "Authorization": "Bearer sk_live_YOUR_KEY",
        "Content-Type": "application/json",
    },
    json={
        "creators": [
            {
                "platform": "tiktok",
                "handle": "glowwithmaya",
            },
            {
                "platform": "tiktok",
                "handle": "skinbyjo",
            },
        ],
        "videos_per_creator": 25,
    },
)
response.raise_for_status()

data = response.json()
```

## Related guides

This call creates a Run. With `wait` it holds the response open until the Run finishes or the wait runs out, then answers with the finished result or `202` and the running Run. Poll [`GET /v1/runs/{id}`](https://www.shortsintel.com/docs/reference/runs/get-run) until it settles; re-reading a Run is free.

- [Runs](https://www.shortsintel.com/docs/concepts/runs): How a call becomes a Run, waiting inline, polling and free re-reads.
- [Pricing and estimates](https://www.shortsintel.com/docs/concepts/pricing-and-estimates): Credits, estimates and why a cached result costs the same as a fresh one.
- [Research](https://www.shortsintel.com/docs/concepts/research): How an intent becomes a digest, why a research id is a run id, and how to re-slice what you collected for free instead of paying for it twice.

[Previous Research a niche](https://www.shortsintel.com/docs/reference/research/create-research)[Next Read a research's status](https://www.shortsintel.com/docs/reference/research/get-research)
