Reelora API

REST API for programmatic integration of subtitles, voiceover, transcription, dubbing, and pause removal. The same capabilities as the Telegram bot — via HTTP. Available on all plans, including Free.

Endpoints

MethodPathDescription
GET/healthService health check
GET/v1/meProfile, limits, usage
POST/v1/uploadsUpload file
POST/v1/tasksCreate task
GET/v1/tasksList of tasks
GET/v1/tasks/{id}Task status + result

Quick Start

1

Get API Key

In the bot @reelora_ai_bot send /apikey or open Settings → 🔑 API Access. The key is shown only once.

2

Upload File

POST /v1/uploads → get upload_id

3

Create Task

POST /v1/tasks with type and parameters

4

Get Result

Poll GET /v1/tasks/{id} or specify webhook_url

Authentication

All requests (except /health) require the header:

X-API-Key: reel_xxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
⚠️The key is shown only once. If lost — run /revokekey in the bot and generate a new one via /apikey.
API is available on all plans. Limits are the same as in the bot — Free, Standard, Pro, Pro+.

Base URL

https://api.reelora.cc

Interactive Swagger documentation: https://api.reelora.cc/docs

Status Check

GET/healthno auth required
curl https://api.reelora.cc/health
# {"status":"ok","service":"reelora-api"}

Profile and Limits

GET/v1/me
curl https://api.reelora.cc/v1/me -H "X-API-Key: reel_..."
{
  "user_id": 618899534, "tariff": "pro_plus",
  "tariff_expires_at": "2026-07-17T07:20:20",
  "limits": {
    "seconds_per_month": 36000, "tts_chars_limit": 1000000,
    "elevenlabs_chars_limit": 25000, "transcription_seconds": 72000,
    "max_photos": 30, "max_video_duration": 600, "max_file_size_mb": 500,
    "can_translate": true, "can_dub": true, "premium_voices": true
  },
  "usage": {
    "used_seconds_this_month": 705, "used_chars_this_month": 12674,
    "used_eleven_chars_this_month": 0, "used_transcription_seconds": 0,
    "limit_reset_date": "2026-07-17T07:18:07"
  },
  "api_key_prefix": "xxxxxxxx", "api_key_created_at": "2026-06-19T22:43:23"
}

File Upload

POST/v1/uploads

Uploads a file and returns upload_id. Files live for 2 hours. Limit: 20 uploads/min.

curl -X POST https://api.reelora.cc/v1/uploads \
  -H "X-API-Key: reel_..." -F "file=@photo.jpg"
# {"upload_id":"550e8400-...","filename":"photo.jpg","size_bytes":245891}
TypeFormatsMax.
ImagesJPEG, PNG, WebP500 MB
VideoMP4, MOV, AVI500 MB
AudioMP3, WAV, M4A500 MB
SubtitlesSRT

Use upload_id in the photos, file, srt_file, bgm_file fields. Alternatively, you can pass direct https:// URLs instead of uploading.

Task Creation

POST/v1/tasks202 Accepted

Task type is defined in the "type" field. Executed asynchronously. All types support webhook_url and send_to_telegram.

{ "task_id": 744, "status": "pending" }

Type: video — video creation

Photos or video clips + voiceover + karaoke subtitles + music → MP4.

