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 "[email protected]"
# {"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. Ohne Text — Video ohne Ton. Füge Szenenmarker zwischen Absätzen ein, um den Schnitt zu wechseln (und optional den Übergang zu wählen): {scene} · {scene:slide} · {scene:zoom:0.8} · {scene:0.8}
scene_durationsoptionalnullExplizite Dauer pro Medium in Sekunden, z. B. [10, 15, 5]. Länge = Medienanzahl. Überschreibt das {scene}-Auto-Timing, funktioniert auch ohne Stimme
scene_transitionsoptionalnullExpliziter Übergang pro Schnitt (Länge = photos − 1), z. B. ["slide","zoom"]. Alternative zu {scene:type}-Markern
target_durationoptionalnullZiel-Cliplänge in Sekunden. Ist das Ergebnis länger, wird es beschleunigt (Video+Audio+Untertitel synchron), um zu passen. Kürzeres bleibt unangetastet. Beschleunigung max. ×2. Muss ≤ Tariflimit sein
formatoptional"9_16""9_16" · "16_9" · "1_1" · "original"
animationoptional"none""none" · "ken_burns"
transitionoptional"crossfade"Globaler Übergang für unmarkierte Schnitte: none · random · fade · dissolve · slide · cover · reveal · zoom · squeeze · wind · pixel · circle (crossfade = Alias von dissolve). Wirkt nur wenn animation ≠ none
transition_durationoptional0.5Übergangslänge in Sekunden. Pro-Schnitt-Länge via {scene:type:sek}
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-Sprache — beliebiger Code aus dem Abschnitt Sprachen ("ar", "fa"…) oder volles Locale "ru-RU". Für seltene Sprachen — 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_overlaysoptionalnullStatische Beschriftungen über dem Bild, unabhängig vom Voice-over (siehe „Text im Video“ unten)
text_overlays_rawoptionalnullDasselbe als kompakter „Codes“-String, serverseitig geparst (siehe unten). Nur genutzt, wenn text_overlays fehlt
auto_textoptionalfalse⚡ Auto-Text. Ein LLM wählt die stärksten Schlüsselwörter des Voice-overs und fügt sie als animierte Einblendungen hinzu, synchron zu den TTS-Wort-Timestamps (siehe „Auto-Text“ unten). Erfordert text. Zählt gegen dein monatliches Auto-Text-Kontingent
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

🎬 Dynamische Szenen — wann der Schnitt wechselt

  • Mit Stimme: füge {scene}-Marker in text zwischen Absätzen ein. Jedes Segment wird durch die Erzählung getaktet und der Reihe nach den Medien zugeordnet. Marker werden vor TTS entfernt — die Stimme liest sie nie.
  • Ohne Stimme / explizit: übergib scene_durations (z. B. [10, 15, 5]) — ein Wert pro Medium. Clips werden zugeschnitten/geloopt, Fotos für die Szenenlänge gezeigt.
  • Szenen ↔ Medien: gleich → 1:1; weniger Szenen → die letzte teilt sich auf den Rest; mehr → das letzte Medium hält den Rest.

✨ Übergänge pro Schnitt — wie der Schnitt wechselt

  • Im Text: erweitere einen Marker um Typ und/oder Dauer: {scene:slide}, {scene:zoom:0.8}, {scene:0.8} (nur Dauer). Der Übergang gilt für den Schnitt in diese Szene.
  • Explizites Array: scene_transitions (Länge = photos − 1), z. B. ["slide","zoom","fade"] — ein Übergang pro Schnitt. Praktisch für stumme Videos mit expliziten Dauern.
  • Typen: fade · dissolve · slide · cover · reveal · zoom · squeeze · wind · pixel · circle · random. Gerichtete (slide/cover/reveal/wind) wählen pro Schnitt eine zufällige Richtung.
ℹ️Übergänge werden nur gerendert, wenn animationnone. Ein none pro Schnitt wird nicht unterstützt — es fällt auf das globale transition zurück.

🅰️ Text im Video — statische Einblendungen

Füge beliebige Beschriftungen/Titel über dem Video hinzu, unabhängig von den Untertiteln. Mit oder ohne Sprecher. Jede Einblendung hat eigenen Stil, Position, Timing und Ein-/Ausblendung. Zwei Wege:

Strukturiert — 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 }
]
Feld
textrequirederforderlich, max. 200 Zeichen
startoptionalSek. null → ab 0
endoptionalSek. null → bis Videoende
styleoptionalTitelstil (unten)
sizeoptionalsmall · medium · large
positionoptional9er-Raster: top_left · top_center · top_right · middle_left · center · middle_right · bottom_left · bottom_center · bottom_right
bold / italicoptionalüberschreibt Gewicht/Kursiv des Stils
coloroptionalASS hex &HAABBGGRR, z. B. "&H00B469FF" (rosa)
fadeoptionalsanftes Ein-/Ausblenden (300 ms)
animoptionalErscheinungs-Animation: pop · slide_left · slide_right · type (Schreibmaschine). Hat Vorrang vor fade

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

Codes — text_overlays_raw (String). Universelle englische Syntax (in jeder UI-Sprache gleich), serverseitig geparst. Eine Einblendung pro Zeile (oder durch /); Felder innerhalb durch |:

