API reference contents

FrameThrower API

Curated film frames, searchable by concept, color, mood, composition and image similarity — for agents, creative tools and production pipelines.

Create your API key

Free to start — every new account gets credits, and the API is metered against the balance. No card, no plan to pick.

Introduction

The FrameThrower API gives you programmatic access to the same cinematography reference library that filmmakers use on the website — text search, image search, color matching, craft-attribute filtering, and a growing film catalog. It's designed to be read by humans, agents and LLMs alike.

Every response returns metadata, thumbnail URLs and deep links — never raw image bytes. Reference use, with attribution.

Base URL

https://framethrower.ai/api/v1

What you can build

The API is built for creative workflows. Four things it is already used for, each one a couple of calls:

Quickstart

1

Create a FrameThrower account

Sign up for free — Google or email. Takes 30 seconds.
2

Complete onboarding

Accept the Terms of Service and set your content preferences. This is required for API access because the API exposes the same content as the website.
3

Create an API token

Go to Settings → API and click + Create token. Copy the token — it's shown only once.
4

Make your first request

curl -X POST https://framethrower.ai/api/v1/search \
  -H "Authorization: Bearer ft_YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"query": "neon-lit rainy street at night", "limit": 5}'
Or from JavaScript, with the official SDK:
npm install framethrower-ai

Pricing

The API is included in the free plan. A new account gets 200 free credits, no card, and can spend them here straight away. There is no plan to upgrade to for access — usage is metered per call, and you buy more credits when you want more calls.

Most calls2 credits
Search, colour, craft, similar, frame detail, films.
Image search10 credits
Costs more because it embeds the image you send.
$11,000 credits
So $1 covers 500 searches. Credits never expire.
Top up$20 packs
In Settings → Billing, in whole multiples.

No rate limits. Deliberately — throttling the thing you are building against is not a business model. Run a balance down and the API answers with a message saying so, including where to top up, rather than failing silently.

Response format

Every endpoint returns JSON with this shape:

{
  "data": [ ... ] or { ... },   // the result(s)
  "meta": {                      // context about the request
    "total": 20,
    "query": "...",
    "mode": "hybrid",
    ...
  }
}

Every frame in a response carries:

idstring
Frame identifier, stable — use it with /similar and /frames.
imageUrlstring
Full-size original.
thumbUrlstring
1280px WebP thumbnail.
depthUrlstring | null
Precomputed depth map — see Depth maps. null when the frame has none; never the colour image.
deepLinkstring
Opens the frame on FrameThrower.
filmobject
Title, year, director, DP, genres.
metadataobject
Scene description, shot type, camera angle, lens character, lighting, moods, colors, era, setting, time of day.
promptstring
A composed AI image prompt. Frame detail only — see Frame detail.

Search results and frame detail are not the same shape. Two fields exist only on the single-frame call, which is the most common thing to get wrong:

FieldSearch, browse, similar/frames?id=
id, imageUrl, thumbUrl, deepLinkyesyes
depthUrlyesyes
film, metadatayesyes
metadata.sceneDescriptionyesyes
prompt (composed, ready to generate from)noyes
scoreyesno

So the usual shape of an integration is: search once to find candidates, then call /frames?id= on the handful you keep, to get the composed prompt.

Errors return { "error": "message" } with the appropriate HTTP status — 400, 401, 403, 404 or 500.

Depth maps

Every frame in the library has been through Depth-Anything V2 already, and the result is served alongside it. You do not request it, compute it, or pay for it — depthUrl is on every frame in every response, including search results.

{
  "id": "ac75d913-...",
  "imageUrl": "https://frames.shotspot.pro/core/ac75d913....jpg",
  "thumbUrl": "https://frames.shotspot.pro/thumb/ac75d913....jpg.webp",
  "depthUrl": "https://frames.shotspot.pro/depth/ac75d913....jpg.webp"
}

The map is greyscale WebP, roughly 9KB, and has the same pixel dimensions as the frame — so it drops straight into a depth ControlNet with no resizing. Near is bright, far is dark.

# Search, then feed the geometry straight into a generation
curl -s https://framethrower.ai/api/v1/search \
  -H "Authorization: Bearer $FT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"query":"low-angle staircase, hard shadows","limit":4}' \
| jq -r '.data[].depthUrl'

