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
| Method | Path | Description |
|---|---|---|
| GET | /health | Service health check |
| GET | /v1/me | Profile, limits, usage |
| POST | /v1/uploads | Upload file |
| POST | /v1/tasks | Create task |
| GET | /v1/tasks | List of tasks |
| GET | /v1/tasks/{id} | Task status + result |
Quick Start
Get API Key
In the bot @reelora_ai_bot send /apikey or open Settings → 🔑 API Access. The key is shown only once.
Upload File
POST /v1/uploads → get upload_id
Create Task
POST /v1/tasks with type and parameters
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
Base URL
https://api.reelora.cc
Interactive Swagger documentation: https://api.reelora.cc/docs
Status Check
curl https://api.reelora.cc/health
# {"status":"ok","service":"reelora-api"}
Profile and Limits
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
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 "[email protected]"
# {"upload_id":"550e8400-...","filename":"photo.jpg","size_bytes":245891}
| Type | Formats | Max. |
|---|---|---|
| Images | JPEG, PNG, WebP | 500 MB |
| Video | MP4, MOV, AVI | 500 MB |
| Audio | MP3, WAV, M4A | 500 MB |
| Subtitles | SRT | — |
Use upload_id in the photos, file, srt_file, bgm_file fields. Alternatively, you can pass direct https:// URLs instead of uploading.
Task Creation
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
}
| Field | Default | Description | |
|---|---|---|---|
| photos | required | — | upload_id or https:// (images or video clips, max. 30) |
| text | optional | null | Voiceover 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_durations | optional | null | Explicit per-media durations in seconds, e.g. [10, 15, 5]. Length must equal media count. Overrides {scene} auto-timing, works even without voice |
| scene_transitions | optional | null | Explicit transition per cut (length = photos − 1), e.g. ["slide","zoom"]. Alternative to {scene:type} markers |
| target_duration | optional | null | Target 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 |
| format | optional | "9_16" | "9_16" · "16_9" · "1_1" · "original" |
| animation | optional | "none" | "none" · "ken_burns" |
| transition | optional | "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_duration | optional | 0.5 | Transition length in seconds. Per-cut length can be set via {scene:type:sec} |
| tts_provider | optional | "google" | TTS provider (see Providers section) |
| tts_voice | optional | from profile | Voice name (see Voices section) |
| tts_speed | optional | 1.0 | 0.8 · 1.0 · 1.2 · 1.5 |
| language | optional | — | Voiceover language — any code from the Languages section ("ar", "fa"…) or a full locale "ru-RU". For the long tail use tts_provider:"edge" |
| tts_scene | optional | null | Speech style instruction (for Gemini TTS) |
| tts_style | optional | null | Voice style preset: standard · calm · expressive · energetic · dramatic · cheerful (Gemini and ElevenLabs) |
| subtitles | optional | true | Karaoke subtitles |
| sub_style | optional | "yellow_basic" | Subtitle style |
| sub_position | optional | "bottom" | "top" · "center" · "bottom" |
| sub_size | optional | "medium" | "small" · "medium" · "large" |
| sub_color | optional | null | ASS color: "&H0000E6FF" (yellow) |
| text_overlays | optional | null | Static captions burned over the frame, independent of the voice-over (see Text on video below) |
| text_overlays_raw | optional | null | The same feature as a compact “codes” string, parsed on the server (see below). Used only if text_overlays is absent |
| auto_text | optional | false | ⚡ 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_track | optional | null | File name from the music library (see BGM section) |
| bgm_file | optional | null | upload_id of your own MP3 file (mutually exclusive with bgm_track) |
| bgm_volume | optional | 0.2 | Music volume 0.0–1.0 |
| audio_mix_type | optional | "replace" | Mixing for video clips: "replace" · "add" · "overlay" |
audio_mix_type — for clips with original sound
| Value | Behavior |
|---|---|
| replace | TTS/BGM only — clip sound is removed |
| add | TTS on top of original (both at full volume) |
| overlay | TTS 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.
🅰️ 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 | ||
|---|---|---|
| text | required | required, max 200 chars |
| start | optional | sec. null → from 0 |
| end | optional | sec. null → until end of video |
| style | optional | title style (below) |
| size | optional | small · medium · large |
| position | optional | 9-grid: top_left · top_center · top_right · middle_left · center · middle_right · bottom_left · bottom_center · bottom_right |
| bold / italic | optional | overrides the style weight/slant |
| color | optional | ASS hex &HAABBGGRR, e.g. "&H00B469FF" (pink) |
| fade | optional | smooth fade in/out (300 ms) |
| anim | optional | Appearance 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
⚡ 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.
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"
}
| Field | Description | |
|---|---|---|
| file | required | upload_id or https:// URL |
| language | optional | "uk" · "en" · "de" · "fr" etc. null = auto |
| ai_prompt | optional | Context for Whisper (improves accuracy) |
| output_format | optional | "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"
}
| Field | Default | Description | |
|---|---|---|---|
| text | required | — | Text to synthesize |
| tts_provider | optional | "google" | TTS provider (see Providers section) |
| tts_voice | optional | from profile | Voice name (see Voices section) |
| tts_speed | optional | 1.0 | 0.8 · 1.0 · 1.2 · 1.5 |
| tts_scene | optional | null | Speech style instruction (for Gemini TTS) |
| tts_style | optional | null | Voice 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_to | optional | ISO code of a target language. Set → subtitles are translated into it (timing preserved, audio stays original). Omit → subtitles in the original language |
| auto_text | optional | ⚡ Auto-text — keyword captions from the (translated) subtitle timings. Counts against your monthly auto-text quota |
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"
}
| Field | Default | Description | |
|---|---|---|---|
| file | required | — | upload_id or URL of the video to dub |
| source_lang | optional | "auto" | "auto" or any code from the Languages section ("ar", "fa", "tr"…). Whisper auto-detects when "auto" |
| target_lang | required | — | Dubbing language — any code from the Languages section. If the provider can't speak it, the bot auto-falls back to Edge |
| tts_provider | optional | "edge" | TTS provider for the new voice |
| tts_voice | optional | auto | Voice for the target language |
| mix_mode | optional | "overlay" | "overlay" (original is ducked) · "replace" (full replacement) |
| overlay_volume | optional | 20 | Original volume % when mix_mode=overlay (0–100) |
| sub_lang | optional | "dubbed" | "dubbed" · "original" · "none" · or ISO language code |
| sub_style | optional | "yellow_basic" | Subtitle style |
| sub_position | optional | "bottom" | "top" · "center" · "bottom" |
| auto_text | optional | false | ⚡ Auto-text over the dubbed speech — LLM-picked animated keyword captions. Counts against your monthly auto-text quota |
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" }
1\n00:00:00,000 --> 00:00:03,500\nLine text\n\n2\n... Timecodes must not exceed video duration.List of 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
{
"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"
}
Async Flow
Task Statuses
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/..." }
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:
| code | language | locale | TTS engines |
|---|---|---|---|
| uk | Ukrainian | uk-UA | Edge, Gemini, 11Labs, Google |
| en | English | en-US | Edge, Gemini, 11Labs, Google |
| ru | Russian | ru-RU | Edge, Gemini, 11Labs, Google |
| es | Spanish | es-ES | Edge, Gemini, 11Labs, Google |
| de | German | de-DE | Edge, Gemini, 11Labs, Google |
| fr | French | fr-FR | Edge, Gemini, 11Labs, Google |
| pl | Polish | pl-PL | Edge, Gemini, 11Labs, Google |
| it | Italian | it-IT | Edge, Gemini, 11Labs, Google |
| pt | Portuguese | pt-BR | Edge, Gemini, 11Labs, Google |
| zh | Chinese | zh-CN | Edge, Gemini, 11Labs, Google |
| ja | Japanese | ja-JP | Edge, Gemini, 11Labs, Google |
| ar | Arabic (RTL) | ar-SA | Edge, Gemini, 11Labs, Google |
| fa | Persian (RTL) | fa-IR | Edge only |
| he | Hebrew (RTL) | he-IL | Edge, Google |
| ur | Urdu (RTL) | ur-PK | Edge only |
| tr | Turkish | tr-TR | Edge, Gemini, 11Labs, Google |
| hi | Hindi | hi-IN | Edge, Gemini, 11Labs, Google |
| bn | Bengali | bn-BD | Edge, Gemini, Google |
| id | Indonesian | id-ID | Edge, Gemini, 11Labs, Google |
| ms | Malay | ms-MY | Edge, 11Labs, Google |
| vi | Vietnamese | vi-VN | Edge, Gemini, Google |
| th | Thai | th-TH | Edge, Gemini, Google |
| ko | Korean | ko-KR | Edge, Gemini, 11Labs, Google |
| nl | Dutch | nl-NL | Edge, Gemini, 11Labs, Google |
| ro | Romanian | ro-RO | Edge, Gemini, 11Labs, Google |
| el | Greek | el-GR | Edge, 11Labs, Google |
| cs | Czech | cs-CZ | Edge, 11Labs, Google |
| sv | Swedish | sv-SE | Edge, 11Labs, Google |
| az | Azerbaijani | az-AZ | Edge only |
| kk | Kazakh | kk-KZ | Edge only |
| uz | Uzbek | uz-UZ | Edge only |
TTS Providers
| Provider | Value | Quality | Limit |
|---|---|---|---|
| Google (Standard/Wavenet/Chirp) | "google" | Good | 1M chars/month |
| Gemini 2.5 TTS | "gemini" | Excellent | plan limit |
| Azure Neural | "azure" | Good | plan limit |
| Edge TTS (Microsoft) | "edge" | Good | unlimited |
| HuggingFace | "hf" | Basic | plan limit |
| ElevenLabs | "elevenlabs" | Premium | Pro and Pro+ |
Voices
Google ("google")
Locale is taken from account settings. Change it via: Settings → 🌍 Language.
| tts_voice | Description |
|---|---|
| Standard-A | Female, basic quality |
| Standard-B | Male, basic quality |
| Wavenet-A | Female, neural quality |
| Wavenet-B | Male, neural quality |
| Journey-F | Female, EN only, natural |
| Chirp3-HD-Aoede | Female, multilingual, HD |
| Chirp3-HD-Puck | Male, 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_voice | Character |
|---|---|
| Zephyr | Bright 🌤 |
| Puck | Upbeat 🎉 |
| Charon | Informative 📚 |
| Kore | Firm 💪 |
| Fenrir | Excitable ⚡ |
| Leda | Youthful 🌱 |
| Aoede | Breezy 🍃 |
| Callirrhoe | Easy-going 😌 |
| Sadachbia | Lively 🎵 |
| Sulafat | Warm 🔥 |
| Achird | Friendly 🤝 |
| Enceladus | Breathy 🌬 |
| Gacrux | Mature 🍷 |
Edge TTS ("edge") — unlimited
| tts_voice | Language |
|---|---|
| uk-UA-OstapNeural | Ukrainian, male |
| uk-UA-PolinaNeural | Ukrainian, female |
| en-US-GuyNeural | English US, male |
| en-US-JennyNeural | English US, female |
| en-GB-RyanNeural | English GB, male |
| de-DE-ConradNeural | German, male |
| fr-FR-HenriNeural | French, male |
| es-ES-AlvaroNeural | Spanish, male |
| pl-PL-MarekNeural | Polish, male |
| it-IT-DiegoNeural | Italian, male |
| tr-TR-AhmetNeural | Turkish, male |
| cs-CZ-AntoninNeural | Czech, male |
| ru-RU-DmitryNeural | Russian, male |
ElevenLabs ("elevenlabs") — Pro and Pro+
| tts_voice (ID) | Name |
|---|---|
| pNInz6obpgDQGcFmaJgB | Adam (male, multilingual) |
| 21m00Tcm4TlvDq8ikWAM | Rachel (female, multilingual) |
| AZnzlk1XvdvUeBnXmlld | Domi (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.mp3 | Lofi chill |
| chill_background-the-weekend-117427.mp3 | Lofi weekend |
| bodleasons-lofi-chill-smooth-chill-lofi-for-vlogs-and-background-music-159456.mp3 | Lofi smooth |
| alexgrohl-phonk-505963.mp3 | Phonk |
| white_records-neon-drift-phonk-house-background-music-for-video-27-second-496492.mp3 | Phonk house (27s) |
| white_records-toxic-drift-slap-house-background-music-for-video-stories-37-second-503887.mp3 | Slap house (37s) |
| diamond_tunes-majestic-tides-60sec-286729.mp3 | Cinematic majestic (60s) |
| raspberrymusic-relic-of-honor-cinematic-video-game-epic-478243.mp3 | Cinematic epic |
| raspberrymusic-be-the-change-epic-documentary-corporate-394293.mp3 | Documentary |
| raspberrymusic-triumph-adventure-news-cinematic-394333.mp3 | News cinematic |
| Under_the_Clock.mp3 | Dramatic |
| The_Approaching_Hour.mp3 | Dramatic ambient |
bgm_volume: 0.1 (quiet background) → 1.0 (full volume). Default is 0.2.
Subtitle Styles
| sub_style | Description |
|---|---|
| yellow_basic | MrBeast style — bold yellow words |
| purple_green | Neon — purple + green highlight |
| minimal_white | Minimal white, clean |
| news_style | News rolling ticker line style |
| fire_orange | Fiery orange highlight |
| pop_green | Bright green |
| word_box_orange | Orange bounding box around word |
| karaoke_cyan | Karaoke cyan |
| cursor_selection | Cursor selection style |
| single_green_box | Green bounding box on a single word |
| business_news | Business news style |
| dynamic_colors | Dynamic colors |
Video Formats
| format | Aspect Ratio | Platforms |
|---|---|---|
| 9_16 | 9:16 (vertical) | TikTok, Instagram Reels, YouTube Shorts |
| 16_9 | 16:9 (horizontal) | YouTube, Desktop |
| 1_1 | 1:1 (square) | Instagram Feed, Facebook |
| original | original | Keep source dimensions |
Plan Requirements
| Feature | Free | Standard | Pro | Pro+ |
|---|---|---|---|---|
| video | ✅ | ✅ | ✅ | ✅ |
| transcription | ✅ | ✅ | ✅ | ✅ |
| audio (TTS) | ✅ | ✅ | ✅ | ✅ |
| subtitles | ✅ | ✅ | ✅ | ✅ |
| pause_removal | ✅ | ✅ | ✅ | ✅ |
| dubbing | ✅ | ✅ | ✅ | ✅ |
| ElevenLabs voices | ❌ | ❌ | ✅ | ✅ |
| API access | ✅ | ✅ | ✅ | ✅ |
Error Codes
| HTTP | Reason |
|---|---|
| 401 | Missing or invalid X-API-Key |
| 403 | Feature unavailable on the current plan |
| 404 | Task or file not found (upload_id might have expired after 2h) |
| 413 | File exceeds 500 MB |
| 415 | Unsupported file type |
| 429 | Monthly limit exceeded or 30 requests/min rate limit hit |
| 500 | Internal 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 "[email protected]" \
| 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