API
Make videos from code.
Ask Ivan for an explainer from your own servers and agents. One call makes a video; a few more follow it, fetch it and share it. It is priced, held and charged exactly like a video made on the site.
Version 1 · Updated · OpenAPI 3.1 · llms.txt
Quickstart
Four calls take a prompt to a video you can play and share. Every example uses curl; any HTTP client works.
1. Make a key
Sign in at tryivan.com, open Account, and choose Create a key under API keys. The key is shown once. Keep it on your server, never in a web page or an app people download.
export IVAN_API_KEY=ivan_sk_live_…
2. Ask for a video
Send what the video should explain. The Idempotency-Key makes the request safe to repeat: keep it, and if the call fails or times out, send it again with the same key. You get the same video, never a second one.
KEY=$(uuidgen)
curl https://api.tryivan.com/v1/videos \
-H "Authorization: Bearer $IVAN_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $KEY" \
-d '{"prompt": "How do vaccines train the immune system?", "max_credits": 1000}'
The answer is 202 Accepted with the video, "status": "queued", and its id, such as vid_01J8….
3. Wait for it
Ask for the video with wait_seconds=25. Ivan holds the call until the video changes or 25 seconds pass, so a loop of these calls follows it closely without hammering. Stop when status is completed, failed or canceled.
curl "https://api.tryivan.com/v1/videos/vid_01J8…?wait_seconds=25" \
-H "Authorization: Bearer $IVAN_API_KEY"
4. Fetch it
Download the film, or its captions, transcript or poster, from Ivan's own host.
curl -o video.mp4 "https://api.tryivan.com/v1/videos/vid_01J8…/content" \
-H "Authorization: Bearer $IVAN_API_KEY"
5. Share it
A video is private until you share it. Sharing gives it a public page anyone with the link can watch, and a player other sites can embed.
curl -X POST https://api.tryivan.com/v1/videos/vid_01J8…/share \
-H "Authorization: Bearer $IVAN_API_KEY"
MCP server
An AI agent can use Ivan as tools, with no code to write. Ivan's MCP server is at https://mcp.tryivan.com/mcp. Claude, ChatGPT and Grok connect to it by signing in; coding agents send the same key as the API, the same way. Connect it once, then ask your agent for a video: "Make me an Ivan video on how tides work."
Claude, ChatGPT and Grok
These three connect with no key to copy. Add Ivan to the app as a custom connector with the URL https://mcp.tryivan.com/mcp; it needs no client ID or secret. The app sends you to tryivan.com, where you sign in and press Allow.
Claude, on claude.ai or the desktop app: open Add custom connector with Ivan filled in, or go to Settings, Connectors, Add custom connector and paste the URL.
ChatGPT, and Grok on grok.com: add a custom connector in the app's settings and paste the URL. ChatGPT lists custom connectors under Apps, with developer mode switched on.
On the Allow screen you set what the app may spend: a limit for the month, $10 unless you change it, and a limit for any one video, $3. A video that would hold more than the one-video limit is made shorter to fit. Public links are off unless you switch them on there: the app gives you a private link to watch, and share_video is refused with insufficient_scope. A connected app has up to 5 videos in progress at once.
The app never holds a key. It holds a token that lasts an hour and renews itself, and asks you to sign in again once it has gone 90 days unused. See what each app has spent, and disconnect one, under Connected apps in your account. A disconnected app is stopped the way a revoked key is, and the videos it started still finish. To change a limit, connect the app again. However often an app is connected, and after a disconnect too, what it spent this month still counts. These tokens work on the MCP server only, never on the API.
Ivan is invite-only, so signing in needs an account. Only these three apps can connect this way; every other agent uses a key.
Claude Code
claude mcp add --transport http ivan https://mcp.tryivan.com/mcp \
--header "Authorization: Bearer $IVAN_API_KEY"
Codex
codex mcp add ivan --url https://mcp.tryivan.com/mcp \
--bearer-token-env-var IVAN_API_KEY
Cursor
In ~/.cursor/mcp.json:
{
"mcpServers": {
"ivan": {
"url": "https://mcp.tryivan.com/mcp",
"headers": { "Authorization": "Bearer ${env:IVAN_API_KEY}" }
}
}
}
VS Code
In .vscode/mcp.json:
{
"servers": {
"ivan": {
"type": "http",
"url": "https://mcp.tryivan.com/mcp",
"headers": { "Authorization": "Bearer ${input:ivan-key}" }
}
},
"inputs": [
{ "id": "ivan-key", "type": "promptString", "description": "Ivan API key", "password": true }
]
}
Any other agent
Point an MCP client at the URL over Streamable HTTP, with the header Authorization: Bearer ivan_sk_live_…. The server answers every protocol version from 2024-11-05 to 2026-07-28, keeps no session, and replies to each POST with plain JSON. Signing in is for Claude, ChatGPT and Grok only; any other client is asked for a key.
The tools
| Tool | What it does | Scope |
|---|---|---|
estimate_video | Prices a video without making it. | videos:create |
create_video | Starts a video from a prompt and answers at once. | videos:create |
wait_for_video | Waits up to 25 seconds for the video to move on. The agent calls it until the video is done, when it carries download_url. | videos:read |
get_video | Reads a video as it is now. | videos:read |
list_videos | Lists the account's videos, newest first. | videos:read |
cancel_video | Stops a video still being made. | videos:create |
share_video | Publishes a finished video at a public link. | videos:share |
get_account | Reads the credits left and the key's limits. | None; the credits need usage:read |
A finished video from wait_for_video or get_video carries download_url, a link to its MP4 file that needs no key and works for an hour. An agent that can upload or attach files, in Slack or another chat app, fetches it and posts the video itself, so it plays where the person asked. The agent is told this when it connects.
Each tool is one of the endpoints below and answers with the same JSON, so the video, the credits and limits and the key's scopes work as they do on the API. A refusal comes back as the tool's answer, marked as an error, with the same code. create_video takes an optional idempotency_key. Without one, the same request from the same key in the last 10 minutes answers with the video it made, so an agent that repeats a call it lost the answer to is never charged twice; once that video is canceled, deleted, or fails with nothing charged, the same request makes a new one. Send a new idempotency_key to make another video like one already made. A video made this way has a channel of mcp: and the name of the agent that asked, such as mcp:claude-code.
Keys and authentication
Send your key on every request as a bearer token, to the API host and nowhere else:
Authorization: Bearer ivan_sk_live_…
The API lives at https://api.tryivan.com/v1. That host answers nothing but /v1, and reads nothing but the key: no cookie, no sign-in. The site at tryivan.com never answers /v1. Call the API from a server or an agent; it does not answer pages in a browser, and a key in a web page is a key anyone can copy.
Make and revoke keys in the API keys section of your account. A key is shown once, when it is made; Ivan keeps only a hash of it. Each key has a name, its scopes, a monthly cap and a limit on videos in progress. An account holds up to 10 active keys. A revoked key can make, cancel, delete or share nothing from that moment, and stops reading within 15 seconds; videos it already started still finish and are settled.
A team's admins make keys for the team itself in the team console, under API keys. Such a key acts as the team, not as a person: it keeps working when people leave, and what it makes is charged to the team's pool, up to the key's monthly cap. A team's keys share the team's rate limits and one set of Idempotency-Keys, so give each service its own.
A key is ivan_sk_live_, 40 random letters and digits, and a 6-character checksum, so a mistyped key is refused without a lookup and secret scanners can recognise a leaked one. The account page shows only its last four characters.
Keys belong to member accounts that are approved. The operator's account cannot hold keys. Every response carries a request-id header, the id Ivan's logs carry; quote it when you contact support. No response is cached.
Scopes
A key does only what its scopes allow. A new key has every scope unless you untick some when you make it. A call outside a key's scopes is refused with insufficient_scope.
| Scope | Allows |
|---|---|
videos:read | Listing and reading videos, and downloading their files. |
videos:create | Estimating, making, canceling and deleting videos. |
videos:share | Sharing a video by link, and stopping sharing. |
usage:read | Reading the account's credits and this key's spending, in GET /v1/me. |
files:write | Reserved for uploading source files. No endpoint uses it yet. |
GET /v1/me needs no scope.
Idempotency
Every POST /v1/videos must carry an Idempotency-Key header: 8 to 100 visible ASCII characters, such as a UUID. Use a new key for each new video.
Repeating a request with the same key and the same body returns the same video, 202, with the header Idempotent-Replayed: true. The body is compared as JSON, so the order of its fields and its spacing do not matter. The same key with a different body is refused with idempotency_key_reused.
Keys are remembered for the account, across all of its API keys. Repeat a create whenever its outcome is unknown: a dropped connection, a timeout, or any 5xx. A request that runs past 120 seconds is answered timeout and may still have been accepted, so repeat it with the same key.
Credits and limits
Videos are paid for in credits: 1,000 credits are $1. A video is charged by the minute of what it delivers, at its power's rate:
| Power | Credits a minute | Per minute |
|---|---|---|
eco (default) | 200 | $0.20 |
max | 400 | $0.40 |
The hold. Ivan's writer chooses each video's length. When a video starts, it holds the price of the longest video it may run: up to 15 minutes, and no more than the account can spend, the key may still commit this month, the request's max_credits allows, or a connected app's limit for one video allows. The writer is told that length. When the video finishes, it is charged for the length it delivered, rounded up to a whole credit, and the rest of the hold is released. A video that fails is not charged. A hold is never less than one minute at the video's rate; a max_credits below that is refused with max_credits_too_low.
A board prompt. With look board, a prompt of several sentences may be spoken as written, so it can set its own length. If speaking it could run longer than the account's credits, the key's month or max_credits pay for, the video holds the full 15 minutes instead, and is refused if that hold can't be covered. A refusal over money carries credits_needed, the hold to allow for.
Estimates. POST /v1/videos/estimate takes the same body as a create, holds nothing, and says what the video would hold, how long it could run, and whether it can start now.
Each key's monthly cap. A key may commit only so many credits in a calendar month (UTC): held work counts at its hold, finished work at its charge. A new key's cap is 50,000 credits ($50) unless you choose another, up to 10,000,000 ($10,000); 0 stops it spending. A video that would pass it is refused with spend_cap_reached.
Videos in progress. An account makes 3 videos at once, 6 once it has bought $50 of credits, and 12 from $500. Ask for more and they still start straight away while Ivan has machines to spare, though it always keeps a couple free for other people; when it is busy, they wait for the account's own videos to finish while other people's go first. Up to 20 more may wait; past that, a new video is refused with too_many_waiting until one finishes. These numbers are the account's, shared by all its keys and the site, and GET /v1/me reports them in limits. Each key also has a limit of its own, max_in_flight (50), refused with too_many_in_flight.
Videos made through the API are never free videos and never carry the Ivan mark. On a team, videos are charged to the team's pool, and the team's limits apply.
The video object
Every video call answers with this object, or a list of them.
{
"object": "video",
"id": "vid_01J8ZQ3N4X7K2M",
"status": "in_progress",
"stage": "drawing",
"progress": 0.4,
"playable": true,
"topic": "How vaccines train the immune system",
"power": "eco",
"look": "illustrated",
"aspect": "16:9",
"language": "en",
"duration_seconds": null,
"watch_url": "https://tryivan.com/?video=01J8ZQ3N4X7K2M",
"share_url": null,
"download_url": null,
"download_expires_at": null,
"credits": { "held": 1000, "charged": 0 },
"music": {
"title": "Investigations",
"by": "Kevin MacLeod",
"licence": "CC BY 4.0",
"licence_url": "https://creativecommons.org/licenses/by/4.0/",
"source": "https://incompetech.com/music/royalty-free/",
"credit": "Music: “Investigations” by Kevin MacLeod (https://incompetech.com/music/royalty-free/), licensed under CC BY 4.0 (https://creativecommons.org/licenses/by/4.0/)"
},
"web_check": null,
"error": null,
"channel": "api",
"metadata": { "order": "A-1" },
"logo_corner": null,
"brand": null,
"created_at": 1790000000,
"completed_at": null
}
idvid_and the video's own id.statusqueued,in_progress,completed,failedorcanceled. The last three are final.stage- While
in_progress, what Ivan is doing now:checkingthe request,writing,drawing,voicing,rendering(from the moment the first scene is being made into film) orfinishingthe final cut; orreviewingwhile its charge is checked. It only moves forward. Otherwisenull. progress- How far along the video is, from 0 to 1, to two decimals: each step’s share of a typical video’s making time, by the scenes it has finished and the ones it has underway. It moves steadily while the video is made, never goes back, and reaches 1 only when the video is completed. If Ivan has to restart a video after an interruption, its
statusreturns toqueuedand progress starts again from the scenes it saved. A step that takes longer than usual holds it still rather than running it ahead. playable- Whether its first scenes can already be watched at
watch_url. topic- A short label for the video, drawn from its prompt.
power,look,aspect,language- What it was made with.
lookisillustrated,flat,doodleorboard. duration_seconds- The delivered length it was charged for, once settled;
nullbefore. watch_url- Where the account's owner watches and edits it, signed in. It is private to the account. It is
nullfor a video made with a team's own key, which no one signs in to: share the video, or use its files. share_url- Its public page while it is shared, otherwise
null. download_url,download_expires_at- Where one completed video is read (
GET /v1/videos/{id}, waited for or not), a link to its film (MP4) that needs no key, and when the link stops working, in Unix seconds: an hour on. Use it where a key cannot be sent, such as a chat app that fetches a file or a tool that uploads one. Anyone holding the link can open the film until then, so hand it only to whoever the video is for. Revoking the key ends its links at once, as does disconnecting an app or connecting it again. Every read gives a new link. Both arenulluntil the video is completed, in a list and in the answer to a create or a cancel, and for an app connected by sign-in with public links switched off. creditsheldfor work still running andchargedfor finished work.music- The music under the narration and its licence, or
nullfor a video without music. Wherever you publish the video, publishcreditwith it: the music is licensed on that condition. The video's file carries the same line in its metadata. web_check- For a prompt about something recent, the quick check of the web Ivan made before writing, as
{"checked_at", "sources"}: when, in Unix seconds, and the pages its facts came from, each{"title", "site", "url"}.nullfor every other video, and until the check has found facts. A prompt that needs more than a quick check is refused withresearch_needed. error- Why it failed, as
{"code", "message"}, ornull. Codes:generation_failed,at_capacity,drawing_failed,voice_failed,render_failed,interrupted,canceled,source_unreadableandinternal_error. channelapi;mcp:and the agent's name for a video made through the MCP server; orstudiofor a video made on the site.metadata- Your own labels, as you sent them.
logo_corner- The corner its logo sits in, or
nullfor a video without one. brand- The brand colours it is dressed in, as
{"page", "ink", "accent", "desk"}, each#RRGGBB; ornullfor a video made without them. created_at,completed_at- Unix seconds.
completed_atisnulluntil it is completed.
Endpoints
Paths are relative to https://api.tryivan.com. Bodies are JSON, up to 64 KiB. A field an endpoint does not take is refused, never ignored, so a misspelt option never silently does nothing.
POST /v1/videos
Makes a video. Scope videos:create. Requires an Idempotency-Key. Answers 202 with the video, and a Location header naming it.
promptrequired- What the video should explain, in your words. Up to 4,000 characters.
powereco(default) ormax.lookillustrated(default),flat,doodleorboard.language- The narration language, as a code Ivan narrates in, such as
en(default),en-IN,hiores. aspect16:9(default) or9:16.narratormale(default) orfemale.motionfalsekeeps the drawings still.max_credits- The most this video may hold and be charged, from 1 to 100,000,000 credits.
metadata- Up to 20 labels of your own. Keys are up to 40 letters, digits,
_,-or.; values are strings of up to 500 characters. logo_url- A public
httporhttpslink, up to 2,048 characters, to a logo: a PNG or JPG file of up to 2 MB. The video carries it in a corner of every frame, in place of Ivan's mark, at no charge. Only a video a team pays for can carry one; any other key is refused withpermission_denied. Ivan reads the link once, when the video is made, and the video keeps its own copy, so a later change at the link never changes a video already made. A link Ivan can't read a logo from refuses the create withinvalid_request: a video asked for with a logo is never made without it. Send the same logo with every video of one company or workspace. logo_corner- Where the logo sits:
bottom_right(default),bottom_left,top_rightortop_left. It needslogo_url. A top corner is refused for a language whose titles Ivan can't letter beside it. brand- A company's brand colours to dress the video in, at no charge, such as
{"colors": ["#283C8B", "#1AA06B"]}.colorsis one to three colours as#RRGGBB, the main one first. The page becomes a pale tint of the first colour, lines and words a deep ink of the brand, and the light that runs along each drawing as it is explained takes the second colour (or the first), or another of them where that one is too near the ink or too pale to show.pagemay belight(default),white, or a pale#RRGGBBof your own, such as a cream;inkandaccentmay be given too. Every drawing is still made and checked on white, so a brand never changes what is drawn; and the page stays pale so every drawing reads on it: a colour too strong for a page is made a pale tint of itself, and an ink or accent that would not read is darkened until it does (an accent too near the ink is lightened instead, so the light shows on the lines). The estimate says the colours the video will use and anything changed. Only a video a team pays for can take a brand; any other key is refused withpermission_denied. It works on theillustratedandflatlooks; others are refused withinvalid_request. Each video takes its own brand: an agent making videos for several companies sends each company's colours, and logo, with its videos.
POST /v1/videos/estimate
Prices a video without making it. Scope videos:create. Takes the same body as a create and needs no idempotency key.
{
"object": "estimate",
"max_credits": 1000,
"credits_per_minute": 200,
"longest_seconds": 300,
"available_credits": 12000,
"key_remaining_credits": 49000,
"can_start": true,
"refusal": null
}
refusal is why a create would be refused now, as its error code: max_credits_too_low, spend_cap_reached, insufficient_credits, too_many_in_flight or too_many_waiting.
A request with brand colours also gets brand: the page, ink, accent and desk the video would be dressed in, each colour that was changed so the video reads (fixed), and what you should know (warnings), such as a red accent, which reads as loss or danger.
GET /v1/videos/{id}
One video. Scope videos:read. With wait_seconds, from 0 (default) to 25, an unfinished video is held until its status, stage or playable changes, its progress moves a tenth further, or the time runs out.
GET /v1/videos
The account's videos, newest first, including those made on the site. Scope videos:read. limit is from 1 to 100 (default 20); for the next page, send the last id you have as starting_after.
{ "object": "list", "data": [ { "object": "video", … } ], "has_more": true }
POST /v1/videos/{id}/cancel
Stops a video being made. Scope videos:create. A queued video stops at once and its hold is released; one already being made stops at its next safe point. Cancelling is free until the first scene can be watched; after that, the scenes already made are charged at the video's rate and the rest of the hold is released. Answers 202 with the video; follow it until status is canceled. A video that is not being made is refused with not_cancelable.
DELETE /v1/videos/{id}
Deletes the whole video: every version of it, its files and its public link. Scope videos:create. A video still being made is refused with video_in_progress; cancel it first.
{ "object": "video", "id": "vid_01J8ZQ3N4X7K2M", "deleted": true }
GET /v1/videos/{id}/content
A finished video's files. Scope videos:read. They come from the version the video plays now: its newest finished cut, edits made on the site included. Choose one with variant:
videodefault- The film,
video/mp4. It answersRange,If-Range,HEADandETag, so players and resumable downloads work. captions- WebVTT captions,
text/vtt. transcript- Plain text: the title, then each chapter's heading and its sentences.
poster- A lettered poster,
image/png.
Before anything has finished it is refused with video_not_ready; once the film or its script is no longer kept, with content_expired. A file that still matches the ETag sent in If-None-Match answers 304 with no body. A Range that starts past the end of the film answers 416 with invalid_request, and Content-Range: bytes */<length> gives the film's length; ask for a range within it.
GET /v1/downloads/{token}/{file}
The film again, at the link a completed video carries in download_url. It is the one address that takes no key: the link is the permission, for an hour. Follow a link as the video gave it; one cannot be built. It answers as the film does above, with Range, HEAD and ETag, and its file is named for the video's topic. A key's links have an allowance and 6 downloads at once of their own, so whoever holds one takes nothing from the key's own calls.
A link that was changed, has expired, or was made for a key since revoked or an app since disconnected answers 404 with not_found. Read the video again for a new one.
POST /v1/videos/{id}/share
Shares a video by link, exactly as the share switch on the site does. Scope videos:share. Sharing again returns the same link.
{
"object": "share",
"video_id": "vid_01J8ZQ3N4X7K2M",
"shared": true,
"url": "https://tryivan.com/f/…",
"embed_url": "https://tryivan.com/f/…/embed",
"shared_at": 1790000400
}
DELETE /v1/videos/{id}/share
Stops sharing. Scope videos:share. The page stops answering at once. The answer is the same object with "shared": false and url, embed_url and shared_at all null. Sharing again later makes a new link.
GET /v1/me
The key and the account it acts for. Needs no scope. For a connected app, key is the connection: it also carries app, such as claude, and video_cap, its limit for one video. limits says how many of the account's videos are made at once, videos_at_once, and the most it may have in progress, made and waiting together, videos_in_progress. With usage:read it also carries credits.available, what the account can spend (a team member's is the team's pool), the key's committed_this_month, remaining_this_month and in_flight, and the account's videos making and waiting now.
{
"object": "account",
"id": "…",
"key": {
"id": "key_…", "name": "Support bot", "hint": "ivan_sk_live_…a1B2", "mode": "live",
"scopes": ["usage:read", "videos:create", "videos:read"],
"monthly_cap": 50000, "max_in_flight": 50,
"committed_this_month": 1000, "remaining_this_month": 49000, "in_flight": 1
},
"limits": { "videos_at_once": 3, "videos_in_progress": 23, "making": 1, "waiting": 0 },
"credits": { "available": 12000 }
}
Rate limits
Each key may send bursts of up to 120 requests, then about 10 a second, and all of an account's keys together up to 300, then about 20 a second. Creates and estimates together are limited to 30 a minute for each key, and 60 a minute across all of an account's keys. A held wait_seconds call counts once, and an account may hold 24 at a time; past that, or while Ivan holds as many waits as it can, a wait is answered at once with Retry-After: 5, and the next one should come after it. An account may download 6 films at a time, each for up to 30 minutes; while Ivan sends as many films as it can, a download is refused with unavailable and Retry-After.
Requests with a missing or invalid key count against the address they come from; after about 120 in quick succession, that address is refused for a minute, except for a key that worked from it in the last 10 minutes. An account may make 10 keys an hour.
A refusal over a limit is 429 with a Retry-After header in seconds; so is every 503. The same number is in the body as retry_after. Wait at least that long before trying again, adding a little random delay when many workers share a key.
Errors
Every error is a problem document (RFC 9457), served as application/problem+json. Branch on code, which never changes; detail is a sentence for people. param names the field at fault, and refusals over money carry credits_needed and credits_available.
{
"type": "https://tryivan.com/docs/api#error-insufficient_credits",
"title": "Payment Required",
"status": 402,
"detail": "This video may hold up to 1000 credits; the account has 200 available. Add credits at https://tryivan.com.",
"code": "insufficient_credits",
"request_id": "0f6c…",
"credits_needed": 1000,
"credits_available": 200
}
| Code | Status | What happened, and what to do |
|---|---|---|
invalid_request | 400 | A field is missing or wrong; param names it. A request Ivan read but cannot act on is 422, and a Range past the end of a film is 416. Fix it before sending it again. |
unauthenticated | 401 | No key was sent. Send Authorization: Bearer ivan_sk_live_…. |
invalid_api_key | 401 | The key is mistyped, unknown or revoked. Check it, or make a new one. A connected app whose sign-in ended gets this too, and asks its person to sign in again. |
insufficient_scope | 403 | The key lacks the scope this call needs. Make a key that has it. For a connected app, public links are switched off; connect it again with them on. |
account_not_approved | 403 | The account can't use the API right now: it is on the waitlist or its access is paused. |
permission_denied | 403 | The account may not do this, such as the operator's account using a key, an account that can't make videos, or a logo_url or brand sent with a key no team pays for. |
team_paused | 403 | Your team, or your seat on it, is paused. Ask your team admin. |
team_tier | 400 | Your team's plan doesn't include this power. Choose another, or ask your team admin. |
insufficient_credits | 402 | The account can't cover the hold. Add credits at tryivan.com. |
spend_cap_reached | 402 | The video would pass this key's monthly cap. Make a key with a higher cap, or wait for next month (UTC). For a connected app, connect it again with a higher limit. |
max_credits_too_low | 402 | max_credits can't cover the smallest hold the video allows: one minute at the video's rate, or the full 15 minutes for a board prompt that could run longer spoken than max_credits pays for. credits_needed says how much. Raise it, or leave it out. A connected app is refused this way when its limit for one video can't cover that hold. |
team_limit | 402 | The video would pass your monthly limit on the team's pool. Ask your team admin. |
content_refused | 422 | Ivan won't make a video of this prompt, and nothing was charged. suggestions may offer up to three other ways to ask, each a prompt you can send as it stands. |
research_needed | 422 | The prompt needs research before a video can be made of it, and nothing was charged. Ivan checks a fact or two on the web, but it is an explainer video maker, not a research tool. Do the research, then send the prompt again with your findings written into it (text or links), and Ivan will explain them. |
not_found | 404 | No such video for this key's account, or no such endpoint. For a download link: it was changed, has expired, or its key was revoked or its app disconnected. |
method_not_allowed | 405 | The endpoint does not take that method. The Allow header lists those it does. |
conflict | 409 | The request crossed another change to the same account. Send it again; a create with the same idempotency key. |
not_cancelable | 409 | The video is not being made, so there is nothing to cancel. |
video_in_progress | 409 | The video is still being made. Cancel it, then delete it. |
edit_in_progress | 409 | An edit made on the site is running on this video. Wait for it to finish, or stop it on the site. |
video_not_ready | 409 | Nothing has finished yet. Wait until the video is completed. |
too_many_keys | 409 | On the account page: the account already holds 10 active keys. Revoke one first. |
content_expired | 410 | The film or its script is no longer kept. |
request_too_large | 413 | The body is over 64 KiB. |
unsupported_media_type | 415 | Send the body as JSON. |
idempotency_key_reused | 422 | This idempotency key was used for a different request. Use a new key for new work. |
rate_limited | 429 | Too many requests. Wait for Retry-After. |
too_many_in_flight | 429 | This key already has as many videos in progress as its own max_in_flight allows. Wait for one to finish; Retry-After is 30. |
too_many_waiting | 429 | The account already has as many videos being made or waiting as it may, across all its keys and the site (videos in progress). Wait for one to finish; Retry-After is 60. |
queue_full | 429 | Ivan is very busy. Videos within the account's number at once are still accepted until it is far busier; others are refused. Try again after Retry-After, 60 seconds. |
generation_paused | 503 | Making videos is paused for a few minutes. Try again after Retry-After. |
at_capacity | 503 | Ivan is at capacity. Try again after Retry-After. |
screen_unavailable | 503 | Ivan couldn't check the prompt just now, so nothing was started or charged. Repeat it after Retry-After, 5 seconds, with the same idempotency key. |
timeout | 503 | The request took too long. Repeat it; a create with the same idempotency key, since it may have been accepted. |
unavailable | 503 | Ivan could not complete the request. Try again after Retry-After; a create with the same idempotency key. |
Building with Ivan?
Tell us what you are making, or what got in your way. Include the request-id of any call that surprised you.