{
  "type": "video",
  "photos": ["upload_id_1", "https://example.com/img.jpg"],
  "text": "Text for voiceover",
  "scene_durations": null, "scene_transitions": null, "target_duration": 30,
  "format": "9_16", "animation": "ken_burns", "transition": "random", "transition_duration": 0.5,
  "tts_provider": "google", "tts_voice": "Chirp3-HD-Aoede",
  "tts_scene": "Speak like a news anchor.",
  "subtitles": true, "sub_style": "yellow_basic", "sub_position": "bottom",
  "bgm_track": "chill_background-lofi-vibes-113884.mp3", "bgm_volume": 0.2
}
FieldDefaultDescription
photosrequiredupload_id or https:// (images or video clips, max. 30)
textoptionalnullVoiceover text. Without text — silent video. Insert scene markers between paragraphs to switch the shot (and optionally pick the transition): {scene} · {scene:slide} · {scene:zoom:0.8} · {scene:0.8}
scene_durationsoptionalnullExplicit per-media durations in seconds, e.g. [10, 15, 5]. Length must equal media count. Overrides {scene} auto-timing, works even without voice
scene_transitionsoptionalnullExplicit transition per cut (length = photos − 1), e.g. ["slide","zoom"]. Alternative to {scene:type} markers
target_durationoptionalnullTarget clip length in seconds. If the result is longer it is sped up (video+audio+subtitles in sync) to fit. Shorter is left untouched. Speed-up capped at ×2. Must be ≤ your plan limit
formatoptional"9_16""9_16" · "16_9" · "1_1" · "original"
animationoptional"none""none" · "ken_burns"
transitionoptional"crossfade"Global transition for unmarked cuts: none · random · fade · dissolve · slide · cover · reveal · zoom · squeeze · wind · pixel · circle (crossfade = alias of dissolve). Applies only when animation ≠ none
transition_durationoptional0.5Transition length in seconds. Per-cut length can be set via {scene:type:sec}
tts_provideroptional"google"TTS provider (see Providers section)
tts_voiceoptionalfrom profileVoice name (see Voices section)
tts_speedoptional1.00.8 · 1.0 · 1.2 · 1.5
languageoptionalVoiceover language — any code from the Languages section ("ar", "fa"…) or a full locale "ru-RU". For the long tail use tts_provider:"edge"
tts_sceneoptionalnullSpeech style instruction (for Gemini TTS)
tts_styleoptionalnullVoice style preset: standard · calm · expressive · energetic · dramatic · cheerful (Gemini and ElevenLabs)
subtitlesoptionaltrueKaraoke subtitles
sub_styleoptional"yellow_basic"Subtitle style
sub_positionoptional"bottom""top" · "center" · "bottom"
sub_sizeoptional"medium""small" · "medium" · "large"
sub_coloroptionalnullASS color: "&H0000E6FF" (yellow)
text_overlaysoptionalnullStatic captions burned over the frame, independent of the voice-over (see Text on video below)
text_overlays_rawoptionalnullThe same feature as a compact “codes” string, parsed on the server (see below). Used only if text_overlays is absent
auto_textoptionalfalse⚡ Auto-text. An LLM picks the strongest keywords from the voice-over and adds them as animated on-screen captions, timed to the TTS word timestamps (see Auto-text below). Requires text. Counts against your monthly auto-text quota
bgm_trackoptionalnullFile name from the music library (see BGM section)
bgm_fileoptionalnullupload_id of your own MP3 file (mutually exclusive with bgm_track)
bgm_volumeoptional0.2Music volume 0.0–1.0
audio_mix_typeoptional"replace"Mixing for video clips: "replace" · "add" · "overlay"

audio_mix_type — for clips with original sound

ValueBehavior
replaceTTS/BGM only — clip sound is removed
addTTS on top of original (both at full volume)
overlayTTS at full volume + original audio is ducked

🎬 Dynamic scenes — when the shot changes

  • With voice: insert {scene} markers in text between paragraphs. Each segment is timed by the narration and mapped to media in order. Markers are stripped before TTS — the voice never reads them.
  • Without voice / explicit: pass scene_durations (e.g. [10, 15, 5]) — one value per media. Video clips are trimmed/looped to fit, photos are shown for the scene length.
  • Scenes ↔ media: equal → 1:1; fewer scenes → the last splits across the rest; more → the last media holds the leftover.

✨ Per-cut transitions — how the shot changes

  • In text: extend a marker with a type and/or duration: {scene:slide}, {scene:zoom:0.8}, {scene:0.8} (duration only). The transition applies to the cut into that scene.
  • Explicit array: scene_transitions (length = photos − 1), e.g. ["slide","zoom","fade"] — one transition per cut. Handy for silent videos with explicit durations.
  • Types: fade · dissolve · slide · cover · reveal · zoom · squeeze · wind · pixel · circle · random. Directional ones (slide/cover/reveal/wind) pick a random direction per cut.
ℹ️Transitions render only when animationnone. A per-cut none is not supported — it falls back to the global transition.

🅰️ Text on video — static captions

Add arbitrary captions/titles on top of the video, independent of the subtitles. Works with or without narration. Each caption has its own style, position, timing and fade in/out. Two ways to pass them:

Structured — text_overlays (array, max 10):

"text_overlays": [
  { "text": "BIG TITLE", "style": "bold_yellow", "size": "large",
    "position": "top_center", "start": 0.0, "end": 3.0, "fade": true, "anim": "pop" },
  { "text": "the end", "style": "soft_pink", "position": "bottom_center", "start": 3.0 }
]
Field
textrequiredrequired, max 200 chars
startoptionalsec. null → from 0
endoptionalsec. null → until end of video
styleoptionaltitle style (below)
sizeoptionalsmall · medium · large
positionoptional9-grid: top_left · top_center · top_right · middle_left · center · middle_right · bottom_left · bottom_center · bottom_right
bold / italicoptionaloverrides the style weight/slant
coloroptionalASS hex &HAABBGGRR, e.g. "&H00B469FF" (pink)
fadeoptionalsmooth fade in/out (300 ms)
animoptionalAppearance animation: pop · slide_left · slide_right · type (typewriter). Overrides fade

