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. 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.

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

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

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.

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

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