Fetching the map needs no authentication. It is a plain public file on our image host, not an API endpoint — do not send your Authorization header with it. Responses are cached for a year and support range requests.

curl -s -o depth.webp \
  "https://frames.shotspot.pro/depth/ac75d913....jpg.webp"   # no token

From a browser, read it server-side. The image host sends no CORS headers, so an <img src> displays the map fine but fetch() is blocked, and drawing it to a canvas taints the canvas. If your pipeline runs in the browser, proxy the map through your own origin first.

Two things that will bite otherwise. depthUrl is null when no map exists — it never falls back to the colour image, because a photograph handed to a depth ControlNet produces a confidently wrong result with nothing to explain it. And a URL can 404 in the rare case where a map is missing; treat that the same as null and compute depth locally.

One quirk if you use Python: our CDN blocks the default Python-urllib user-agent, so urllib.request gets a 403 on any image URL, depth included. requests, httpx and anything with a normal user-agent are fine.

Depth costs no credits. Only the search that found the frame does.

Connect

Authentication

Use your API token as a Bearer token in the Authorization header. All endpoints accept JSON and return JSON.

Authorization: Bearer ft_your_token_here
Content-Type: application/json

From an AI agent (Claude, ChatGPT, Cursor…)

If your agent supports custom tools or API integrations, configure it with these three values. For a connection with no plumbing at all, use the MCP server instead. FrameThrower is the only film reference library with a public API and an MCP server — see how it compares.

Base URLstring
https://framethrower.ai/api/v1
Authheader
Bearer ft_your_token
Docs URLstring
https://framethrower.ai/developers

Point your agent at this page (/developers) — it can read the endpoint reference and examples to understand what's available. The agent makes requests on your behalf using your token, so it has the same access as you do on the website.

OpenAPI / tool definitions

For agents that consume OpenAPI specs, the machine-readable spec lives at /api/v1/openapi.json. To hand-write a tool schema instead, here is a minimal definition for search:

{
  "name": "framethrower_search",
  "description": "Search FrameThrower's film frame library by concept, mood, lighting, color, or composition. Returns thumbnails + metadata + deep links. Use for finding visual references.",
  "parameters": {
    "type": "object",
    "properties": {
      "query": { "type": "string", "description": "Natural language search (e.g. 'overhead dinner table warm candlelight')" },
      "limit": { "type": "integer", "minimum": 1, "maximum": 50, "default": 10 }
    },
    "required": ["query"]
  }
}

JavaScript SDK

framethrower-ai wraps every endpoint on this page — same token, no HTTP plumbing. TypeScript types are bundled.

npm install framethrower-ai
import FrameThrower from 'framethrower-ai'

const ft = new FrameThrower('ft_YOUR_TOKEN')

// Search by text, colour, or image
const frames = await ft.search('neon rain at night')
const warm   = await ft.searchByColor(['#e8a87c', '#1b4332'])
const like   = await ft.searchByImage('https://example.com/photo.jpg')

// Browse by craft, then follow a frame into its film
const shots = await ft.browse({ shot_type: 'close-up', lens_character: 'anamorphic' })
const detail = await ft.frame(frames[0].id)   // detail.prompt is a composed image prompt
const more   = await ft.similar(frames[0].id, { mode: 'visual' })

const films = await ft.films({ q: 'kubrick', sort: 'frames' })
const film  = await ft.film(films[0].slug)

Source and full reference: npmjs.com/package/framethrower-ai.

POST/api/v1/search

Search the frame library with natural language. Describe a mood, scene, composition or visual — the search understands cinematography.

querystringrequired
Natural language, e.g. neon-lit rainy street at night.
limitinteger
1–50. Defaults to 20.
modestring
hybrid (default) fuses visual and text embeddings. semantic uses image-derived embeddings only. description searches text metadata only.
{
  "query": "neon-lit rainy street at night",
  "limit": 20,
  "mode": "hybrid"
}
POST/api/v1/search/image

Find frames that match a reference image by visual similarity, color and semantic meaning.

imageUrlstringrequired
Publicly reachable URL of the reference image.
limitinteger
1–50. Defaults to 20.
{ "imageUrl": "https://example.com/reference.jpg", "limit": 20 }
POST/api/v1/search/color

Find frames dominated by specific colors. Pass one hex color or a palette of up to six.