Title styles (style): clean_white · bold_yellow · outline_black · soft_pink · elegant_script · news_bar · impact_red.

Codes — text_overlays_raw (string). A universal English syntax (same in every UI language), parsed on the server. One caption per line (or separated by /); fields inside a caption separated by |:

TEXT | attributes | start-end
"text_overlays_raw": "Hello | large italic center | 0-3 / Subscribe | bottom pink pop | 3-end"
  • size — small · medium · large
  • weight/slant — bold · thin · italic
  • vertical — top · middle · bottom; horizontal — left · center · right
  • color — white · yellow · red · green · blue · pink · orange · purple · black · cyan
  • time — 0-3, 2.5-8, 5-end (sec, dot/comma). No time → caption shows the whole clip
  • animation — pop · slide · type (typewriter) · fade · none
📋Example: first caption — “Hello” large italic centered 0–3 s; second — “Subscribe” bottom pink from 3 s to the end. Attributes and time are optional and order-independent. Unknown tokens are ignored (never fail the request).

⚡ Auto-text — automatic keyword captions

auto_text: true turns on ⚡ Auto-text. Instead of positioning captions yourself, an LLM reads the recognized words of the voice-over, picks the strongest ones (names, numbers, punchy claims) and renders them as animated captions, exactly on the TTS word timestamps. It chooses a modern style, color and appearance animation (pop / slide / typewriter) per word, stacks words spoken together and keeps out of the subtitle zone.

  • Requires a voice-over (text non-empty). With no narration there are no word timestamps, so auto_text is ignored.
  • Additive: generated captions are appended to your text_overlays / text_overlays_raw, they don't replace them.
  • Fully automatic: there are no per-word parameters. If the LLM matches nothing, the render simply proceeds without the extra captions (never fails the request).
  • Quota: each successful auto-text render consumes one unit of your monthly allowance (auto_text_limit; free plans limited, paid plans unlimited). Over the limit → 429.
  • Auto-text works across every API task type: video, dubbing and subtitles. Translation over the API is done through subtitles with translate_to (there is no separate translation type), so auto_text covers translated subtitles too.

🎬 AI-tables — animated cards over the video

ai_graphics: true turns on 🎬 AI-tables (smart scenes): animated cards drawn over the video — lists, numbers, quotes, comparisons, step lists, bar/percent charts, trend arrows, plus subscribe / like call-to-action cards. Cards avoid faces and your subtitle zone automatically, and are sized for vertical (Reels) playback. Available on video, subtitles (cards in the subtitle language) and dubbing (cards in the dubbing language).

  • ai_graphics_mode: auto — an LLM picks the moments from the voice-over (needs narration); semi — auto plus your directives (directives win on overlap); manual — only your directives (works on a silent video).
  • ai_graphics_density: 0.6 (rare) · 1.0 (normal) · 1.5 (dense).
  • ai_graphics_directives (manual / semi): one card per line — MM:SS[-MM:SS] template args [@position]. Templates: caption, quote, subscribe, like, list, steps, stat, percent, trend, compare. Positions: @center @top @bottom @left @right.
  • ai_graphics_sfx: a short sound as each card appears (see Sound effects). null = silent.
  • Quota: each successful render consumes one unit of your monthly ai_graphics_limit. Over the limit → 429; plans without the feature → 403. Never fatal: if nothing is selected, the render proceeds without cards.
{
  "type": "video", "photos": ["upload_abc"],
  "text": "Three things to pack for Hawaii...",
  "ai_graphics": true, "ai_graphics_mode": "auto",
  "ai_graphics_density": 1.0, "ai_graphics_sfx": "whoosh"
}
// manual directives example
{
  "ai_graphics": true, "ai_graphics_mode": "manual",
  "ai_graphics_directives": "0:15 subscribe\n0:33-0:45 list What to take | Passport | Money | Tickets\nend like Hit like"
}

🔊 Sound effects

Short sound effects synced to on-screen events. Independent, optional channels — all off by default (null = silent). The value is the filename (without extension) of a sound in the server's SFX library; an unknown value is treated as null.

FieldApplies toValuesFires on
sfx_auto_soundvideo, dubbing, subtitles, smart_clip"click" · "pop" · "whoosh"Each ⚡ auto-text caption appearing (requires auto_text:true)
sfx_transition_soundvideo"whoosh" · "paper_flip"Each scene change in a photo slideshow (2+ photos)
ai_graphics_sfxvideo, dubbing, subtitles"whoosh" · "paper_flip"Each 🎬 AI-table card appearing (requires ai_graphics:true)

