Theme
Language

Media generation

POST /api/media/generate — asynchronous generation of images, video, TTS, music and 3D. Parameters model, prompt, opts. Poll with GET /api/media/jobs/{id}.

Async media generation: images, video, voiceover (TTS), speech-to-text, music and 3D. You create a task and poll the result. The full model list is in the media-models catalog; webhooks, errors and billing are in the webhook section.

POSThttps://getvibeapi.com/api/media/generate
GEThttps://getvibeapi.com/api/media/jobs/{id}

Create parameters

ParameterTypeDescription
modelreq.stringA media model name, e.g. veo-3.1 or gpt-image-2 (see the catalog).
promptreq.stringText description.
optsobjectAdditional generation options (model-dependent): resolution, duration and so on.

Create and poll

Creating returns 202 with { ok: true, jobId, status: "processing" }. Poll GET /api/media/jobs/{jobId} until status: "success", then take the resultUrls.

# 1. create a generation job
curl https://getvibeapi.com/api/media/generate \
  -H "Authorization: Bearer $GETVIBE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "veo-3.1",
    "prompt": "a cat on a skateboard, slow motion"
  }'
# → {"ok":true,"jobId":"...","status":"processing"}
Billing is based on the actual generation volume (resolution, duration). If the model is unavailable or the request is rejected, the response comes with ok: false and no charge. Site prices are indicative; the exact charge is returned in the x-pv-cost-rub header of each accepted response. Stream bodies are unchanged by default; send x-pv-cost: 1 to add the cost to that protocol's final usage event.

Advanced parameters (opts)

Besides model and prompt, you can pass extra options in opts. The set depends on the model. The full machine-readable contract is via GET /api/model-capabilities (or GET /api/model-capabilities/{model} for a specific model).

Commonly used advanced parameters
ParameterTypeDescription
aspect_ratiostringAspect ratio: 1:1, 16:9, 9:16, 4:3, 3:4.
durationstring|numberVideo duration in seconds (5, 10, 15).
resolutionstringResolution: 480p, 720p, 1080p, 4K.
image_urlstring (URL)Source image for I2V / image-to-image.
negative_promptstringWhat to exclude from generation (qwen-image, wan-2-7).
guidancenumberGuidance scale — prompt adherence (flux-2, qwen-image, seedance).
seednumberSeed for a reproducible result (flux-2).
stabilitynumberStability 0–1 (hailuo, happyhorse, seedance, TTS).
camerastringCamera motion, e.g. "pan left" (kling-3.0, hailuo).
first_frame_urlstring (URL)First frame for I2V (kling-3.0, wan-2-7).
last_frame_urlstring (URL)Last frame for I2V (seedance, kling-3.0, wan-2-7).
reference_imagestring (URL)Style/character reference (happyhorse).
similarity_boostnumberVoice similarity 0–1 (ElevenLabs TTS).
speednumberSpeech rate 0.5–2 (ElevenLabs TTS).
All parameters are optional unless stated otherwise. Unknown parameters are ignored. The full list with types, ranges and examples is in the GET /api/model-capabilities/{model} response.

Your own files: base64 instead of a link

URL options (image_url, input_urls, mask_url and the rest) are links the provider fetches, so they accept only public https:// addresses. An inline data:image/...;base64,… string in one of those fields is rejected with 400.

To send your own file, use the matching *_base64 field instead: the service stores the bytes, publishes them and passes the resulting link to the provider. Up to 16 files per request in input_base64.

