Connect Overindex
Use the same 34 typed research tools in the web app, over MCP, from the CLI, or through REST. Start by creating a key on the API keys page.
Connect via MCP
One URL and one API key. Nothing to install, no repository to clone, no database or provider credentials anywhere near your machine — the endpoint speaks MCP’s streamable-HTTP transport and serves all 34 tools.
1 · The endpoint
https://overindex.app/api/mcp Authorization: Bearer ugc_your_key_here
Create the key on the API keys page. It scopes every call to your workspace; runs and briefs are visible to nobody else.
2 · Claude Code
claude mcp add --transport http overindex https://overindex.app/api/mcp \ --header "Authorization: Bearer ugc_your_key_here"
Then ask Claude things like “research protein coffee UGC and give me five concepts I can shoot tomorrow.”
3 · Claude Desktop / Cursor / any client
{
"mcpServers": {
"overindex": {
"type": "http",
"url": "https://overindex.app/api/mcp",
"headers": { "Authorization": "Bearer ugc_your_key_here" }
}
}
}Clients that cannot speak HTTP transports can run the stdio fallback from the repository (apps/mcp); it proxies to this same endpoint with the same key.
4 · Verify
npx @modelcontextprotocol/inspector \ --cli https://overindex.app/api/mcp \ --transport http --header "Authorization: Bearer ugc_..." \ --method tools/list
You should see 34 tools. Results come back as structuredContent validated against the published JSON Schema in docs/api/mcp-schemas.json, and long jobs stream notifications/progress while they run.
5 · Render the visual strategy
After get_run returns done:true:
render_content_strategy({ "runId": "run_01J…" })ChatGPT and other MCP Apps hosts open the result as an inline visual artifact with cited poster or private-video previews, source links, formats and beat-by-beat filming instructions. Plain MCP and stdio clients receive the same complete structuredContent without needing the UI.
Research runs
A run discovers, watches and clusters ~40 short-form videos for a subject and persists a cited brief. It takes minutes, so the API is asynchronous end to end: start it, then poll or stream. Every poll also keeps the run moving, so a run you are watching cannot stall.
Start — answers in under a second
curl -X POST https://overindex.app/api/v1/runs \
-H "Authorization: Bearer ugc_..." \
-H "content-type: application/json" \
-H "Idempotency-Key: my-run-1" \
-d '{"subject": "protein coffee", "platforms": ["tiktok"]}'
# 202 Accepted
# {"ok":true,"runId":"run_01J…","status":"queued","etaSeconds":480,
# "pollUrl":"…/api/v1/runs/run_01J…","eventsUrl":"…/events","briefUrl":"…/brief"}Repeating the same Idempotency-Key returns the same runId and starts nothing — a retried request can never fork a second paid run.
Poll, or stream
curl -H "Authorization: Bearer ugc_..." \ "https://overindex.app/api/v1/runs/run_01J…?include=skips" # server-sent events, resumable curl -N -H "Authorization: Bearer ugc_..." -H "Last-Event-ID: 42" \ "https://overindex.app/api/v1/runs/run_01J…/events"
The stream closes its window every few seconds and tells you the id to resume from; reconnect with Last-Event-ID and you get every event after it, exactly once. Counters only ever go up — they are count(*) over the rows the run wrote.
Read the brief, or stop the run
curl -H "Authorization: Bearer ugc_..." \ "https://overindex.app/api/v1/runs/run_01J…/brief?format=markdown" curl -H "Authorization: Bearer ugc_..." \ "https://overindex.app/api/v1/runs/run_01J…/brief?format=csv" # the reference table curl -X POST -H "Authorization: Bearer ugc_..." \ "https://overindex.app/api/v1/runs/run_01J…/cancel"
Cancelling stops the run within one phase, refunds the unspent part of the reservation and leaves everything already gathered readable (?include=references).
CLI
Every tool is also a subcommand, generated from the same registry — useful for scripting and piping into jq. With UGCGPT_API_KEY set it proxies to the hosted API, so it needs no database and no provider keys.
git clone https://github.com/garrrikkotua/ugcgpt && cd ugcgpt pnpm install export UGCGPT_API_KEY=ugc_your_key_here # the only variable you need pnpm ugc run start --subject "protein coffee" # follows the run and prints the result pnpm ugc run brief --run-id run_01J… --format markdown pnpm ugc search videos "sleep tracker app" pnpm ugc account audit calai.app --platform instagram --limit 30 pnpm ugc corpus stats
Add --json for machine-readable output and --no-follow to get the 202 answer immediately. Errors print their machine code first, e.g. [insufficient_credits]. Set DATABASE_URL instead of a key to run everything locally against your own database.
REST API
curl -X POST https://overindex.app/api/v1/tools/search_videos \
-H "Authorization: Bearer ugc_..." \
-H "content-type: application/json" \
-d '{"query": "sleep tracker app"}'Responses are { "ok": true, "result": … }, or { "ok": false, "code": "…", "error": "…" } with a 4xx status. The code is machine-readable and comes from a closed set — unauthorized, invalid_api_key, invalid_input, run_not_found, insufficient_credits, rate_limited, provider_throttled and friends — so a client never has to match on prose.
Each key gets 300 units per rolling 24h; tools marked 10 units watch video with an LLM or sweep many API calls — everything else costs 1 unit. Exceeding the limit returns 429.
Tool reference
Tool names and arguments are identical across MCP, CLI, and REST.
POST /api/v1/tools/start_run10 unitsStart a research run. Standard watches 40 videos for 1 credit; deep watches 80 for 2. Mini audits one public @handle (the user's own or a competitor) or workspace-owned uploads, watches at most 10 for 0.25 credit, and produces account coaching; public-handle ownership is not verified. Returns immediately unless wait:true.
CLI: ugc start run · ugc run start
| Field | Type | Required | Description |
|---|---|---|---|
| subject | string | required | What to research, e.g. "calorie tracking apps" |
| platforms | array | optional | Defaults to both TikTok and Instagram Reels. |
| windowDays | number | optional | Recency window in days |
| depth | "mini" | "standard" | "deep" | optional | |
| watchBudget | number | optional | Videos to watch |
| maxAccountFollowers | number | optional | Demote videos from accounts bigger than this (default 500000): a huge audience explains the views, so they say little about what made the video work. Lower it for a small niche, e.g. 100000. |
| includeUploads | boolean | optional | For an account subject, also include this workspace's private uploads |
| idempotencyKey | string | optional | Repeat with the same key to get the same run instead of starting a second one |
| scopingAnswer | string | optional | Answer to the scoping question an earlier ambiguous start_run returned. Changes the plan. |
| wait | boolean | optional | Block until the run finishes. Defaults to true only on the CLI, where a human is watching a terminal; every remote door (REST, MCP, the agent) returns in <1s and keeps working in the background. |
| waitSeconds | number | default 600 | Inline driving budget |
POST /api/v1/tools/research_step10 unitsAdvance a research run by one invocation (~200s of work) and return its compact status. Call repeatedly until it returns done:true — a full run is 2–3 calls, not one per phase. Safe to call concurrently: whoever holds the run lease does the work and everyone else returns the current status.
CLI: ugc research step · ugc run step
| Field | Type | Required | Description |
|---|---|---|---|
| runId | string | required | Run id, e.g. run_01J… |
| deadlineSeconds | number | default 200 | Wall-clock budget for this invocation |
POST /api/v1/tools/get_run1 unitRead a run: status, phase, per-stage counters, cost so far, and whether its lease is stale. Polling this is also what keeps a run moving — a poll that finds nobody driving kicks the run in the background before answering (it never blocks). Pass kick:false for a pure read.
CLI: ugc get run · ugc run get
| Field | Type | Required | Description |
|---|---|---|---|
| runId | string | required | Run id, e.g. run_01J… |
| kick | boolean | default true | Kick a stalled run in the background before answering (driver B). |
POST /api/v1/tools/get_brief1 unitFetch a finished run's brief. Defaults to a compact summary (counts, format labels, reference ids) that is safe to hold in an agent's context; ask for format "markdown", "json" or "csv" only when the full document is actually needed.
CLI: ugc get brief · ugc run brief
| Field | Type | Required | Description |
|---|---|---|---|
| runId | string | required | Run id, e.g. run_01J… |
| format | "summary" | "markdown" | "json" | "csv" | default "summary" |
POST /api/v1/tools/render_content_strategy1 unitRender a completed research run as a visual content-strategy artifact: graph-confirmed promoter accounts when available, every cited video with its poster preview and link, winning formats with resolved median views, and complete filming instructions (hook, timed beats, shot list, sound, CTA and modeled-on references). Call after get_run reports done. In MCP Apps hosts such as ChatGPT it opens an inline visual document; in other clients it returns the same portable structured data.
CLI: ugc render content strategy · ugc run artifact
| Field | Type | Required | Description |
|---|---|---|---|
| runId | string | required | The completed Overindex research run id |
POST /api/v1/tools/cancel_run1 unitCancel a running research run. It stops within one phase boundary and stops watching immediately; work already persisted (analyses, references) stays readable, and the unspent part of the credit reservation is refunded.
CLI: ugc cancel run · ugc run cancel
| Field | Type | Required | Description |
|---|---|---|---|
| runId | string | required | Run id, e.g. run_01J… |
POST /api/v1/tools/answer_scoping1 unitAnswer the scoping question a run returned at start. The answer is folded into the subject before the plan is built, so it genuinely changes the queries, hashtags and rubric the run uses. A run waits a short grace for this and then plans without it — answering late is a no-op, never a block.
CLI: ugc answer scoping · ugc run answer scoping
| Field | Type | Required | Description |
|---|---|---|---|
| runId | string | required | Run id, e.g. run_01J… |
| answer | string | required | The caller's choice, in their own words |
POST /api/v1/tools/crawl_step1 unitOperator-only. Advance the scheduled niche crawler by one invocation: for one niche (or, with no niche, the least-recently crawled niches until the deadline), discover → select → watch through the run pipeline under the system org, metered by the crawler’s daily USD ceiling (CRAWLER_DAILY_USD_CEILING, default $5). Stops at the ceiling and records why; the next call resumes where it stopped. dryRun discovers and selects without watching. Refuses callers that are not the system org, the CLI/cron, or an operator.
CLI: ugc crawl step · ugc crawler step
| Field | Type | Required | Description |
|---|---|---|---|
| niche | string | optional | A niche slug (see corpus_niches). Omitted: sweep niches oldest-first until the deadline. |
| deadlineSeconds | number | default 200 | Wall-clock budget for this invocation |
| dryRun | boolean | default false | Discover and select, report what would be watched, spend nothing on watching |
| maxNiches | number | optional | When sweeping, touch at most this many niches |
POST /api/v1/tools/app_promoters1 unitWho promotes an app (or a brand/product) on TikTok, from the videos the model has actually watched: the accounts promoting it (handle, followers, videos in the corpus, median views, first/last seen), the top videos (url, views, hook, hook type, production type, date) and the hook-type distribution across them. `app` accepts an App Store URL or id, a Google Play URL or package, a website domain, or a name. Every number is traceable to a watched video; returns insufficient-evidence below 3 videos. Free and read-only.
CLI: ugc app promoters
| Field | Type | Required | Description |
|---|---|---|---|
| app | string | required | App Store URL/id, Play URL/package, domain, or a name (e.g. "cal ai") |
| limit | number | default 10 | How many top videos and accounts to return |
| minConfidence | number | default 0.5 | Ignore promotion edges below this confidence (0–0.99) |
POST /api/v1/tools/entity_videos1 unitPage through every watched video linked to a promoted entity (from app_promoters), newest first, with the extractor’s confidence and the verbatim evidence for each link: the caption link, bio link, anchor, on-screen or spoken name, or partner hashtag. Use it to audit an app_promoters answer video by video. Free and read-only.
CLI: ugc entity videos
| Field | Type | Required | Description |
|---|---|---|---|
| entityId | string | required | The entity id from app_promoters (ent_…) |
| cursor | string | optional | Opaque cursor from a previous page |
| limit | number | default 20 | |
| minConfidence | number | default 0 | Ignore edges below this confidence |
POST /api/v1/tools/upload_video1 unitUpload your own MP4 or MOV (up to 3 MiB through this JSON tool) as a private workspace video. Requires a live plan or trial and never adds the video to the shared corpus.
CLI: ugc upload video
| Field | Type | Required | Description |
|---|---|---|---|
| filename | string | required | |
| contentType | "video/mp4" | "video/quicktime" | optional | |
| contentBase64 | string | required |
POST /api/v1/tools/upload_list1 unitList this workspace's private uploaded videos and watch status.
CLI: ugc upload list · ugc uploads list
POST /api/v1/tools/upload_remove1 unitDelete one of this workspace's private uploads, its stored bytes, analysis, and any run artifacts derived from it.
CLI: ugc upload remove · ugc uploads remove
| Field | Type | Required | Description |
|---|---|---|---|
| videoId | string | required |
POST /api/v1/tools/hook_predict1 unitForecast how a hook or script idea is likely to perform BEFORE you shoot it, using the videos most similar to it that the model has actually watched. Returns comparable videos with real view counts, the median/top-decile outcome, which hook types and angles the winners used, and what separates the top performers from the rest. Use whenever the user proposes a hook, script, or concept.
CLI: ugc hook predict
| Field | Type | Required | Description |
|---|---|---|---|
| text | string | required | The hook line, script, or concept to evaluate |
| niche | string | optional | Restrict comparables to a corpus niche slug (see corpus_niches) |
| limit | number | default 20 | How many comparables to retrieve |
POST /api/v1/tools/corpus_build1 unitOperator-only. Pre-build the corpus for one seed niche (or all of them): paginate searches and hashtags to ingest metadata cheaply, then fully watch the top-viewed unwatched videos in that niche. Run ahead of demand so later research questions are instant queries instead of live crawls.
CLI: ugc corpus build
| Field | Type | Required | Description |
|---|---|---|---|
| niche | string | default "all" | Niche slug from the seed list, or "all" for every niche |
| pagesPerQuery | number | default 2 | Pages per search/hashtag |
| watchPerNiche | number | default 10 | Top unwatched videos to fully watch per niche (the paid step) |
| minViews | number | default 20000 | Only watch videos above this view count |
| ingestOnly | boolean | default false | Skip the watching phase entirely |
| concurrency | number | default 3 | Niches processed in parallel (provider calls are ~30s each) |
| skipCovered | boolean | default true | Skip niches that already have enough coverage — makes reruns resumable |
| coveredThreshold | number | default 150 | Videos that count as covered |
POST /api/v1/tools/corpus_niches1 unitList the seed niches and how much of the corpus each one covers (videos ingested and fully watched). Use to find gaps before running corpus_build.
CLI: ugc corpus niches
POST /api/v1/tools/track_add1 unitStart tracking a target for continuous collection: an account handle, hashtag, keyword, or TikTok sound ID. Watchers poll active targets and ingest+watch new videos.
CLI: ugc track add
| Field | Type | Required | Description |
|---|---|---|---|
| kind | "account" | "hashtag" | "keyword" | "sound" | required | |
| value | string | required | Handle (no @), hashtag (no #), keyword phrase, or sound ID |
| platform | "tiktok" | "instagram" | default "tiktok" | |
| label | string | optional |
POST /api/v1/tools/track_list1 unitList tracked targets and when they last ran.
CLI: ugc track list
POST /api/v1/tools/watcher_run10 unitsRun one watcher sweep: for each active tracked target, pull latest videos, ingest, then fully watch the top-viewed videos that have no analysis yet. Intended to be invoked on a schedule.
CLI: ugc watcher run
| Field | Type | Required | Description |
|---|---|---|---|
| maxWatchPerTarget | number | default 2 | |
| pullLimit | number | default 20 |
POST /api/v1/tools/audit_report10 unitsProduce a shareable markdown UGC audit for an account: pulls the account, watches its top outlier videos, and synthesizes performance shape + winning hooks + 3 copyable video concepts. The flagship report — takes a few minutes.
CLI: ugc audit report
| Field | Type | Required | Description |
|---|---|---|---|
| platform | "tiktok" | "instagram" | default "tiktok" | |
| handle | string | required | |
| limit | number | default 30 | Videos to pull for the baseline |
| maxWatch | number | default 4 | Outlier videos to fully watch |
POST /api/v1/tools/ads_search1 unitSearch the Meta Ad Library (EU coverage: all ads delivered to the EU in the last ~12 months) by free text or Facebook page IDs. Reveals which paid creatives competitors run, how long they have run (longevity = winner signal), and EU reach. Requires META_AD_LIBRARY_TOKEN.
CLI: ugc ads search
| Field | Type | Required | Description |
|---|---|---|---|
| searchTerms | string | optional | Free-text search over ad content, e.g. an app name |
| pageIds | array | optional | Facebook page IDs to pull all ads for |
| activeOnly | boolean | default false | |
| limit | number | default 25 |
POST /api/v1/tools/video_info1 unitFetch metadata for a single video: stats (views/likes/comments/shares), caption, hashtags, duration, posting date, and the music/sound used (title, author, original-sound flag). Ingests into the corpus.
CLI: ugc video info
| Field | Type | Required | Description |
|---|---|---|---|
| url | string | required | TikTok or Instagram video/reel URL |
POST /api/v1/tools/video_transcript1 unitGet the spoken transcript of a video (provider-side, no download needed).
CLI: ugc video transcript
| Field | Type | Required | Description |
|---|---|---|---|
| url | string | required | TikTok or Instagram video/reel URL |
POST /api/v1/tools/video_sound1 unitIdentify the music/sound used in a video from platform metadata: title, author, whether it is an original sound, and how many videos use it.
CLI: ugc video sound
| Field | Type | Required | Description |
|---|---|---|---|
| url | string | required | TikTok or Instagram video/reel URL |
POST /api/v1/tools/video_watch10 unitsWATCH a video with a multimodal LLM and extract its creative anatomy: hook (first 2s), hook type, pacing, on-screen text, script beats with timestamps, CTA, emotional angle, creator archetype, visual style, transcript, and a why-it-works summary. Cached: returns the stored analysis if this video was already watched. This is the core primitive.
CLI: ugc video watch
| Field | Type | Required | Description |
|---|---|---|---|
| url | string | required | TikTok or Instagram video/reel URL |
| force | boolean | optional | Re-watch even if a cached analysis exists |
| subjectShape | "app" | "dtc-physical" | "dtc-consumable" | "service" | optional | Picks the analyst framing: app, dtc-physical, dtc-consumable or service |
POST /api/v1/tools/video_similar1 unitFind videos in the corpus similar to a given video or free-text description, via embedding similarity over transcript+creative-analysis. Complement with sound_videos (same sound) for trend-cloning.
CLI: ugc video similar
| Field | Type | Required | Description |
|---|---|---|---|
| url | string | optional | TikTok or Instagram video/reel URL |
| text | string | optional | Free-text description to search by instead of a URL |
| limit | number | default 10 |
POST /api/v1/tools/account_pull1 unitFetch an account profile and its recent videos (with stats and sounds) and ingest everything into the corpus. Returns the profile and the video list.
CLI: ugc account pull
| Field | Type | Required | Description |
|---|---|---|---|
| platform | "tiktok" | "instagram" | default "tiktok" | |
| handle | string | required | Account handle, without @ |
| limit | number | default 30 | Max recent videos to pull |
POST /api/v1/tools/account_audit10 unitsPull an account and compute its performance shape: median views, outlier videos (≥3x account median), posting cadence, and top sounds/hashtags. The raw material for an account audit report; follow up with video_watch on the outliers.
CLI: ugc account audit
| Field | Type | Required | Description |
|---|---|---|---|
| platform | "tiktok" | "instagram" | default "tiktok" | |
| handle | string | required | |
| limit | number | default 50 |
POST /api/v1/tools/sound_videos1 unitList videos using a specific sound — the strongest "same trend" signal. TikTok takes a music ID (from video_info / video_sound); Instagram takes an audio_id, which only /v1/instagram/post returns. Ingests results into the corpus.
CLI: ugc sound videos
| Field | Type | Required | Description |
|---|---|---|---|
| soundId | string | required | TikTok music/sound ID, or Instagram audio_id |
| platform | "tiktok" | "instagram" | optional | Platform to search — "tiktok" (default) or "instagram". Instagram is live as of P7; see docs/ops/instagram-go-no-go.md. |
| cursor | string | optional |
POST /api/v1/tools/hashtag_videos1 unitList TikTok videos or Instagram reels for a hashtag. Ingests results into the corpus.
CLI: ugc hashtag videos
| Field | Type | Required | Description |
|---|---|---|---|
| tag | string | required | Hashtag, with or without # |
| platform | "tiktok" | "instagram" | optional | Platform to search — "tiktok" (default) or "instagram". Instagram is live as of P7; see docs/ops/instagram-go-no-go.md. |
| cursor | string | optional |
POST /api/v1/tools/search_videos1 unitKeyword-search TikTok videos or Instagram reels (discovery entry point for a niche, e.g. "calorie tracking app"). Ingests results into the corpus.
CLI: ugc search videos
| Field | Type | Required | Description |
|---|---|---|---|
| query | string | required | |
| platform | "tiktok" | "instagram" | optional | Platform to search — "tiktok" (default) or "instagram". Instagram is live as of P7; see docs/ops/instagram-go-no-go.md. |
| cursor | string | optional |
POST /api/v1/tools/trending_videos1 unitList the reels Instagram is currently promoting platform-wide — a zero-query discovery surface for format trends. Instagram only; unpaginated. Ingests results into the corpus.
CLI: ugc trending videos
| Field | Type | Required | Description |
|---|---|---|---|
| platform | "tiktok" | "instagram" | optional | Instagram only today; defaults to "instagram". |
POST /api/v1/tools/video_comments1 unitFetch comments on a TikTok video or Instagram reel: author handle, verbatim text, like count and posting time. `limit` is a target the client pages towards (TikTok returns 20 per page, Instagram 15); each page is one provider credit.
CLI: ugc video comments
| Field | Type | Required | Description |
|---|---|---|---|
| url | string | required | TikTok or Instagram video/reel URL |
| limit | number | optional | |
| cursor | string | optional |
POST /api/v1/tools/corpus_stats1 unitSummary of what is already in the corpus: video/account/analysis counts, top hook types, and most-seen sounds. Use before scraping more.
CLI: ugc corpus stats