Sounds are mixed below the voice-over / music, so they accent without masking speech. In the Telegram bot these live inside the ⚡ Auto-text screen, the 🎬 AI-tables screen and the 🎵 Background screen.

Type: transcription — transcription

Audio or video → text + .SRT with timecodes.

{
  "type": "transcription", "file": "upload_id_or_https_url",
  "language": "uk", "ai_prompt": "Medical podcast. Correct terminology.",
  "output_format": "both"
}
FieldDescription
filerequiredupload_id or https:// URL
languageoptional"uk" · "en" · "de" · "fr" etc. null = auto
ai_promptoptionalContext for Whisper (improves accuracy)
output_formatoptional"txt" · "srt" · "both" (default)
// result_urls for transcription
{ "txt_url": "https://cdn.reelora.cc/.../transcript.txt",
  "srt_url": "https://cdn.reelora.cc/.../transcript.srt" }

Type: audio — voiceover (TTS)

Text → MP3 file without video.

{
  "type": "audio", "text": "Hello! This is a Reelora API test.",
  "tts_provider": "gemini", "tts_voice": "Puck",
  "tts_speed": 1.0, "tts_scene": "Warm and friendly tone",
  "tts_style": "expressive"
}
FieldDefaultDescription
textrequiredText to synthesize
tts_provideroptional"google"TTS provider (see Providers section)
tts_voiceoptionalfrom profileVoice name (see Voices section)
tts_speedoptional1.00.8 · 1.0 · 1.2 · 1.5
tts_sceneoptionalnullSpeech style instruction (for Gemini TTS)
tts_styleoptionalnullVoice style preset: standard · calm · expressive · energetic · dramatic · cheerful (Gemini and ElevenLabs)

Type: subtitles — subtitles on video

Burns karaoke subtitles into a finished video via Whisper + FFmpeg. Standard+ plan.

{
  "type": "subtitles", "file": "upload_id_or_https_url",
  "language": "uk", "sub_style": "yellow_basic",
  "sub_position": "bottom", "sub_size": "medium"
}

Result: MP4 with subtitles in result_url. Seconds are deducted from the monthly limit.

Field
translate_tooptionalISO code of a target language. Set → subtitles are translated into it (timing preserved, audio stays original). Omit → subtitles in the original language
auto_textoptional⚡ Auto-text — keyword captions from the (translated) subtitle timings. Counts against your monthly auto-text quota
reels_modeoptionaltrue → convert the video to vertical 9:16 (for Reels/TikTok). false = original format
reels_reframeoptional9:16 reframe strategy (only when reels_mode:true): blur (default) · fill · fill_blur · zoom · crop · auto (auto face-crop)

Type: pause_removal — pause removal

Automatically cuts out silence and pauses from video. Standard+ plan. Silence threshold is detected automatically.

{ "type": "pause_removal", "file": "upload_id_or_https_url" }

Result: processed MP4 in result_url.

Type: dubbing — AI dubbing

Transcribes video, translates, synthesizes a new voice, mixes audio. Three modes. Available on all plans within the monthly limit.

Mode A: auto — fully automatic

{
  "type": "dubbing", "mode": "auto",
  "file": "upload_id_or_https_url",
  "source_lang": "auto", "target_lang": "en",
  "tts_provider": "edge", "tts_voice": "en-US-GuyNeural",
  "mix_mode": "overlay", "overlay_volume": 20,
  "sub_lang": "dubbed", "sub_style": "yellow_basic"
}
FieldDefaultDescription
filerequiredupload_id or URL of the video to dub
source_langoptional"auto""auto" or any code from the Languages section ("ar", "fa", "tr"…). Whisper auto-detects when "auto"
target_langrequiredDubbing language — any code from the Languages section. If the provider can't speak it, the bot auto-falls back to Edge
tts_provideroptional"edge"TTS provider for the new voice
tts_voiceoptionalautoVoice for the target language
mix_modeoptional"overlay""overlay" (original is ducked) · "replace" (full replacement)
overlay_volumeoptional20Original volume % when mix_mode=overlay (0–100)
sub_langoptional"dubbed""dubbed" · "original" · "none" · or ISO language code
sub_styleoptional"yellow_basic"Subtitle style
sub_positionoptional"bottom""top" · "center" · "bottom"
auto_textoptionalfalse⚡ Auto-text over the dubbed speech — LLM-picked animated keyword captions. Counts against your monthly auto-text quota
reels_modeoptionalfalsetrue → convert the video to vertical 9:16 (for Reels/TikTok). false = original format
reels_reframeoptional"blur"9:16 reframe strategy (only when reels_mode): blur · fill · fill_blur · zoom · crop · auto (auto face-crop)