Inline files
ParameterTypeDescription
image_base64stringSource image. Replaces image_url.
mask_base64stringInpainting mask (image/* only). Replaces mask_url.
input_base64string[]References for image-to-image and multi-input. Replaces input_urls.
audio_base64stringSource audio. Replaces audio_url.
video_base64stringSource video. Replaces video_url.
The value is the full data:image/png;base64,… string including the prefix — the file type is read from it.

Request contract: strict, quote_id, max_price_rub

POST /api/media/generate and POST /api/media/price accept the same body, so you can quote it and then submit it unchanged. There are no other top-level fields: an unknown one is refused on both endpoints with 400 and code unknown_field naming it (field, fields), with or without strict. Generation parameters go inside opts.

Top-level fields
ParameterTypeDescription
modelreq.stringPublic model id or one of its routes (base:alias).
promptstringText; required only when the model operation requires it.
optsobjectModel options. Exact schema: operations[].requestJsonSchema in GET /v1/model-capabilities/{model}.
strictbooleanStrict mode (or the header x-getvibe-strict: 1): a request that would not run exactly as written (a parameter dropped, a value clamped, a key outside the model contract) is refused with 422 strict_validation_failed and a violations list, before any quote or charge. A documented field the model accepts and ignores (duration on source-length models) is not a violation: it comes back as a param_ignored warning.
quote_idstringmq_… from POST /api/media/price. The job runs only at that price; a stale, used or re-routed quote is refused with 409/422 before anything runs.
max_price_rubnumberCeiling for this one job in the account currency. If the quote is higher: 422 media_max_price_exceeded and nothing runs. A metered job is never charged above it.
webhook_urlstringPublic https URL that receives a signed completion notification.

Files by id (Files API)

Any source file can be sent three ways: as a link (image_url, audio_url, video_url…), as inline bytes (image_base64, audio_base64, video_base64…) or as the id of your own file from POST /v1/files. The id fields are listed in every operation’s requestJsonSchema and are accepted in strict mode.

ParameterTypeDescription
image_file_idstringInstead of image_url.
audio_file_idstringInstead of audio_url.
video_file_idstringInstead of video_url.
mask_file_idstringInstead of mask_url.
first_frame_file_id / last_frame_file_idstringInstead of first_frame_url / last_frame_url.
input_file_idsstring[]Appended to input_urls.
reference_*_file_idsstring[]Appended to reference_image_urls / reference_audio_urls / reference_video_urls.

Video priced by the input file’s length

For models whose output length follows an uploaded track — kling-ai-avatar, omnihuman-v2, omnihuman-2-0, omnihuman-v1.5, omnihuman-1-5, volcengine-lipsync (audio), kling-motion-control, wan-animate, topaz-video-upscale, p-video-edit (video), lipsync-2-pro, sync-3, react-1 (both inputs) — /api/media/price and the max_price_rub pre-flight on /api/media/generate measure that file server-side (link, data: URI, base64 or *_file_id) and price the measured seconds. The answer carries sourceSeconds (outputSeconds for lip-sync), and estimateReason says the length was measured. If the file cannot be read, the price keeps its previous basis (one second, the catalog estimate or the route maximum) and estimateReason says so. The charge is the per-second rate × the seconds actually rendered, never above max_price_rub. opts.duration on these models is accepted and ignored with a param_ignored warning.

Voice cloning

POSThttps://getvibeapi.com/api/media/voices
GEThttps://getvibeapi.com/api/media/voices
POSThttps://getvibeapi.com/api/media/voices/{voice_id}/samples
DELETEhttps://getvibeapi.com/api/media/voices/{voice_id}

Create: { name, consent: true, engine, audio_url | audio_base64 } or several recordings samples: [{ audio_url } | { audio_base64 }, …] (engine: "elevenlabs" only). An ElevenLabs voice is used by voice-clone-tts and by the Voice Changer (elevenlabs-voice-changer). Appending recordings to an ElevenLabs voice is free. Every recording is downloaded and measured before the supplier is called: a refusal changes and charges nothing.

Limits and error codes
ParameterTypeDescription
one recording≤ 10 MBwav/mp3, up to 60 s. Larger: 422 voice_sample_too_large / voice_sample_invalid.
recordings per voice≤ 25Counting those already added. More: 422 voice_samples_limit.
voice length≤ 300 sAll recordings together, counting those already added. Longer: 422 voice_samples_limit; shorter than the engine minimum: 422 voice_samples_invalid.
recordings size≤ 50 MBAll recordings of one request together. More: 422 voice_samples_too_large.
request body≤ 70 953 644 B50 MB of recordings as base64 (×4/3) plus JSON. Larger: 413 payload_too_large (JSON, with max_body_bytes). Send long sets in parts via …/samples, or as audio_url links.
additions per 24 h20Per client. More: 429 voice_samples_rate_limited.
Other answers: 404 voice_not_found (unknown, deleted or another account’s voice), 409 voice_samples_unsupported (recordings can be added only to an ElevenLabs voice), 409 voice_slots_full, 502 voice_samples_failed (the supplier refused the recordings; the voice is unchanged). An ElevenLabs voice is billed monthly; prices are in GET /api/media/voices (pricing).

Voice Changer and Forced Alignment

ParameterTypeDescription
elevenlabs-voice-changermodelopts.voice_id (your ElevenLabs voice) + the source recording opts.audio_url | audio_base64 | audio_file_id, up to 5 minutes. Priced on the measured length: 1 minute = 1000 characters.
forced-alignmentmodelprompt is the transcript, opts.audio_url | audio_base64 | audio_file_id the recording. Result: word timings in resultMeta.words and an SRT file in resultUrls. Priced per 1000 characters of text.

Sandbox: unknown submit outcome

When the supplier’s start confirmation does not arrive, the job stays in status: "submitting" with stage: "submission_unknown", errorCode: "media_submission_uncertain" and failure.advice: "reconcile_first" — keep polling the same jobId and do not create a duplicate. To test this path at no cost, send POST /api/media/generate with a sandbox key and the header x-pv-sandbox-outcome: submission_uncertain. The answer is a normal 202 with a jobId; polling it answers as an unknown submit for 60 seconds, then as a job that never started (failure.code: "submission_not_started", charged: false). Nothing is stored, sent to a supplier or charged. A regular key with this header gets 400 sandbox_outcome_requires_sandbox_key.

Speech-to-text (transcription)

Same endpoint as the rest of generation: put the STT model name in model, leave prompt as an empty string, and pass the recording in opts.audio_url. The transcript arrives in resultText on GET /api/media/jobs/{id}; resultUrls stays empty — transcription produces no file.

# speech-to-text: create the job, then read the transcript
curl https://getvibeapi.com/api/media/generate \
  -H "Authorization: Bearer $GETVIBE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "whisper-large-v3-turbo",
    "prompt": "",
    "opts": {
      "audio_url": "https://example.com/meeting.mp3",
      "language_code": "en"
    }
  }'
# → {"ok":true,"jobId":"JOB_ID","status":"processing","priceRub":0.03}

curl https://getvibeapi.com/api/media/jobs/JOB_ID \
  -H "Authorization: Bearer $GETVIBE_API_KEY"
# → {"status":"success","resultText":"the transcribed text…","resultUrls":[],"priceRub":0.03}

Models

  • whisper-large-v3-turbo — $0.00025 per minute of audio. Cheapest and fastest, 99 languages.
  • nemotron-asr-multilingual — $0.00025 per minute. NVIDIA multilingual ASR, punctuates on its own.
  • whisper-large-v3 — $0.000563 per minute. Full Whisper: slower than turbo, more accurate on noisy audio.
  • universal-2 — $0.003125 per minute.
  • speech-to-text — $0.01 per minute (ElevenLabs Scribe). Supports speaker diarization and audio-event tags.

Parameters

ParameterTypeDescription
audio_urlreq.string (URL)Public https link that serves the file directly. Redirects and private network addresses are refused. Use audio_base64 instead if the file is not online.
audio_base64stringThe recording inline as data:audio/mpeg;base64,… Replaces audio_url.
language_codestringLanguage code (en, ru, …). Detected automatically when omitted.
duration_minutesnumberLength of the recording in minutes — used for the up-front price estimate. The final charge is measured (see below).
diarizebooleanSpeaker diarization (universal-2, speech-to-text).
tag_audio_eventsbooleanTag audio events such as laughter or applause (speech-to-text).
keytermsstring[]Hint list of terms and names (speech-to-text; +30% price).

Formats and limits

mp3, wav, m4a/mp4, flac, ogg/opus and webm are accepted. File size is 25 MB maximum — roughly 26 minutes of 128 kbps mp3, or several hours of speech in opus. Anything larger is refused immediately with an explicit message and is not billed; split the recording instead.

Billing is per minute of audio. For whisper-large-v3-turbo, whisper-large-v3 and nemotron-asr-multilingual the final amount is computed from the measured length of the recording, taken from the transcript — not from whatever you passed in duration_minutes. The minimum charge is one kopeck. The exact amount is always returned as priceRub on the finished job.

Readiness webhook (instead of polling)

To avoid polling, register one address mediaWebhookUrl in your dashboard. When the task completes (success or error), the service makes up to five delivery attempts of a POST with a JSON body:

// POST to your mediaWebhookUrl when a job finishes
{
  "jobId": "...",
  "status": "success",            // or "fail"
  "model": "veo-3.1",
  "kind": "video",                // image | video | audio | music | 3d
  "resultUrls": ["https://getvibeapi.com/api/media/file/..."],
  "failMsg": null,
  "createdAt": "2026-06-17T10:00:00.000Z"
}
Delivery details, error codes and billing are in the Webhook and media task lifecycle. In short: the webhook is signed with Standard Webhooks headers, duplicates are possible, and delivery stops after five failed attempts. Polling remains the source of truth.

Fetching the file: /api/media/file/{id}/{idx}

GEThttps://getvibeapi.com/api/media/file/{id}/{idx}

The generation result URL (image, video, audio or 3D model) from resultUrls. Use it as returned by the service: it opens without an API key and works in <img>, <video> or a browser. The provider is hidden: the upstream address is resolved server-side, the client never sees it.

Path and query parameters
ParameterTypeDescription
idreq.string (path)Task id (jobId) from the /api/media/jobs/{id} response.
idxreq.integer (path)Result index (0-based). For a single result — 0.
query paramsas in resultUrlsDo not build the URL yourself or modify the parameters returned by the service.
downloadstring (query)download=1 returns the file as an attachment whose name and extension follow the content (.glb for a 3D model). Without it the file opens in the browser.
# The signed URL is returned in resultUrls of the /api/media/jobs/{id} response
# or in the webhook body. Opens directly in the browser / <img> — no key needed.
https://getvibeapi.com/api/media/file/JOB_ID/0?sig=HMAC_SIGNATURE
Results are kept for 7 days after the job completes (the resultsExpireAt field of /api/media/jobs/{id}). The finished file is copied to our storage and resultUrls switch to a permanent address of the form https://getvibeapi.com/media/r/…; a previously returned /api/media/file/… URL keeps working too. Both support Range requests (seeking in video and audio). After 7 days the file is deleted and the URL answers 410 Gone with the code media_result_expired — download what you need before then.
GetVibe API media generation — image, video, audio, music, 3D | GetVibe API