colorstring
A single hex color. Use this or colors.
colorsstring[]
Up to six hex colors, blended into one target.
limitinteger
1–50. Defaults to 20.
// Single color
{ "color": "#e94560" }

// Palette (blended)
{ "colors": ["#e94560", "#1a1a2e", "#16213e"], "limit": 20 }

Browse by craft

POST/api/v1/browse

Filter frames by cinematography attributes — no text query needed. Combine any filters. This is the unique surface: search by how a shot was made, not just what it shows.

{
  "shot_type": "close-up",          // or "wide", "medium", "extreme-close-up"
  "lens_character": "anamorphic",   // or "spherical", "vintage_soft"
  "setting": "exterior",            // or "interior"
  "time_of_day": "night",           // or "day", "golden_hour", "blue_hour"
  "camera_angle": "low_angle",      // or "high_angle", "eye_level", "overhead"
  "visual_style": "noir",           // or "naturalistic", "stylized"
  "director": "Denis Villeneuve",
  "genre": "Science Fiction",
  "era": "2010s",
  "year_min": 2010,
  "year_max": 2025,
  "limit": 20
}

Similar frames

POST/api/v1/similar

Find frames similar to one or more reference frames. The “more like this” flow.

frameIdstring
A single frame id. Use this or frameIds.
frameIdsstring[]
Several ids, averaged — the result is the centroid of what you passed in.
modestring
semantic, visual or color.
limitinteger
1–50. Defaults to 20.
// Single frame
{ "frameId": "abc123", "mode": "semantic", "limit": 20 }

// Multiple frames (averaged — finds the centroid)
{ "frameIds": ["abc", "def", "ghi"], "mode": "visual", "limit": 20 }

Frame detail

GET/api/v1/frames?id=FRAME_ID

Full metadata for a single frame — film info, cinematography attributes, colors, mood, and a ready-to-use AI image prompt.

// Response shape
{
  "data": {
    "id": "abc123",
    "imageUrl": "https://frames.shotspot.pro/core/...",
    "thumbUrl": "https://frames.shotspot.pro/thumb/...",
    "deepLink": "https://framethrower.ai/#frame/abc123",
    "prompt": "A lone figure walks through dense orange haze toward a distant structure. Set in exterior, day. Diffused amber backlight with atmospheric haze. Desaturated warm tones dominated by deep orange and ochre. Naturalistic style with a melancholy, atmospheric, isolation mood.",
    "film": {
      "title": "Blade Runner 2049", "year": 2017,
      "director": "Denis Villeneuve", "dp": "Roger Deakins",
      "genres": ["Science Fiction", "Drama"],
      "tmdbId": 335984, "imdbId": "tt1856101"
    },
    "metadata": {
      "sceneDescription": "A lone figure walks through orange haze...",
      "shotType": "wide", "cameraAngle": "eye_level",
      "lensCharacter": "anamorphic",
      "lightingKey": "low_key",
      "lightingDescription": "Diffused amber backlight with atmospheric haze",
      "moods": ["melancholy", "atmospheric", "isolation"],
      "colorPalette": ["#c45a28", "#1a1a2e", "#e8a87c"],
      "era": "2010s", "setting": "exterior", "timeOfDay": "day"
    }
  }
}

Random frames

GET/api/v1/frames/random?limit=20

Get a diverse random sample. Useful for seeding a UI, inspiration, or sampling the catalog.

Autocomplete

GET/api/v1/suggest?q=kub

Fuzzy-match film titles and director/DP names. Returns up to 5 films and 3 people.

// Response
{
  "data": {
    "films": [
      { "type": "film", "title": "2001: A Space Odyssey", "year": 1968,
        "director": "Stanley Kubrick", "posterUrl": "...", "imdbId": "..." }
    ],
    "people": [
      { "type": "person", "name": "Stanley Kubrick", "slug": "stanley-kubrick",
        "portraitUrl": "...", "filmCount": 12 }
    ]
  }
}

Film catalog

GET/api/v1/films?page=1&per_page=50&q=kubrick

Browse the full film catalog. Paginated, filterable by title or director name.

Film detail

GET/api/v1/films/:slug

Full detail for a film — plot, runtime, MPAA rating, dominant color palette and sample frames.

GET /api/v1/films/blade-runner-2049-2017

Film frames

GET/api/v1/films/frames?slug=blade-runner-2049-2017

Get all frames for a specific film (up to 500). Each includes a thumbnail, deep link and scene description.