Mode B: semi_auto — with verification (2 steps)

// Step 1: transcription
{ "type": "dubbing", "mode": "semi_auto", "step": "transcribe",
  "file": "upload_id_video", "source_lang": "uk", "target_lang": "en" }
// → srt_url: download, edit, upload via /v1/uploads

// Step 2: render with edited SRT
{ "type": "dubbing", "mode": "semi_auto", "step": "render",
  "file": "upload_id_video", "srt_file": "upload_id_of_your_srt",
  "target_lang": "en", "tts_provider": "edge",
  "tts_voice": "en-US-GuyNeural", "mix_mode": "overlay", "overlay_volume": 20 }

Mode C: manual — own SRT file

Skips transcription. You provide your own SRT file containing text in the target language.

{ "type": "dubbing", "mode": "manual",
  "file": "upload_id_video", "srt_file": "upload_id_of_srt_file",
  "target_lang": "uk", "tts_voice": "uk-UA-Wavenet-A", "mix_mode": "replace" }
📋SRT format: 1\n00:00:00,000 --> 00:00:03,500\nLine text\n\n2\n... Timecodes must not exceed video duration.
On the Free plan, dubbing is available only with "edge" and "google" providers. Monthly limit: Free 120s · Standard 1800s · Pro 3600s · Pro+ 18000s.

Type: smart_clip — smart clips

One long video (podcast, interview, vlog) → Whisper detects the language → an LLM picks the strongest self-contained moments → ffmpeg cuts several ready short clips. One call = several clips. Requires a plan with smart clips.

{
  "type": "smart_clip", "file": "upload_id_or_https_url",
  "max_clips": 6, "target_sec": 60, "language": "uk",
  "context": "business podcast, look for tips and strong quotes",
  "reels_mode": true, "subtitles": true, "sub_style": "yellow_basic",
  "auto_text": true, "filters": ["cinema"],
  "webhook_url": "https://mysite.com/hooks/reelora"
}
FieldDefaultDescription
filerequiredupload_id or a direct https:// link to the long video
max_clipsoptional0Max number of clips. 0 = auto (~1 per 5 min, within 4..10). A ceiling, not a plan
target_secoptional60Target clip length (sec). Actual bounds snap to speech pauses
languageoptionalnullVideo language for Whisper (uk, en, ru…). null/"auto" = autodetect
contextoptionalnullContext for the LLM: what the video is and what to look for. Noticeably boosts accuracy
reels_modeoptionalfalsetrue → each clip vertical 9:16 (blurred bg). false = original format
reels_reframeoptional"blur"9:16 reframe strategy (when reels_mode): blur · fill · fill_blur · zoom · crop · auto (auto face-crop)
subtitlesoptionaltrueBurn karaoke subtitles onto each clip
sub_styleoptional"yellow_basic"Subtitle style (same presets)
sub_positionoptional"bottom"top · center · bottom
sub_sizeoptional"medium"small · medium · large
sub_coloroptionalnullHEX subtitle color, e.g. "#00FF00"
auto_textoptionalfalse⚡ Auto-text — dynamic captions on key words
filtersoptional[]Color filters: vivid · contrast · ocean · forest · warm · cinema · bw · vintage

Clips come back in result_urls.clips[]. result_url = the first clip (for compatibility). Clip fields: title, hook, start/end/duration, score (1–100).

{
  "task_id": 12345, "status": "completed",
  "result_urls": { "clips": [
    { "index": 1, "title": "...", "hook": "...",
      "start": 132.4, "end": 197.8, "duration": 65.4,
      "score": 92, "result_url": "https://cdn.../..._01_....mp4" }
  ] }
}
💳Single counter — minutes of the input video are billed (transcription + moment selection + rendering all clips are included). Also available via webhook_url.
📋Monthly limits / max input per file: Free 20 min / 20 min · Standard 3 h / 45 min · Pro 8 h / 60 min · Pro+ 15 h / 120 min. State is in GET /v1/me (smart_clip_seconds, max_smart_clip_duration, used_smart_clip_seconds).

List of Tasks

GET/v1/tasks
curl "https://api.reelora.cc/v1/tasks?limit=10&offset=0" -H "X-API-Key: reel_..."

Parameters: limit (default 20, max 100), offset (default 0).

Task Status

