Quickstart
- 01Create an API keyPick Access → Full API and top up your balance.
- 02Send a requestA generation returns a task right away.
- 03Get the resultPoll the task, or add a webhook, until it succeeds or fails.
# 1. Start a video (returns a task right away)
curl https://api.opennan.com/v1/videos/generations \
-H "Authorization: Bearer $OPENNAN_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: my-first-video" \
-d '{"model": "wan-2-2-fast", "prompt": "rain on a neon street", "duration": 5}'
# 2. Poll until status is succeeded or failed
curl https://api.opennan.com/v1/tasks/task_… -H "Authorization: Bearer $OPENNAN_KEY"Endpoints
One base URL, https://api.opennan.com, and one key for every category. Each model lists the inputs, qualities and durations it accepts in GET /v1/models.
- GET
/v1/modelsEvery model with its inputs, starting price and price per tier (price.matrix, compared with the maker's price). Filter with ?category=video, ?category=image (priced per image), ?category=text, ?category=voice, ?category=music, ?category=3d or ?category=roblox. - GET
/v1/toolsEvery tool that is not a model (captions, voice tools, 3D, song to MIDI, Roblox) with its status (live, unavailable or soon), price, limits and real example runs. No key needed. - POST
/v1/videos/generationsVideo from text, a start frame, start and end frames, or reference images. Returns a task. - POST
/v1/images/generationsOne image from text, or an edit of the images you send. Returns a task. - POST
/v1/chat/completionsText models in the OpenAI Chat Completions format, streamed token by token. Answers right away (no task). - POST
/v1/audio/speechText to speech in the OpenAI format. Returns the audio file right away (no task), charged per started 1,000 characters. With a vc_ voice id it speaks in your own cloned voice. - GET
/v1/audio/voicesThe voices for the speech models, with language and gender. No key needed. - POST
/v1/music/generationsA song or instrumental track from a prompt, with optional lyrics, 10 s to 4 min. Returns a task. - POST
/v1/audio/separationsSplit a song into vocals and instrumental, or into vocals, drums, bass and other. Returns a task. - GET
/v1/voices/consentThe consent sentence (with two code words) the speaker reads aloud before a voice is cloned, and its consent_token. - POST
/v1/voicesClone your own voice from a 15 to 60 second sample and the recorded consent sentence. Returns a vc_ voice id. - GET
/v1/voicesYour cloned voices and the stock voices. - DELETE
/v1/voices/{id}Delete a cloned voice everywhere it is kept. - POST
/v1/audio/translationsTranslate the speech in an audio or video file and speak it in your own voice (or a stock voice). Returns a task. - WSS
/v1/realtime/sttLive transcribe: stream speech in, get partial and final transcripts back. - WSS
/v1/realtime/translateLive translate in your own voice: stream speech in, get the translation spoken in your cloned voice back. - POST
/v1/pipelines/video-to-captions/runsTimed captions for a video (an upload or an https link): SRT and VTT, optionally translated, plus an MP4 with the captions burned in. Returns a task. - POST
/v1/3d/charactersA rigged 3D character (GLB, FBX, thumbnail, walk and run clips, and the T-pose image used) from a picture or a prompt: T-pose check, model, then rig. Returns a task. - POST
/v1/3d/tposeA strict front T-pose of the character in a picture (edited only when it is not one already). Returns a task. - POST
/v1/3d/modelsA textured 3D model (GLB, FBX, thumbnail) from a picture or a prompt, as a character or a prop. Returns a task. - POST
/v1/3d/rigA rigged FBX and GLB of a humanoid model, with optional walk and run clips. Returns a task. - POST
/v1/roblox/modelsA Roblox-ready 3D item or avatar body (FBX, GLB and a thumbnail) from a prompt or an image. Returns a task. - POST
/v1/roblox/clothingRoblox Shirt and Pants templates and a T-shirt image from a prompt or your artwork. - GET
/v1/roblox/avatarA Roblox player's public look: skin colors, headshot and full-body images. - POST
/v1/piano/transcriptionsSong to MIDI: turn a song (YouTube link, song name or file) into MIDI notes. Also at POST /v1/music/midi-transcriptions. - GET
/v1/tasks/{id}The current state of a task, with output URLs when it succeeds. - DELETE
/v1/tasks/{id}Delete a finished task's result files and text. Its status and price stay.
Authentication & keys
Send your key on every paid request, and when you read a task, as Authorization: Bearer sk-…. The same key works for REST and MCP. Keys are shown once, so store them as a secret and never put them in browser code you ship to users.
Access scopes: api (every model and tool), api:video (video and video to captions), api:image (images only), api:text (chat only), api:voice (speech and the voice tools), api:music (music and stem splitting only), api:3d (the 3D tools and Roblox 3D models), read (read-only account data, cannot spend). Pick one under Access when you create a key. Keys without API access for a category get 403. See every call a key made in Request logs.
Free tools (no key)
Song to MIDI (POST /v1/music/midi-transcriptions, also at POST /v1/piano/transcriptions), Roblox clothing (POST /v1/roblox/clothing) and a Roblox player's look (GET /v1/roblox/avatar) are free and need no key. They are limited per IP address: 3 songs a day (YouTube links, song names and files count together), and 5 prompt designs plus 30 artwork uploads a day for Roblox clothing. Past the limit you get 429 with a Retry-After header.
Song to MIDI: send a multipart form with one of url (a YouTube link), search (a song name) or file (audio or video of solo piano, up to 10 minutes and 200 MB). It returns a job id; poll GET …/midi-transcriptions/{id} for its stage and progress, then download GET …/{id}/midi. Jobs and their files are deleted after about two hours, so save the MIDI. When the importer is off, GET /v1/tools shows song-to-midi as unavailable and a call returns 503.
Roblox clothing: a multipart form with one of prompt or file (PNG, JPG or WEBP artwork, up to 15 MB) and an optional color (#rrggbb). It answers right away with a Shirt and Pants template (585×559 PNG) and a T-shirt image (512×512 PNG) as data URLs. Nothing is saved.
Tasks
Generations are asynchronous. POST /v1/videos/generations, POST /v1/images/generations, POST /v1/music/generations, POST /v1/audio/separations, POST /v1/audio/translations, POST /v1/pipelines/video-to-captions/runs, the four /v1/3d/… tools and POST /v1/roblox/models return 202 with a task; GET /v1/tasks/{id} returns its current state. Statuses move queued → running → succeeded or failed. Output URLs are on our CDN.
{
"id": "task_8f2c…",
"object": "task",
"model": "wan-2-2-fast",
"status": "succeeded", // queued | running | succeeded | failed
"progress": 100,
"outputs": [{ "type": "video", "url": "https://cdn.opennan.com/…" }],
"price_usd": 0.333,
"created_at": "2026-09-28T07:00:00Z",
"finished_at": "2026-09-28T07:01:12Z",
"error": null // on failure: { "message": "…", "refunded": true }
}Delete results. DELETE /v1/tasks/{id} (the same key as GET; only the account that made the task) deletes a finished task's output files from our storage and CDN and clears its transcripts, caption lines, other text results and request text. It returns 200 with the task: deleted: true, deleted_at and empty outputs. Status, price and timestamps stay; nothing is refunded or charged. Calling it again returns the same answer. A task that is still queued or running returns 409 task_running. MCP: delete_task. Copies the CDN has already cached can stay reachable at the same link for some time after deletion.
# Delete a finished task's results (files and text); returns the task with "deleted": true curl -X DELETE https://api.opennan.com/v1/tasks/task_… -H "Authorization: Bearer $OPENNAN_KEY"
How long text is kept. 90 days after a task is created, its text is cleared automatically: transcripts, translations, caption lines, prompts and other request text. Text outputs keep their type and role with text: null, lines: [] and expired: true. Result files of a task with no project_id are deleted 7 days after it finished (its expires_at); delete the task to remove them sooner. Webhook delivery logs older than 90 days keep only the event, task id, status and response code.
Video
Inputs: prompt (text to video), image (start frame), image + end_image (start and end frame; end_image only works with image), reference_images (up to 9). Each model in GET /v1/models lists its inputs, qualities and durations, plus durations_by_quality (lengths allowed at a quality), image_only_qualities (qualities that need a start image) and prompt_min_chars. A field the endpoint does not know, or a misspelled one, returns 400 and nothing is charged. If a run fails, we retry it for you automatically. You still see one task.
See the Quickstart for a full call, and every video model for prices per second at each quality.
Images
POST /v1/images/generations makes one image per call. Send model and prompt; add image or images (https links, up to 10) to edit them or use them as references, for models whose inputs include image. Optional: quality and size (the values a model lists in GET /v1/models) and aspect_ratio. Leave quality and size out to pay the model's price.from_usd (its lowest-priced tier). A higher quality, a larger size or input images can cost more: price.tiers lists the price of each quality and size, and price.per_input_image_usd what each input image adds. The task's price_usd is what you are charged. On success outputs is [{"type": "image", "url": "…"}].
Text (chat)
POST /v1/chat/completions speaks the OpenAI Chat Completions format, so the OpenAI SDKs and tools built on them work as they are: set the base URL to https://api.opennan.com/v1 and use your opennan key. Text models on sale right now: gpt-oss-120b (OpenAI), DeepSeek V4.1 Flash (DeepSeek), DeepSeek V4 Flash (DeepSeek), Llama 3.3 70B (Meta), Llama 4 Scout (Meta), Nemotron 3 Super (NVIDIA), GLM-4.7 (Z.ai), Qwen3.6 35B A3B (Alibaba (Qwen)), Qwen3 235B A22B Instruct 2507 (Alibaba (Qwen)), Gemma 4 31B (Google), gpt-oss-20b (OpenAI), DeepSeek V3.2 (DeepSeek), Nemotron 3.5 Super VL (NVIDIA) and Qwen3.8 27B (Alibaba (Qwen)). GET /v1/models?category=text always lists the ones you can call, with their prices. Use our id or the maker's (gpt-oss-120b, deepseek-v4-1-flash, deepseek-v4-flash, llama-3.3-70b, llama-4-scout, nemotron-3-super, glm-4.7, qwen3.6-35b-a3b, qwen3-235b-a22b-2507, gemma-4-31b, gpt-oss-20b, deepseek-v3.2, nemotron-3.5-super-vl, qwen3.8-27b, openai/gpt-oss-120b, deepseek-v4.1-flash, deepseek-v4-flash-0731, llama-3.3-70b-instruct, llama-4-scout-17b-16e-instruct, nemotron-3-super-120b-a12b, glm-4-7, qwen3-6-35b-a3b, qwen3-235b-a22b-instruct-2507, gemma-4-31b-it, openai/gpt-oss-20b, deepseek-v3-2, nemotron-3-5-super-vl, qwen3-8-27b); an unknown id returns 404. Keys need the api or api:text scope.
import os
from openai import OpenAI
# The only change from OpenAI: base_url and your opennan key.
client = OpenAI(base_url="https://api.opennan.com/v1", api_key=os.environ["OPENNAN_KEY"])
reply = client.chat.completions.create(
model="gpt-oss-20b",
messages=[{"role": "user", "content": "Write a haiku about rain."}],
)
print(reply.choices[0].message.content)
print(reply.usage) # tokens, plus cost_usd: what this call costEvery model takes stream (real token-by-token SSE), stream_options.include_usage, max_tokens / max_completion_tokens and stop (up to 4 sequences of up to 1,000 characters). Output stops at max_tokens (finish_reason: "length"). On some models we measure that cut at about 3 bytes per token, so the reply can be shorter than max_tokens of the model's own tokens; it is billed as max_tokens of output, never more. A model with min_max_tokens refuses a smaller max_tokens (400). A reply cut at a stop sequence is billed up to the cut (on a thinking model, plus its reasoning). Other parameters vary by model: temperature, top_p, seed, response_format, reasoning_effort and the rest are listed per model in supported_parameters (GET /v1/models?category=text). One a model doesn't use is accepted and ignored, and the x-opennan-ignored-params response header names it. A json_schema response format on a model that can't do it returns 400 (unsupported_parameter): use {"type": "json_object"} and describe the schema in the prompt. tools and tool_choice need a model with tools: true, image parts one with vision: true. n must be 1.
Billing is per token, at each model's price per 1M input, cached input and output tokens, at least 10% below the maker's own price where the maker publishes one (a model with no official price shows none). When a call starts we hold the prompt estimate plus max_tokens of output (without max_tokens, the model's default_max_tokens is used), then charge the real usage and give the rest back. Every reply's usage includes cost_usd. A call that fails before the first token is not charged; if you disconnect or the reply stops part-way, you pay for the prompt and the tokens sent. A reply without stream is cut at 9 minutes and a stream at 20 (finish_reason: "length", charged for what was produced), so stream long outputs.
Voice (text to speech)
POST /v1/audio/speech speaks the OpenAI speech format, so client.audio.speech.create in the OpenAI SDKs works as it is: set the base URL to https://api.opennan.com/v1 and use your opennan key. Send model (kokoro, by hexgrad), input (up to 4,096 characters), voice, response_format (mp3, wav or opus) and speed (0.25 to 4). The reply is the audio file itself, not a task, with X-Usage-Characters and X-Usage-Cost-Usd headers.
GET /v1/audio/voices (no key) lists every voice with its id, name, language and gender; filter with ?model=kokoro&language=en-US. The default voice is heart, and the OpenAI names alloy, nova, echo, onyx and fable work too. Billing is per started 1,000 characters (price.billing_block_characters in GET /v1/models?category=voice), so a short call costs price.minimum_usd. A call that fails is not charged. Try it in the playground.
import os
from openai import OpenAI
client = OpenAI(base_url="https://api.opennan.com/v1", api_key=os.environ["OPENNAN_KEY"])
with client.audio.speech.with_streaming_response.create(
model="kokoro",
voice="heart",
input="Hello! This voice was made with one API call.",
) as res:
res.stream_to_file("speech.mp3")Music and stems
POST /v1/music/generations makes one track and returns a task. Send model (ace-step, by ACE Studio & StepFun), prompt (style, mood, instruments, tempo), duration (10 to 240 seconds, default 30) and either instrumental: true or lyrics to have them sung as written; with neither, lyrics are written for you. You pay for exactly the seconds you ask for, and a failed task is refunded. On success outputs is [{"type": "audio", "format": "mp3", "url": "…"}]. Try it in the playground.
POST /v1/audio/separations splits a song (model demucs, by Meta). Send audio_url (a public https link, up to 50 MB and 15 minutes) and stems: vocals (vocals and instrumental) or all (vocals, drums, bass and other). The price follows the song's length, with a $0.01 minimum, and is checked before anything is charged. Each output carries its stem name.
# 1. Start a 30 s instrumental track (returns a task right away)
curl https://api.opennan.com/v1/music/generations \
-H "Authorization: Bearer $OPENNAN_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "ace-step", "prompt": "warm lo-fi hip hop, soft piano", "duration": 30, "instrumental": true}'
# 2. Poll until status is succeeded; outputs[0].url is the mp3
curl https://api.opennan.com/v1/tasks/task_… -H "Authorization: Bearer $OPENNAN_KEY"
# Split a song into vocals + instrumental
curl https://api.opennan.com/v1/audio/separations \
-H "Authorization: Bearer $OPENNAN_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "demucs", "audio_url": "https://example.com/song.mp3", "stems": "vocals"}'Tools catalog
GET /v1/tools (no key) lists every tool that is not a model: video to captions, the voice tools, the 3D tools, song to MIDI and the Roblox tools. Each row has its id, category, method and path, its mcp_tool when there is one, and a status: live (you can call it now), unavailable (the route exists but the tool can't run right now, for example when the song to MIDI importer or a 3D step is off) or soon (announced, not callable yet). Paid tools carry their price (null while the tool can't run) and input limits; free tools say free: true. A tool with a real recorded run carries it as example (its inputs and outputs), and a tool with runs from different inputs carries all of them as examples. Prices in this list are the ones charged. A run that fails is never charged: its hold is refunded in full.
Voice tools
Clone your voice. GET /v1/voices/consent returns text, a sentence with two code words, and a consent_token (valid 15 minutes, for this account only). The speaker reads the sentence aloud. Then POST /v1/voices (multipart) with name, consent=true, consent_token, sample (15 to 60 seconds of only that person's clean speech), consent_audio (the sentence read once) and an optional language; or JSON with sample_url and consent_audio_url (public https). The recording must match the sentence (consent_mismatch), the sample must hold one speaker (multiple_speakers) and the consent must be read by the sample's speaker (consent_speaker); a refused clone is not charged. It returns a voice with an id like vc_…, priced per voice created. A voice is used only by the account that made it, up to 20 voices per account. GET /v1/voices lists your clones and the stock voices; DELETE /v1/voices/{id} deletes a voice everywhere it is kept, and deleting your account deletes all your cloned voices. A consent proof (the sentence, the transcript of the consent recording, the check results and fingerprints of the files; no audio) is kept while the voice exists and for 3 years after it is deleted, then erased. Clone only your own voice, or a voice whose owner has agreed and reads the consent sentence themselves.
Speak in your voice. POST /v1/audio/speech with voice set to your vc_ id (input up to 4,096 characters, response_format mp3, wav or opus, optional language). Returns the audio file, priced per 1,000 characters. MCP: generate_speech with the vc_ voice.
Translate a file in your voice. POST /v1/audio/translations (JSON): audio_url (public https audio or video, up to 50 MB and 15 minutes), target_lang, voice_id (your vc_ id or a stock voice), source_lang (default auto) and callback_url. A task: on success outputs holds the translated MP3, the transcript and the translation as text. Priced per minute of input audio, billed by the second; refunded if it fails. MCP: translate_audio.
Live transcribe and live translate. Two WebSockets: wss://api.opennan.com/v1/realtime/stt (speech in, partial and final transcript out) and wss://api.opennan.com/v1/realtime/translate (speech in, the translation spoken in your cloned voice out). Send {"type": "auth", "key": "sk-…"} first, within 5 seconds (server clients may send an Authorization header instead; a key in the URL is refused), then session.config (translate: source_lang, target_lang, voice_id; transcribe: language), then binary frames of 16 kHz mono 16-bit PCM and finally session.end. Billed per second of audio you send, held a minute at a time and settled each minute; a session ends after 30 seconds without audio or after 30 minutes, with up to 3 live sessions per account. The full message list is in llms.txt.
Keys need the api or api:voice scope. Each tool's exact price is in GET /v1/tools (voice-clone, voice-speak, voice-translate-file, voice-transcribe and voice). A voice step that fails is not charged.
# 1. Get the consent sentence; the speaker reads it aloud and you record it
curl https://api.opennan.com/v1/voices/consent -H "Authorization: Bearer $OPENNAN_KEY"
# 2. Clone the voice: the sample (15-60 s of only that speaker) and the consent recording
curl https://api.opennan.com/v1/voices \
-H "Authorization: Bearer $OPENNAN_KEY" \
-F name="My voice" -F consent=true -F consent_token=vct_… \
-F [email protected] -F [email protected]
# 3. Speak in it
curl https://api.opennan.com/v1/audio/speech \
-H "Authorization: Bearer $OPENNAN_KEY" \
-H "Content-Type: application/json" \
-d '{"voice": "vc_…", "input": "Hello in my own voice.", "response_format": "mp3"}' \
--output me.mp3
# Delete it (removed everywhere it is kept)
curl -X DELETE https://api.opennan.com/v1/voices/vc_… -H "Authorization: Bearer $OPENNAN_KEY"Video to captions
POST /v1/pipelines/video-to-captions/runs makes timed captions for a video and returns a task. Send a multipart form with file (MP4, MOV, WEBM or MKV, up to 200 MB and 10 minutes) or url (a public https link), or JSON with url. Options: source_lang (the spoken language, default auto), target_lang (optional: also translate the captions), burn_in (default true: an MP4 with the captions burned in, in target_lang when translating) and callback_url. Keys need the api or api:video scope.
On success outputs holds the captioned MP4 (with burn_in), SRT and VTT files for the original language and for the translation, the transcript and translation as text, and every caption line with its start and end in seconds, text and translation. Priced per minute of video, billed by the second (captions in GET /v1/tools); refunded if it fails, including when no speech is heard. Up to 2 videos are read at a time per account. MCP: video_to_captions (with a url). Caption only videos you own or have the right to use.
# 1. Caption a video and translate the captions into English (returns a task)
curl https://api.opennan.com/v1/pipelines/video-to-captions/runs \
-H "Authorization: Bearer $OPENNAN_KEY" \
-F [email protected] \
-F source_lang=auto \
-F target_lang=en
# Or JSON with a public https link
curl https://api.opennan.com/v1/pipelines/video-to-captions/runs \
-H "Authorization: Bearer $OPENNAN_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com/talk.mp4", "burn_in": true}'
# 2. Poll until status is succeeded: SRT, VTT, the captioned MP4 and every line
curl https://api.opennan.com/v1/tasks/task_… -H "Authorization: Bearer $OPENNAN_KEY"3D and Roblox
Four 3D micro tools, each a task with its own price in GET /v1/tools (price and limits) and its own MCP tool. Keys need the api or api:3d scope; Idempotency-Key and callback_url work on all of them, and a failed run is refunded in full. When a step can't run, the tool shows unavailable in GET /v1/tools.
POST /v1/3d/charactersA rigged character from image (https) or prompt (up to 600 characters): an image that is not a strict front T-pose is redrawn as one first (identity kept), then modelled and rigged. Options: quality standard | high, animations (walk and run clips, default true), prompt_hint. Outputs the T-pose image, GLB, FBX, thumbnail and the rigged FBX and GLB. MCP generate_3d_character.
POST /v1/3d/tposeA strict front T-pose of the character in image (https), with an optional prompt_hint. Checked first and redrawn only when needed, at a lower price when no redraw was needed. The task's result has image_url, was_tpose and changed. MCP generate_tpose_image.
POST /v1/3d/modelsA textured 3D model (GLB, FBX and a thumbnail) from image or prompt; kind prop (default) or character, quality standard | high, optional engine (an id from GET /v1/models?category=3d). MCP generate_3d_model.
POST /v1/3d/rigRig one humanoid model (model_url: an https GLB, or a GLB or FBX from /v1/3d/models): a rigged FBX and GLB, plus walking and running clips with animations true (no extra cost). MCP rig_3d_model.
POST /v1/roblox/modelsA Roblox-ready 3D item (kind item) or avatar body in T-pose (kind body) from exactly one of prompt (up to 600 characters) or image (https or a base64 PNG or JPEG, up to 10 MB). Outputs FBX, GLB and a PNG thumbnail; import the FBX in Roblox Studio. Fixed price per model, listed in GET /v1/models?category=roblox.
Poll GET /v1/tasks/{id}; each step takes a few minutes. Output files are served from our CDN; a task with no project_id keeps them 7 days (its expires_at). Roblox clothing is free and needs no key: see Free tools.
Webhooks & callbacks
Instead of polling, add a webhook in the dashboard for task.succeeded and task.failed. Each delivery is a POST with {"event": "task.succeeded", "data": {…the task…}, "timestamp": "…"}, signed: X-Webhook-Signature is the hex HMAC-SHA256 of the raw body with your endpoint secret. Compute it on your side and compare before you trust the body.
You can also pass callback_url (https) on a request to get that one task POSTed back with the same body and an X-Opennan-Event header. Callbacks carry no signature you can check, so treat them as a hint and read the task with GET /v1/tasks/{id} before acting on it, or use a webhook for signed deliveries.
Errors
Every error has the same shape: {"error": {"type": "…", "message": "…", "code": null}} (code is set for some errors, e.g. context_length_exceeded).
| HTTP | type | Meaning |
|---|---|---|
| 400 | invalid_request_error | A field is missing or invalid (the message says which). |
| 401 | authentication_error | No key, or the key is wrong, revoked or expired. |
| 402 | insufficient_balance | Not enough balance, or a daily/monthly spend limit would be passed. Top up in the dashboard. |
| 403 | permission_error | The key has no API access, or not for this category. Create a key with Access → Full API. |
| 404 | not_found | Unknown model or task (tasks are only visible to the account that made them). |
| 409 | idempotency_error | The Idempotency-Key was already used for a different request. |
| 409 | not_ready | A song to MIDI file was requested before its transcription finished. Poll it until status is succeeded. |
| 409 | task_running | DELETE /v1/tasks/{id} on a task that is still queued or running. Delete it once it has succeeded or failed. |
| 413 | invalid_request_error | The body is too large for this endpoint: 1 MB for JSON generation requests, 256 KB for speech and music, 10 MB for chat and MCP, 15 MB for Roblox artwork, 25 MB per file for a voice clone, and 200 MB for song to MIDI files and video to captions uploads. |
| 429 | rate_limit_error | Too many requests. Wait the seconds in the Retry-After header and retry. |
| 502 | server_error | A text reply stopped part-way (code stream_interrupted): you were charged only for the tokens it used, and with stream: true you keep what arrived. Or a Roblox design could not be drawn (code design_failed): try again. |
| 503 | unavailable | The service could not start the job right now. You were not charged. Retry later. |
| 503 | busy | The free song to MIDI importer is full, or video to captions is busy. Try again in a few minutes. |
Idempotency & retries
Send Idempotency-Key: <your id> on POST requests. Retrying with the same key and the same body returns the original task and never charges twice; the same key with a different body returns 409. Safe to retry on network errors and on 429/503.
Rate limits
Per account: 60 generation requests per minute and 1,000 per hour. Reading tasks and models is not limited this way. Need more? Contact us.
Text, per API key: 120 requests per minute and 3,000 per hour, 2,000,000 tokens per minute (prompt plus max_tokens, counted when a call starts) and 8 calls running at once. Per account, every key combined: 6,000,000 tokens per minute and 24 calls running at once. A 429 carries a Retry-After header.
Speech, per account: 60 requests per minute and 1,000 per hour.
Voice tools, per account: up to 3 live sessions at a time, 20 cloned voices kept, and 3 clones per 10 minutes (5 an hour, 20 a day). Video to captions: up to 2 videos read at a time per account.
Free tools, per IP address: 3 songs to MIDI a day, and 5 Roblox clothing designs from a prompt plus 30 from your own artwork a day.
MCP for AI agents
One Streamable HTTP MCP server at https://api.opennan.com/v1/mcp exposes the catalog as tools. An agent can list the models, start a job and wait for it: get_task with wait_seconds (max 50) waits without polling code. Pass idempotency_key to any tool that starts a task (generate_video, generate_image, generate_music, separate_audio, translate_audio, video_to_captions and the four 3D tools) so a retry after a timeout never starts (or charges) a second task. A failed call comes back with isError: true and the same error JSON as REST.
One server gives your AI app every tool on this page (video, images, chat, voice, music, sound effects, 3D, captions and dubbing). Pick your app below and follow the steps: Codex, Claude Code, Claude Desktop, Cursor, VS Code, Windsurf, Gemini CLI, OpenCode, or any client that speaks Streamable HTTP.
Opennan zu deiner KI-App hinzufügen
Eine Installation gibt deiner KI jedes Opennan-Tool: Video, Bilder, Chat, Stimme, Musik, Soundeffekte, 3D, Untertitel und Synchronisation. Wähle deine App und folge den Schritten.
https:/ / api.opennan.com/ v1/ mcpFunktioniert in der Codex CLI, der Codex-App und der Codex-IDE-Erweiterung: Sie teilen ~/.codex/config.toml. Sieht die App oder IDE OPENNAN_KEY nicht, nimm die config.toml-Variante mit deinem Schlüssel. Codex fragt vor jedem Tool-Aufruf: Bestätige, um weiterzumachen.
API-Schlüssel holen
Erstelle einen Schlüssel in der Konsole. Er beginnt mit sk- und nutzt dasselbe Guthaben wie die Website.
Schlüssel erstellenSpeichere deinen Schlüssel als OPENNAN_KEY
# macOS / Linux (zsh shown; bash: ~/.bashrc) echo 'export OPENNAN_KEY="sk-..."' >> ~/.zshrc source ~/.zshrc # Windows (PowerShell), then open a new terminal setx OPENNAN_KEY "sk-..."Installiere das Opennan-Plugin: der Server plus ein Skill, der deiner KI beibringt, Modelle zu wählen, Preise zu bestätigen und auf Ergebnisse zu warten
codex plugin marketplace add Opennan/opennan-ai-plugin codex plugin add opennan@opennanOder füge nur den Server hinzu
codex mcp add opennan --url https://api.opennan.com/v1/mcp --bearer-token-env-var OPENNAN_KEYOder füge das in ~/.codex/config.toml ein
[mcp_servers.opennan] url = "https://api.opennan.com/v1/mcp" http_headers = { Authorization = "Bearer sk-..." }Prüfe, ob opennan aufgelistet ist
codex mcp listStarte Codex neu und frag nach allem, was du willst
“Mach ein 5-Sekunden-Video von Regen auf einer Neonstraße, einen passenden Song und ein Posterbild.”
Ersetze sk-... durch deinen eigenen Schlüssel und halte ihn geheim: Wer ihn hat, kann dein Guthaben ausgeben. Deine App kann die Tools ohne Schlüssel auflisten; zum Erstellen brauchst du einen.
Pricing & refunds
Pay per call from a prepaid USD balance, with no plan. Each model shows its price per call on Models and in the task (price_usd). We hold the price when you submit and charge only on success; failed tasks are refunded automatically (error.refunded: true).
Where the model maker publishes an API price, no tier is above it for the same quality, length and audio setting (text: for each token meter, against the maker's own price for it), and most are at least 10% below. A text meter sold at exactly the maker's price says at_maker_price: true (with vs_official_percent: 0); no saving is claimed for it. Models with no official price (open-source models, or models whose maker sells no API) make no comparison: their price.vs_official is null and they say no_maker_price: true. price.matrix in GET /v1/models lists every tier with our price, the official price and vs_official_percent (rounded down); video price.tiers gives the exact price of every quality, sound setting and length. A tier less than 10% below the official price carries below_maker_price: false and no official price or percent. A tier we can't sell below the official price is not offered: it is left out of the model's qualities, and a call for it returns 400 naming a tier that is.