Commercials

The commercials library: award-winning and everyday spots with credits for every role, awards, story, script and every shot tagged. Two levels — whole spots (/commercials/search, /commercials/spots) and the shots inside them (/commercials/shots/*). Stills are URLs. Add clips: true to a shot call (or a spot with its shot list) and each shot comes with its clip too (clip_url), on every plan — that call costs 8 credits instead of 2 (image search stays 10). The full-spot video file is never served — every result links to watch it on FrameThrower. Same token, same credits as the film endpoints, and the same tools are on the MCP connector.

One place for the whole treatment: the same account reaches the film library above, and on FrameThrower the animation library, the moodboard / lookbook builder and the canvas — commercial, film and animation references side by side.

Commands at a glance

POST /commercials/search2 credits
GET /commercials/spots2 credits
GET /commercials/spots/:id2 credits
GET /commercials/spots/:id/similar2 credits
POST /commercials/shots/search2 credits
POST /commercials/shots/image10 credits
POST /commercials/shots/similar2 credits
GET /commercials/shots/:id2 credits
GET /commercials/stats2 credits
GET /commercials/profile/:kind/:name2 credits
GET /commercials/suggestfree
GET /commercials/vocabularyfree

Spot filters

Every spot-level endpoint — and the shot endpoints too — takes these. Names are free text and folded (Wieden+Kennedy = wieden and kennedy); a name with no match comes back in meta.hint with suggestions, so an empty result is never a silent typo. Closed values: GET /commercials/vocabulary.

brandstring
Advertiser, e.g. "Nike". Comma-separate for several.
not_brandstring
Leave these advertisers out
directorstring
Credited director — a name
agencystring
Credited agency — a name
productionstring
Credited production — a name
dpstring
Credited dp — a name
editorstring
Credited editor — a name
edit_housestring
Credited edit house — a name
poststring
Credited post — a name
musicstring
Credited music — a name
soundstring
Credited sound — a name
vfxstring
Credited vfx — a name
personstring
Anyone credited, in any role
industrystring
Ad category: Auto, Beer, Spirits, Drinks, Food & QSR, Retail, Tech, Telecom, Finance & Insurance, Fashion & Luxury, Beauty & Personal care, Sport, Travel, Gaming, Health & Pharma, Media & Entertainment, Public service & NGO, Home, FMCG (plain words work: "cars", "banks")
marketstring
Country code: US, UK, FR, DE, BR, JP…
languagestring
Spoken language code: en, es, fr…
genrestring
comedy, drama, documentary…
structurestring
narrative, montage, vignettes, problem_solution, demo…
formatstring
live_action, animation_2d, animation_3d, stop_motion, cgi, motion_graphics, mixed
tonestring
funny, heartwarming, epic, emotional, absurd, dark, inspiring…
tropestring
A trope in plain words
seasonstring
A calendar moment: halloween, christmas, black friday, thanksgiving, new year, valentines, easter, mothers day, fathers day, back to school, summer, spring, pride, super bowl
year_mininteger
Spot year from
year_maxinteger
Spot year to
min_durationinteger
Seconds
max_durationinteger
Seconds
awardedboolean
true = won or was shortlisted at an award show
award_showstring
Award show One of: Cannes Lions, Clio Awards, D&AD, Epica, AICP Awards, London International Awards, British Arrows, The One Show, El Ojo de Iberoamérica, Eurobest, Creative Circle, Effie Awards, Ciclope, Spikes Asia, ANDY Awards, ADC Awards, DGA Awards, Kinsale Sharks, shots Awards, New York Festivals, Webby Awards, Emmy (Outstanding Commercial), IPA Effectiveness Awards, AdFest, Dubai Lynx, ADC Europe, Young Director Award.
min_awardstring
Best award at least this level One of: grand_prix, gold, silver, bronze, shortlist.
award_yearinteger
Year of the award
celebrityboolean
true = a recognizable person appears
POST/api/v1/commercials/search

Whole commercials by idea or story ("two rival neighbours outdo each other at Christmas") and/or any filter — brand, every credit role, award show and level, year, industry, market, tone, genre, structure, season. Without `query` it lists matches, award winners first (Grand Prix → Gold → Silver → Bronze → shortlist, then the rest newest first). Each spot: brand, title, year, credits, best award, logline, tones, tropes, a still and a watch link. Costs 2 credits.

querystring
The idea / story in plain words (optional)
sortstring
relevance (with query) | awards | newest | oldest
limitinteger
1–50 Default 12.
offsetinteger
Paging offset Default 0.

Plus any spot filter above.

{
  "query": "a product demo told as a heist",
  "industry": "Auto",
  "min_award": "gold",
  "limit": 5
}

List commercials

GET/api/v1/commercials/spots

The same as POST /commercials/search, as a GET with query parameters — for links and caches. Costs 2 credits.

sortstring
awards | newest | oldest
limitinteger
1–50 Default 12.
offsetinteger
Paging offset Default 0.

Plus any spot filter above.

/commercials/spots?director=Jonathan+Glazer&sort=awards

One commercial in full

GET/api/v1/commercials/spots/:id

Everything about one spot: idea, logline, tagline, product, audience, talent; credits by role (director, agency, production, DP, editor, edit house, post, music, sound, VFX); every award (show, year, level, category); craft numbers (shots, average shot length, cuts per minute, when the product / brand / emotional peak / end card land); the script with timings; and the shot list — caption, tags, palette, first / middle / last frame and the clip of each shot, with its shot id. Costs 2 credits.

idstringrequired
Commercial id, e.g. AD-0505
wordsboolean
Add word-level timings to the script
shotsboolean
false = leave out the shot list
clipsboolean
true = include each shot's clip_url — the call then costs 8 credits (image search stays 10)

Similar commercials

GET/api/v1/commercials/spots/:id/similar

Commercials like this one — by=story (idea, tropes, tone) or by=look (their shots look alike; each result names the matching shot). Takes any spot filter to narrow. Costs 2 credits.

idstringrequired
Commercial id
bystring
story | look Default story.
limitinteger
1–30 Default 8.
clipsboolean
true = include each shot's clip_url — the call then costs 8 credits (image search stays 10)

Plus any spot filter above.

POST/api/v1/commercials/shots/search

Individual shots by what is in frame ("hand pouring beer in slow motion"), with shot tags and any commercial-level filter (brand, director, agency, award show, year…). Look-alike copies of one shot (cutdowns, re-uploads) are skipped and counted in meta.look_alikes_hidden. The query is rewritten into the register the shots were described in (benchmarked precision@12 0.68 → 0.83); send enhance:false for the raw words. Costs 2 credits.

querystringrequired
What the shot shows
shot_sizestring
extreme_closeup, closeup, medium_closeup, medium, medium_wide, full, wide, extreme_wide, insert
camera_anglestring
eye_level, low_angle, high_angle, overhead, aerial, pov, worms_eye, dutch
settingstring
interior | exterior | studio_void | virtual_cg
time_of_daystring
day, night, golden_hour, dusk, dawn…
lightingstring
natural, high_key, low_key, silhouette, chiaroscuro
moodstring
A mood tag
placestring
Place type, e.g. "kitchen"
peoplestring
women, men, kids, teens, adults, seniors — comma = all of them
product_visibleboolean
The product is in frame
logo_visibleboolean
The brand logo is in frame
text_on_screenboolean
Text on screen
black_and_whiteboolean
Black-and-white shot
spotstring
Only inside this commercial (id)
per_spotinteger
Max shots from one commercial Default 3.
enhanceboolean
Rewrite the query first (default true)
limitinteger
1–50 Default 20.
offsetinteger
≤150 Default 0.
clipsboolean
true = include each shot's clip_url — the call then costs 8 credits (image search stays 10)

Plus any spot filter above.

{
  "query": "car drifting at night in the rain",
  "industry": "Auto",
  "time_of_day": "night",
  "limit": 10
}

Shots that look like an image

POST/api/v1/commercials/shots/image

Shots that look like a reference image (an http(s) URL or a data: URL, ≤ 8 MB). The image is embedded in the same space as the shots' stills. Takes shot tags and spot filters. Costs 10 credits.

imageUrlstringrequired
http(s) or data: URL
shot_sizestring
extreme_closeup, closeup, medium_closeup, medium, medium_wide, full, wide, extreme_wide, insert
camera_anglestring
eye_level, low_angle, high_angle, overhead, aerial, pov, worms_eye, dutch
settingstring
interior | exterior | studio_void | virtual_cg
time_of_daystring
day, night, golden_hour, dusk, dawn…
lightingstring
natural, high_key, low_key, silhouette, chiaroscuro
moodstring
A mood tag
placestring
Place type, e.g. "kitchen"
peoplestring
women, men, kids, teens, adults, seniors — comma = all of them
product_visibleboolean
The product is in frame
logo_visibleboolean
The brand logo is in frame
text_on_screenboolean
Text on screen
black_and_whiteboolean
Black-and-white shot
per_spotinteger
Max shots from one commercial Default 2.
limitinteger
1–50 Default 20.
clipsboolean
true = include each shot's clip_url — the call then costs 8 credits (image search stays 10)

Plus any spot filter above.

Similar shots

POST/api/v1/commercials/shots/similar

More shots that look like these shot ids (from a search or a shot list) — the "more like #3" refine loop. Takes shot tags and spot filters to steer. Costs 2 credits.

shot_idsstring[]required
1–10 shot ids
shot_sizestring
extreme_closeup, closeup, medium_closeup, medium, medium_wide, full, wide, extreme_wide, insert
camera_anglestring
eye_level, low_angle, high_angle, overhead, aerial, pov, worms_eye, dutch
settingstring
interior | exterior | studio_void | virtual_cg
time_of_daystring
day, night, golden_hour, dusk, dawn…
lightingstring
natural, high_key, low_key, silhouette, chiaroscuro
moodstring
A mood tag
placestring
Place type, e.g. "kitchen"
peoplestring
women, men, kids, teens, adults, seniors — comma = all of them
product_visibleboolean
The product is in frame
logo_visibleboolean
The brand logo is in frame
text_on_screenboolean
Text on screen
black_and_whiteboolean
Black-and-white shot
per_spotinteger
Max shots from one commercial Default 2.
limitinteger
1–50 Default 20.
clipsboolean
true = include each shot's clip_url — the call then costs 8 credits (image search stays 10)

Plus any spot filter above.

One shot

GET/api/v1/commercials/shots/:id

One shot: caption, tags, palette, still, timing and its commercial — and its clip with clips=true. Costs 2 credits.

idstringrequired
Shot id (UUID)
clipsboolean
true = include each shot's clip_url — the call then costs 8 credits (image search stays 10)

Count and rank

GET/api/v1/commercials/stats

How many commercials match a filter, broken down by up to 6 fields — e.g. which agencies won most at Cannes Lions for car ads since 2015: fields=agency&award_show=Cannes Lions&industry=Auto&year_min=2015. Fields: brand, director, agency, production, dp, editor, edit_house, post, music, sound, vfx, industry, market, language, year, genre, structure, format, tone, trope, award_show, award_year, season, collection. Costs 2 credits.

fieldsstringrequired
Comma list
limitinteger
Rows per field, 1–100 Default 15.

Plus any spot filter above.

/commercials/stats?fields=agency%2Cdirector&award_show=Cannes+Lions&industry=Auto&year_min=2015

Brand or credit profile

GET/api/v1/commercials/profile/:kind/:name

The track record of a brand, director, agency, production company, DP, editor, composer…: spots, years, per-year counts, who they work with, awards by show, tones and genres, best and most recent work. Directors, agencies and production companies with a public page also carry the researched profile (description, founded, HQ, website). An unknown name returns data:null with meta.did_you_mean. Costs 2 credits.

kindstringrequired
brand | director | agency | production | dp | editor | edit_house | post | music | sound | vfx
namestringrequired
URL-encoded name

Names that exist

GET/api/v1/commercials/suggest

Typeahead over brands, directors, agencies, production companies, DPs, editors and music credits, ranked by number of spots. Free. Free.

qstringrequired
At least 2 characters
kindstring
brand | director | agency | production | dp | editor | music
limitinteger
≤25 Default 8.

Filter values

GET/api/v1/commercials/vocabulary

The values that exist for every closed filter — industries, genres, structures, formats, tones, seasons, markets, languages, award shows, credit roles and the shot tags. Free; read it once and cache it. Free.

Full examples

curl — search and refine

# Search
curl -s -X POST https://framethrower.ai/api/v1/search \
  -H "Authorization: Bearer ft_YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"query": "overhead shot dinner table warm light"}' \
  | jq '.data[:3] | .[] | {title: .film.title, url: .deepLink}'

# Browse by craft
curl -s -X POST https://framethrower.ai/api/v1/browse \
  -H "Authorization: Bearer ft_YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"shot_type": "close-up", "lens_character": "anamorphic", "limit": 5}' \
  | jq '.data | length'

# Autocomplete
curl -s "https://framethrower.ai/api/v1/suggest?q=nolan" \
  -H "Authorization: Bearer ft_YOUR_TOKEN" \
  | jq '.data.films[].title'

Reference to generation, end to end

The flow most image pipelines want: find a frame, take its geometry, take its prompt. Note the second call — the composed prompt only comes from frame detail.

import requests

TOKEN = "ft_YOUR_TOKEN"
H = {"Authorization": f"Bearer {TOKEN}"}

# 1. Find candidate references.
hits = requests.post(f"https://framethrower.ai/api/v1/search", headers=H,
    json={"query": "low-angle staircase, hard shadows", "limit": 5}).json()["data"]

frame = hits[0]

# 2. The composed prompt lives on frame detail, not on search results.
detail = requests.get(f"https://framethrower.ai/api/v1/frames",
    headers=H, params={"id": frame["id"]}).json()["data"]

prompt = detail["prompt"]          # scene + people + lighting + colour + mood
scene  = frame["metadata"]["sceneDescription"]   # just the description, no extra call

# 3. The depth map is a plain public file — no token, and do not send one.
depth = frame["depthUrl"]
if depth:
    r = requests.get(depth, timeout=30)     # NOT urllib: our CDN 403s its user-agent
    if r.ok:
        open("depth.webp", "wb").write(r.content)
    else:
        depth = None                        # rare miss — fall back to computing it

# 4. Hand both to whatever you generate with.
print(prompt)
print("depth map:", "depth.webp" if depth else "compute locally")
Depth controls geometry, not identity. If you are placing a character into a frame's composition, run the character on its own conditioning — an IP-Adapter or a character LoRA — alongside the depth ControlNet rather than expecting depth to carry both. Dropping the depth weight to around 0.6–0.8 also stops it fighting whatever is holding the character.

Python

import requests

TOKEN = "ft_YOUR_TOKEN"
H = {"Authorization": f"Bearer {TOKEN}"}

# Search by concept
r = requests.post(f"https://framethrower.ai/api/v1/search", headers=H,
    json={"query": "anamorphic close-up blue hour", "limit": 5})
for f in r.json()["data"]:
    print(f'{f["film"]["title"]} — {f["deepLink"]}')

# Find by color
r = requests.post(f"https://framethrower.ai/api/v1/search/color", headers=H,
    json={"colors": ["#c45a28", "#1a1a2e"]})
print(f'{len(r.json()["data"])} frames match that palette')

# Browse craft
r = requests.post(f"https://framethrower.ai/api/v1/browse", headers=H,
    json={"director": "Wong Kar-wai", "time_of_day": "night"})
for f in r.json()["data"]:
    print(f'{f["film"]["title"]} — {f["metadata"]["lensCharacter"]}')

JavaScript / TypeScript

const TOKEN = "ft_YOUR_TOKEN";
const api = (path, body) =>
  fetch(`https://framethrower.ai/api/v1${path}`, {
    method: body ? "POST" : "GET",
    headers: { "Authorization": `Bearer ${TOKEN}`, "Content-Type": "application/json" },
    body: body ? JSON.stringify(body) : undefined,
  }).then(r => r.json());

// Script → visual references workflow
async function findRefsForScene(sceneDescription) {
  // 1. Search by scene text
  const { data: results } = await api("/search", {
    query: sceneDescription, limit: 10
  });

  // 2. Pick the best match, find more like it
  if (results.length > 0) {
    const { data: similar } = await api("/similar", {
      frameId: results[0].id, mode: "visual", limit: 5
    });
    return [...results.slice(0, 5), ...similar];
  }
  return results;
}

// Use it
const refs = await findRefsForScene(
  "candlelit dinner party in a grand ballroom, wide shot, warm"
);
refs.forEach(f => console.log(f.film.title, f.thumbUrl));
The API surfaces frames as reference material — metadata, thumbnail URLs and deep links back to FrameThrower. It never returns raw image bytes.

By using the API you agree to the Terms of Service and Intended Use Policy. Bulk redistribution and automated scraping are prohibited. Rights to the original films remain with their respective rights holders.

Questions? contact@framethrower.ai