GET/v1/tasks/{task_id}
{
  "task_id": 744, "type": "voiceover", "status": "completed", "progress": 100,
  "result_url": "https://cdn.reelora.cc/audio/.../744_a3f.mp3",
  "result_url_expires_at": "2026-06-21T23:03:19",
  "result_urls": null, "error_message": null,
  "created_at": "2026-06-19T23:03:17", "updated_at": "2026-06-19T23:03:19"
}
⏱️result_url is valid for 48 hours. For transcription, result_urls contains both: txt_url and srt_url.

Async Flow

POST /v1/tasks │ ▼ {"task_id": 744, "status": "pending"} │ ▼ Poll every 3–5 sec: GET /v1/tasks/744 │ ├── status: "pending" → wait ├── status: "processing" → wait (progress 0–100) ├── status: "completed" → download result_url ✅ └── status: "failed" → read error_message ❌

Task Statuses

pending
In queue, not started yet
processing
In progress. progress: 0–99
completed
Ready. File is in result_url
failed
Error in error_message

Webhooks

Specify a webhook_url — you will receive a POST request when the task is finished.

// POST to your webhook_url
{ "task_id": 744, "status": "completed", "result_url": "https://cdn.reelora.cc/..." }
Webhook retries up to 3 times (15s, 30s) if the server returns 5xx. We recommend backing it up with polling.

Languages

The language (voiceover/TTS), source_lang and target_lang (dubbing) fields accept a short ISO code ("fa") or a full locale ("fa-IR"). For transcription/translation Whisper + the LLM understand ~99 languages — below are the ones with a curated TTS voice.

31 languages with TTS voices:

codelanguagelocaleTTS engines
ukUkrainianuk-UAEdge, Gemini, 11Labs, Google
enEnglishen-USEdge, Gemini, 11Labs, Google
ruRussianru-RUEdge, Gemini, 11Labs, Google
esSpanishes-ESEdge, Gemini, 11Labs, Google
deGermande-DEEdge, Gemini, 11Labs, Google
frFrenchfr-FREdge, Gemini, 11Labs, Google
plPolishpl-PLEdge, Gemini, 11Labs, Google
itItalianit-ITEdge, Gemini, 11Labs, Google
ptPortuguesept-BREdge, Gemini, 11Labs, Google
zhChinesezh-CNEdge, Gemini, 11Labs, Google
jaJapaneseja-JPEdge, Gemini, 11Labs, Google
arArabic (RTL)ar-SAEdge, Gemini, 11Labs, Google
faPersian (RTL)fa-IREdge only
heHebrew (RTL)he-ILEdge, Google
urUrdu (RTL)ur-PKEdge only
trTurkishtr-TREdge, Gemini, 11Labs, Google
hiHindihi-INEdge, Gemini, 11Labs, Google
bnBengalibn-BDEdge, Gemini, Google
idIndonesianid-IDEdge, Gemini, 11Labs, Google
msMalayms-MYEdge, 11Labs, Google
viVietnamesevi-VNEdge, Gemini, Google
thThaith-THEdge, Gemini, Google
koKoreanko-KREdge, Gemini, 11Labs, Google
nlDutchnl-NLEdge, Gemini, 11Labs, Google
roRomanianro-ROEdge, Gemini, 11Labs, Google
elGreekel-GREdge, 11Labs, Google
csCzechcs-CZEdge, 11Labs, Google
svSwedishsv-SEEdge, 11Labs, Google
azAzerbaijaniaz-AZEdge only
kkKazakhkk-KZEdge only
uzUzbekuz-UZEdge only
💡tts_provider: "edge" works for all 31 languages and is free — the safe default for the long tail (fa, ur, az, kk, uz are Edge only). If you omit tts_voice the right voice is auto-picked from language.
🌐RTL languages (ar, fa, he, ur): subtitles render right-to-left with an Arabic-capable font. Animated karaoke styles fall back to plain text for RTL.

TTS Providers

ProviderValueQualityLimit
Google (Standard/Wavenet/Chirp)"google"Good1M chars/month
Gemini 2.5 TTS"gemini"Excellentplan limit
Azure Neural"azure"Goodplan limit
Edge TTS (Microsoft)"edge"Goodunlimited
HuggingFace"hf"Basicplan limit
ElevenLabs"elevenlabs"PremiumPro and Pro+

Voices

Google ("google")

Locale is taken from account settings. Change it via: Settings → 🌍 Language.

tts_voiceDescription
Standard-AFemale, basic quality
Standard-BMale, basic quality
Wavenet-AFemale, neural quality
Wavenet-BMale, neural quality
Journey-FFemale, EN only, natural
Chirp3-HD-AoedeFemale, multilingual, HD
Chirp3-HD-PuckMale, multilingual, HD