TEXT | attributes | start-end
"text_overlays_raw": "Hello | large italic center | 0-3 / Subscribe | bottom pink pop | 3-end"
  • Größe — small · medium · large
  • Gewicht/Kursiv — bold · thin · italic
  • vertikal — top · middle · bottom; horizontal — left · center · right
  • Farbe — white · yellow · red · green · blue · pink · orange · purple · black · cyan
  • Zeit — 0-3, 2.5-8, 5-end (Sek., Punkt/Komma). Ohne Zeit → Einblendung im ganzen Clip
  • Animation — pop · slide · type (Schreibmaschine) · fade · none
📋Beispiel: erste Einblendung — „Hallo“ groß kursiv zentriert 0–3 s; zweite — „Abonnieren“ unten rosa ab Sekunde 3 bis zum Ende. Attribute und Zeit sind optional und reihenfolgeunabhängig. Unbekannte Tokens werden ignoriert (der Request schlägt nie fehl).

⚡ Auto-Text — automatische Schlüsselwort-Einblendungen

auto_text: true aktiviert ⚡ Auto-Text. Statt Einblendungen selbst zu platzieren, liest ein LLM die erkannten Wörter des Voice-overs, wählt die stärksten (Namen, Zahlen, prägnante Aussagen) und rendert sie als animierte Einblendungen, exakt auf den TTS-Wort-Timestamps. Es wählt pro Wort einen modernen Stil, Farbe und Erscheinungs-Animation (pop / slide / typewriter), stapelt zusammen gesprochene Wörter und hält sich aus der Untertitelzone.

  • Erfordert ein Voice-over (text nicht leer). Ohne Sprecher gibt es keine Wort-Timestamps, daher wird auto_text ignoriert.
  • Additiv: erzeugte Einblendungen werden an deine text_overlays / text_overlays_raw angehängt, sie ersetzen sie nicht.
  • Voll automatisch: keine Parameter pro Wort. Findet das LLM nichts, läuft das Rendering einfach ohne Zusatz-Einblendungen weiter (der Request schlägt nie fehl).
  • Kontingent: jedes erfolgreiche Auto-Text-Rendering verbraucht eine Einheit deines Monatskontingents (auto_text_limit; Free begrenzt, Bezahlpläne unbegrenzt). Über dem Limit → 429.
  • Auto-Text funktioniert in allen API-Task-Typen: video, dubbing und subtitles. Übersetzung per API läuft über subtitles mit translate_to (kein eigener translation-Typ), daher deckt auto_text auch übersetzte Untertitel ab.

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.

Feld
translate_tooptionalISO-Code einer Zielsprache. Gesetzt → Untertitel werden dorthin übersetzt (Timing bleibt, Ton bleibt original). Weggelassen → in der Originalsprache
auto_textoptional⚡ Auto-Text — Schlüsselwort-Einblendungen aus den Zeiten der (übersetzten) Untertitel. Zählt gegen dein Auto-Text-Kontingent

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" oder beliebiger Code aus dem Abschnitt Sprachen ("ar", "fa", "tr"…). Whisper erkennt automatisch bei "auto"
target_langrequiredSynchronisationssprache — beliebiger Code aus dem Abschnitt Sprachen. Kann der Anbieter sie nicht sprechen — Auto-Fallback auf 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"
tts_styleoptionalnullVoice style preset: standard · calm · expressive · energetic · dramatic · cheerful (Gemini and ElevenLabs)
auto_textoptionalfalse⚡ Auto-Text über der synchronisierten Sprache — vom LLM gewählte animierte Schlüsselwort-Einblendungen. Zählt gegen dein Auto-Text-Kontingent

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.

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.

Sprachen

Die Felder language (Voiceover/TTS), source_lang und target_lang (Synchronisation) akzeptieren einen kurzen ISO-Code ("fa") oder ein volles Locale ("fa-IR"). Für Transkription/Übersetzung verstehen Whisper + LLM ~99 Sprachen — unten die mit kuratierter TTS-Stimme.

31 Sprachen mit TTS-Stimmen:

CodeSpracheLocaleTTS-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-IRnur Edge
heHebrew (RTL)he-ILEdge, Google
urUrdu (RTL)ur-PKnur Edge
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-AZnur Edge
kkKazakhkk-KZnur Edge
uzUzbekuz-UZnur Edge
💡tts_provider: "edge" funktioniert für alle 31 Sprachen und ist kostenlos — der sichere Standard für seltene Sprachen (fa, ur, az, kk, uz — nur Edge). Lässt du tts_voice weg, wird die richtige Stimme aus language gewählt.
🌐RTL-Sprachen (ar, fa, he, ur): Untertitel werden von rechts nach links mit arabikfähiger Schrift gerendert. Animierte Karaoke-Stile fallen für RTL auf einfachen Text zurück.

TTS Providers

ProviderValueQualityLimit
Google (Standard/Wavenet/Chirp)"google"Good1M chars/month
Gemini 2.5 / 3.1 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 and tts_style for speech styling. Add -3.1 for the 3.1 version 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

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 "[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