Gemini 2.5 / 3.1 TTS ("gemini")

Supports tts_scene (free-text directive) and tts_style preset (standard/calm/expressive/energetic/dramatic/cheerful). Add -3.1 for the 3.1 variant (e.g. Puck-3.1).

tts_voiceCharacter
ZephyrBright 🌤
PuckUpbeat 🎉
CharonInformative 📚
KoreFirm 💪
FenrirExcitable ⚡
LedaYouthful 🌱
AoedeBreezy 🍃
CallirrhoeEasy-going 😌
SadachbiaLively 🎵
SulafatWarm 🔥
AchirdFriendly 🤝
EnceladusBreathy 🌬
GacruxMature 🍷

Edge TTS ("edge") — unlimited

tts_voiceLanguage
uk-UA-OstapNeuralUkrainian, male
uk-UA-PolinaNeuralUkrainian, female
en-US-GuyNeuralEnglish US, male
en-US-JennyNeuralEnglish US, female
en-GB-RyanNeuralEnglish GB, male
de-DE-ConradNeuralGerman, male
fr-FR-HenriNeuralFrench, male
es-ES-AlvaroNeuralSpanish, male
pl-PL-MarekNeuralPolish, male
it-IT-DiegoNeuralItalian, male
tr-TR-AhmetNeuralTurkish, male
cs-CZ-AntoninNeuralCzech, male
ru-RU-DmitryNeuralRussian, male

ElevenLabs ("elevenlabs") — Pro and Pro+

tts_voice (ID)Name
pNInz6obpgDQGcFmaJgBAdam (male, multilingual)
21m00Tcm4TlvDq8ikWAMRachel (female, multilingual)
AZnzlk1XvdvUeBnXmlldDomi (female, multilingual)

Background Music

Pass the exact file name in the bgm_track field. Alternatively, upload your own MP3 via /v1/uploads and pass bgm_file.

bgm_track (file name)Style
chill_background-lofi-vibes-113884.mp3Lofi chill
chill_background-the-weekend-117427.mp3Lofi weekend
bodleasons-lofi-chill-smooth-chill-lofi-for-vlogs-and-background-music-159456.mp3Lofi smooth
alexgrohl-phonk-505963.mp3Phonk
white_records-neon-drift-phonk-house-background-music-for-video-27-second-496492.mp3Phonk house (27s)
white_records-toxic-drift-slap-house-background-music-for-video-stories-37-second-503887.mp3Slap house (37s)
diamond_tunes-majestic-tides-60sec-286729.mp3Cinematic majestic (60s)
raspberrymusic-relic-of-honor-cinematic-video-game-epic-478243.mp3Cinematic epic
raspberrymusic-be-the-change-epic-documentary-corporate-394293.mp3Documentary
raspberrymusic-triumph-adventure-news-cinematic-394333.mp3News cinematic
Under_the_Clock.mp3Dramatic
The_Approaching_Hour.mp3Dramatic ambient

bgm_volume: 0.1 (quiet background) → 1.0 (full volume). Default is 0.2.

Subtitle Styles

sub_styleDescription
yellow_basicMrBeast style — bold yellow words
purple_greenNeon — purple + green highlight
minimal_whiteMinimal white, clean
news_styleNews rolling ticker line style
fire_orangeFiery orange highlight
pop_greenBright green
word_box_orangeOrange bounding box around word
karaoke_cyanKaraoke cyan
cursor_selectionCursor selection style
single_green_boxGreen bounding box on a single word
business_newsBusiness news style
dynamic_colorsDynamic colors
elegant_whiteElegant white — bold karaoke, ultra-transparent bar
dual_duetDuet — two paired lines (yellow + white)

Video Formats

formatAspect RatioPlatforms
9_169:16 (vertical)TikTok, Instagram Reels, YouTube Shorts
16_916:9 (horizontal)YouTube, Desktop
1_11:1 (square)Instagram Feed, Facebook
originaloriginalKeep source dimensions

Plan Requirements

FeatureFreeStandardProPro+
video
transcription
audio (TTS)
subtitles
pause_removal
dubbing
ElevenLabs voices
API access

Error Codes

HTTPReason
401Missing or invalid X-API-Key
403Feature unavailable on the current plan
404Task or file not found (upload_id might have expired after 2h)
413File exceeds 500 MB
415Unsupported file type
429Monthly limit exceeded or 30 requests/min rate limit hit
500Internal server error
{ "detail": "Monthly video render limit exceeded" }

Example: Python — full video pipeline

import httpx, time

API_KEY = "reel_xxxxxxxx_..."
BASE    = "https://api.reelora.cc"
H       = {"X-API-Key": API_KEY}

def poll(task_id):
    while True:
        r = httpx.get(f"{BASE}/v1/tasks/{task_id}", headers=H).json()
        print(f"  status={r['status']} progress={r.get('progress')}%")
        if r["status"] == "completed": return r["result_url"]
        if r["status"] == "failed":    raise RuntimeError(r["error_message"])
        time.sleep(3)

with open("photo.jpg", "rb") as f:
    upload = httpx.post(f"{BASE}/v1/uploads", headers=H, files={"file": f}).json()

task = httpx.post(f"{BASE}/v1/tasks", headers=H, json={
    "type":         "video",
    "photos":       [upload["upload_id"]],
    "text":         "Breaking news from our channel.",
    "format":       "9_16",
    "tts_provider": "google",
    "tts_voice":    "Chirp3-HD-Aoede",
    "subtitles":    True,
    "sub_style":    "news_style",
    "bgm_track":    "raspberrymusic-triumph-adventure-news-cinematic-394333.mp3",
    "bgm_volume":   0.15,
}).json()

print("Download:", poll(task["task_id"]))

Example: Python — semi_auto dubbing

# Step 1: transcription
with open("video.mp4", "rb") as f:
    vid = httpx.post(f"{BASE}/v1/uploads", headers=H, files={"file": f}).json()

t1 = httpx.post(f"{BASE}/v1/tasks", headers=H, json={
    "type": "dubbing", "mode": "semi_auto", "step": "transcribe",
    "file": vid["upload_id"], "source_lang": "uk", "target_lang": "en",
}).json()
srt_url = poll(t1["task_id"])
# download srt_url → edit → save as edited.srt

# Step 2: render with edited SRT
with open("edited.srt", "rb") as f:
    srt = httpx.post(f"{BASE}/v1/uploads", headers=H, files={"file": f}).json()

t2 = httpx.post(f"{BASE}/v1/tasks", headers=H, json={
    "type": "dubbing", "mode": "semi_auto", "step": "render",
    "file": vid["upload_id"], "srt_file": srt["upload_id"],
    "target_lang": "en", "tts_provider": "edge",
    "tts_voice": "en-US-GuyNeural",
    "mix_mode": "overlay", "overlay_volume": 20,
}).json()
print("Dubbed video:", poll(t2["task_id"]))

Example: JavaScript / Node.js

const API_KEY = "reel_xxxxxxxx_...";
const BASE    = "https://api.reelora.cc";
const H       = { "X-API-Key": API_KEY, "Content-Type": "application/json" };

async function poll(taskId) {
  while (true) {
    await new Promise(r => setTimeout(r, 3000));
    const d = await fetch(`${BASE}/v1/tasks/${taskId}`, { headers: H }).then(r => r.json());
    if (d.status === "completed") return d.result_url;
    if (d.status === "failed")    throw new Error(d.error_message);
  }
}

const { task_id } = await fetch(`${BASE}/v1/tasks`, {
  method: "POST", headers: H,
  body:   JSON.stringify({ type: "audio", text: "Hello from JS!", tts_provider: "google", tts_voice: "Chirp3-HD-Puck" }),
}).then(r => r.json());

console.log("MP3:", await poll(task_id));

Example: curl — transcription

# Upload file
UPLOAD=$(curl -s -X POST https://api.reelora.cc/v1/uploads \
  -H "X-API-Key: reel_..." -F "file=@podcast.mp3" \
  | python3 -c "import sys,json; print(json.load(sys.stdin)['upload_id'])")

# Transcription task
TASK=$(curl -s -X POST https://api.reelora.cc/v1/tasks \
  -H "X-API-Key: reel_..." -H "Content-Type: application/json" \
  -d "{\"type\":\"transcription\",\"file\":\"$UPLOAD\",\"language\":\"uk\",\"output_format\":\"both\"}" \
  | python3 -c "import sys,json; print(json.load(sys.stdin)['task_id'])")

# Polling
while true; do
  RESP=$(curl -s https://api.reelora.cc/v1/tasks/$TASK -H "X-API-Key: reel_...")
  STATUS=$(echo $RESP | python3 -c "import sys,json; print(json.load(sys.stdin)['status'])")
  echo "Status: $STATUS"
  [ "$STATUS" = "completed" ] && echo $RESP | python3 -m json.tool && break
  [ "$STATUS" = "failed"    ] && echo "Failed!" && break
  sleep 3
done