RapidVideoMaker
Create an account

API RapidVideoMaker

The REST API lets you process video files from any HTTP client — Python scripts, n8n workflows, third-party tools. Six modes available: Merge videos (multiple MP4), Edit audio (1 MP4 + 1 MP3), Create a video (1 JPG/PNG + 1 MP3), Overlay video (2 MP4 picture-in-picture), Text to MP3 (no file upload). Processing is asynchronous: submit your request, then poll for the result.

Get your API token for free
Create an account to get your API token — 10 requests included per day, usable from the interface or the API.
Create a free account Already have an account? Sign in

Complete workflow

Every integration follows the same 4-step sequence:

1
Create the job — POST /api/v1/render.php
Send your files as multipart/form-data with your Bearer token. 1 JPG/PNG image + 1 MP3 + mode=image_to_video → video generation. 1 MP4 + 1 MP3 → audio edit (add mode=mix_audio to overlay tracks). Multiple MP4 → merge. The server returns a unique job_id.
2
Wait for the job — GET /api/v1/jobs.php?job_id=…
Poll this endpoint regularly (every 5–10 s). The job progresses through queuedprocessingready (or error). The response includes the queue position and an estimated time remaining.
3
Retrieve the download URL
When the status is ready, the response contains a ready-to-use download_url field.
4
Download the file — GET /api/download.php?job_id=…
Download the final MP4. The file is available for 2 hours after the job completes, then automatically deleted. After expiry, the API responds 410 Gone (instead of 404) to indicate the job existed but is no longer retrievable.

Authentication

Every request must include your API token. Two formats are accepted:

# Authentication — Every request must include your API token. Two formats are accepted: Authorization: Bearer rvm_your_token_here # Alternative X-API-Key: rvm_your_token_here

Your token is available in your member spaceone token per account, created automatically at registration. It is only shown in the clear once — keep it safe. If you lose it, you can regenerate it from your member space.

Quota

Each member account has 10 requests per day, shared between the web interface and the API. Failed jobs do not consume quota. The counter resets at midnight UTC.

POST responses include quota_used and quota_remaining. When the limit is exceeded, the server responds 429.

Interface + API = same counter. A video created from the web interface and an API request each consume 1 credit from the same daily quota.

Queue

One job is processed at a time. If multiple jobs are submitted simultaneously, they are queued and processed in arrival order. While waiting, the status response includes:

Rate limit applied: the /api/v1/jobs.php endpoint accepts at most 12 calls per minute per token (1 every 5 s). Beyond that, the server responds 429 with a Retry-After: 5 header. Recommendation: 10 seconds between each call.

Available transitions

Pass transition_type=none (default) for a direct merge, or one of the types below for a fade between clips:

none fade fadeblack dissolve wipeleft wiperight slideleft slideright zoomin circleclose

Duration (transition_duration) is between 0.2 and 2.0 seconds. If a clip is too short to accommodate the transition, it is ignored and the merge proceeds without transition.

Endpoints

POST /api/v1/render.php Creates a video processing job

Six modes, accessible via the mode field (omit mode with multiple MP4s to auto-detect fusion):

UsageAPI Mode(s)Required filesDescription
Merge videos fusion 2–20 MP4 files Clips are assembled end-to-end in the order provided. Optional transitions between clips.
Edit audio add_audio or mix_audio 1 MP4 + 1 MP3 Replaces or enriches the audio track of a video. The video track is never re-encoded (-c:v copy).

add_audio — the original audio is deleted and fully replaced by the MP3.
mix_audio — the original audio is kept and the MP3 is overlaid (amix). Pass options={"mp3_volume":…} to adjust the added MP3 volume. If the video has no audio, behaves like add_audio.
Create a video image_to_video 1 JPG/PNG + 1 MP3 Generates a video from a still image and an audio file. Text overlay, fades, resolution and fps configurable via the options field.
Overlay video overlay_video 2 MP4 Overlays a second video on top of the main one. Position, size, opacity and audio configurable via the options field.
Text to MP3 text_to_mp3 (text only) Converts text to speech and returns an MP3 file. No file upload required — send mode=text_to_mp3, text and lang as form fields. Supports all 20 site languages.
Strict validation: each mode requires an exact number and types of files. add_audio and mix_audio accept only 1 MP4 + 1 MP3 (exactly). image_to_video accepts only 1 JPG/PNG + 1 MP3 (exactly). Any other combination is rejected with an explicit error code. MIME types are verified by magic bytes, not only by extension. Multiple MP4s without a mode field → auto-detect fusion.
Duration rule: in audio and image_to_video modes, the result duration is locked to the MP3 duration. The video track is never re-encoded in audio modes (-c:v copy).

Common parameters (multipart/form-data)

FieldTypeReq.Description
videos[]File[]YESFiles to process. Depending on mode: 2–20 MP4, or 1 MP4 + 1 MP3, or 1 image (JPG/PNG) + 1 MP3. Max 500 MB per file.
modestring*image_to_video | add_audio | mix_audio | overlay_video | text_to_mp3. Required for these non-fusion modes. Omitting mode with only MP4 files always triggers fusion (stream-copy concat) — never overlay_video, even if exactly 2 MP4s are uploaded. For audio editing: add_audio deletes the original, mix_audio keeps it and overlays. If the mode field is provided, uploaded files must match exactly — otherwise the request is rejected.
orderJSONfusion mode only — JSON array of indices defining the clip order.
transition_typestringfusion mode only. Default: none. See the list of available transitions.
transition_durationfloatfusion mode only. Duration 0.2–2.0 s. Default: 0.5.
optionsJSONimage_to_video mode only — JSON object of render parameters (see below).
callback_urlstringPublic URL (http/https) called at job completion. The response includes a webhook_secret.

options parameters — image_to_video mode

All optional. Values not provided → defaults applied by the worker.

KeyTypeDefaultDescription
textstring""Text shown as overlay. Empty = no text. Max 500 characters.
font_sizeint60Font size in pixels. Range: 10–300. Recommended: 36, 48, 60, 72, 96.
text_colorstring"white"Text color (e.g. white, yellow, #ffffff, 0xffffff).
text_positionstring"center"Text position: center, top, bottom.
boxbooltrueSemi-transparent background behind the text.
box_colorstring"black@0.4"Color + opacity in FFmpeg format: color@opacity. Colors: black, white, yellow, #ff4444, #00ccff, #ff8800, #ff69b4… Opacity: 0.0–1.0 (e.g. black@0.4 = black at 40%, white@0.70 = white at 70%).
fade_durationfloat0.5Fade in/out duration in seconds (0–2). Text is included in the fade. 0 = no fade.
widthint1080Video width in pixels (64–3840, forced even). Standard combos: 1080/1920/720/1280.
heightint1920Video height in pixels (64–3840, forced even).
fpsint24Frame rate (1–60).
image_fitstring"contain"Framing mode: contain = the full image is visible, empty areas filled by bg_color (letterbox/pillarbox); cover = image zoomed to fill the entire frame, centered, excess edges cropped (no black bars). The bg_color parameter is ignored in cover mode.
bg_colorstring"black"Background (padding) color if the image does not fill the target resolution — relevant only with image_fit=contain (e.g. black, white, #1a1a2e).
enable_image_motionboolfalseEnables photo animation. If false, the image stays static. If true, the effect defined by image_motion_effect is applied via the FFmpeg zoompan filter.
image_motion_effectstring"ken_burns"Animation effect (ignored if enable_image_motion is false). Accepted values: zoom_in, zoom_out, pan_left_to_right, pan_right_to_left, pan_top_to_bottom, pan_bottom_to_top, zoom_pan_soft, ken_burns. Default if absent or invalid: ken_burns.
motion_intensityfloat1.0Photo animation intensity (0.25–2.0). Multiplies zoom amplitude and pan speed. 0.25 = very subtle, 1.0 = default, 2.0 = double amplitude. Ignored if enable_image_motion is false.
text_effectstring"none"Text overlay animation (ignored if text is absent or text_mode is word_by_word). Accepted values: none, fade_in (fade-in), slide_up (slide from bottom), slide_down (slide from top), slide_left (slide from right), bounce (continuous vertical oscillation).
text_effect_intensityfloat1.0Text animation intensity (0.25–2.0). Controls fade speed, slide entry duration, or bounce frequency and amplitude. Ignored if text_effect is none.
text_border_widthint0Thickness of the black outline around characters, in pixels (0 = disabled, 1–10). Improves readability on bright or colored backgrounds. Ignored if text is absent.
text_modestring"static"Text reveal mode. static (default): full text visible from frame 1. word_by_word: words appear one by one at the rate set by word_reveal_speed. Mutually exclusive with text_effect: if text_mode=word_by_word, text_effect is ignored.
word_reveal_speedfloat1.5Word reveal speed in words per second (0.3–5.0, default 1.5). Only used when text_mode=word_by_word. Recommended value for OpenAI TTS sync: 2.0.
word_animstring"none"Word appearance animation. none (default): instant reveal. fade: each new word fades in over 0.2s with a crossfade to the previous block. Only used when text_mode=word_by_word.

options parameters — overlay_video mode

All optional. Upload order matters: the first file is the main video (background), the second file is the overlay (foreground). Use chroma_key to remove a green screen from the overlay.

KeyTypeDefaultDescription
positionstring"bottom-right"Corner where the overlay is placed: top-left, top-right, bottom-left, bottom-right, or center.
scalefloat0.30Overlay size as a fraction of the main video width. 0.25 = 25% of the width. Range: 0.05–1.0.
opacityfloat1.0Overlay transparency. 1.0 = fully opaque, 0.0 = invisible.
marginint10Gap in pixels between the overlay and the frame edge. Ignored when position=center. Range: 0–200.
audiostring"main"Which audio to keep. main: main video audio only. overlay: overlay audio only. mix: both tracks blended at equal volume.
chroma_keyboolfalseSet to true to remove the pure green (#00FF00) background of the overlay — useful for webcam or animations filmed on a green screen.
chroma_similarityfloat0.20Green screen tolerance. Low = strict match (preserves subject edges). High = broader removal (handles uneven lighting). Recommended: 0.10–0.20 for a digital green, 0.25–0.45 for a physical screen. Range: 0.01–0.60.

options parameters — Edit audio (mix_audio)

Available only with mode=mix_audio. Ignored for add_audio.

KeyTypeDefaultDescription
mp3_volumefloat1.0Volume of the overlaid MP3 (0.0 = muted, 1.0 = original volume, 2.0 = double). Values above 1.0 may clip.

Success response (HTTP 200)

{ "success": true, "job_id": "a3f1c8d2e5b09471...", "mode": "fusion", // "fusion" | "add_audio" | "mix_audio" | "image_to_video" | "overlay_video" "file_count": 2, "transition": null, "quota_used": 1, "quota_remaining": 9, "status_url": "https://rapidvideomaker.com/api/v1/jobs.php?job_id=a3f1c8..." }

curl example — image_to_video mode

curl -X POST https://rapidvideomaker.com/api/v1/render.php \ -H "Authorization: Bearer rvm_your_token" \ -F "videos[]=@scene.jpg" \ -F "videos[]=@narration.mp3" \ -F "mode=image_to_video" \ -F 'options={"text":"Chapter 1","font_size":72,"text_color":"white","text_position":"bottom","box":true,"box_color":"black@0.4","fade_duration":0.5,"width":1080,"height":1920,"image_fit":"contain","bg_color":"black","enable_image_motion":true,"image_motion_effect":"ken_burns","motion_intensity":1.0,"text_effect":"slide_up","text_effect_intensity":1.0}' # image_fit : "contain" | "cover" # bg_color : ignored if image_fit="cover" # enable_image_motion : true = animation on, false (default) = static image # image_motion_effect : zoom_in | zoom_out | pan_left_to_right | pan_right_to_left | pan_top_to_bottom | pan_bottom_to_top | zoom_pan_soft | ken_burns # motion_intensity : 0.25–2.0 — photo animation amplitude (default 1.0) # text_effect : none | fade_in | slide_up | slide_down | slide_left | bounce (ignored if text_mode=word_by_word) # text_effect_intensity : 0.25–2.0 — text animation intensity (default 1.0) # text_border_width : 0–10 px — black outline around characters (0 = disabled) # text_mode : "static" (default) | "word_by_word" — reveal words one by one # word_reveal_speed : 0.3–5.0 w/s — speed for word_by_word mode (default 1.5, ~2.0 for OpenAI TTS) # word_anim : "none" (default) | "fade" — fade-in crossfade on each word (word_by_word only)

curl example — Edit audio

# Replace audio (deletes original) curl -X POST https://rapidvideomaker.com/api/v1/render.php \ -H "Authorization: Bearer rvm_your_token" \ -F "videos[]=@my_video.mp4" \ -F "videos[]=@my_music.mp3" \ -F "mode=add_audio" # Keep original audio + overlay MP3 curl -X POST https://rapidvideomaker.com/api/v1/render.php \ -H "Authorization: Bearer rvm_your_token" \ -F "videos[]=@my_video.mp4" \ -F "videos[]=@my_music.mp3" \ -F "mode=mix_audio" \ -F 'options={"mp3_volume":0.7}' # mp3_volume : 0.0 (muted) → 1.0 (original volume, default) → 2.0 (amplified)

curl example — overlay_video mode

curl -X POST https://rapidvideomaker.com/api/v1/render.php \ -H "Authorization: Bearer rvm_your_token" \ -F "videos[]=@main_video.mp4" \ -F "videos[]=@overlay_video.mp4" \ -F "mode=overlay_video" \ -F 'options={"position":"bottom-right","scale":0.25,"opacity":1.0,"margin":20,"audio":"main"}' # Green screen overlay curl -X POST https://rapidvideomaker.com/api/v1/render.php \ -H "Authorization: Bearer rvm_your_token" \ -F "videos[]=@main_video.mp4" \ -F "videos[]=@webcam_greenscreen.mp4" \ -F "mode=overlay_video" \ -F 'options={"position":"bottom-left","scale":0.35,"chroma_key":true,"chroma_similarity":0.15,"audio":"mix"}' # position : top-left | top-right | bottom-left | bottom-right | center (default: bottom-right) # scale : 0.05–1.0 — overlay width as fraction of main video (default 0.30) # opacity : 0.0–1.0 — overlay transparency (default 1.0) # margin : 0–200 px — gap from frame edge, ignored if center (default 10) # audio : main | overlay | mix — audio to keep (default: main) # chroma_key : true | false — remove green (#00FF00) background from overlay (default false) # chroma_similarity : 0.01–0.60 — green screen tolerance (default 0.20)

curl example — Merge videos

curl -X POST https://rapidvideomaker.com/api/v1/render.php \ -H "Authorization: Bearer rvm_your_token" \ -F "videos[]=@clip1.mp4" \ -F "videos[]=@clip2.mp4" \ -F "transition_type=fade" \ -F "transition_duration=0.5"
GET /api/v1/jobs.php?job_id=<hex32> Job status

Possible statuses

StatusMeaningAdditional fields
queued In queue, not yet processed queue_position, queue_total, eta_minutes
processing FFmpeg processing in progress
ready Completed — file available download_url, created_at, expires_at
error Processing failed error (message)

Responses by status

{ "job_id": "a3f1c8...", "status": "queued", "queue_position": 2, "queue_total": 3, "eta_minutes": 7 }
{ "job_id": "a3f1c8...", "status": "ready", "download_url": "https://rapidvideomaker.com/api/download.php?job_id=a3f1c8...", "created_at": "2026-06-02T10:00:00+00:00", "expires_at": "2026-06-02T12:00:00+00:00" }

curl example

curl -H "Authorization: Bearer rvm_your_token" \ "https://rapidvideomaker.com/api/v1/jobs.php?job_id=a3f1c8..."
GET /api/v1/jobs.php List all jobs created with this token (paginated, sorted by date desc)

Common parameters (multipart/form-data)

apidocs_th_paramTypeDefaultapidocs_th_desc
pageinteger1Page number (starts at 1)
limitinteger20Results per page (max 50, default 20)

Responses by status

{ "success": true, "jobs": [ { "job_id": "a3f1c8...", "status": "ready", "mode": "fusion", "created_at": "2026-06-02T10:00:00+00:00", "expires_at": "2026-06-02T12:00:00+00:00", "download_url": "https://..." }, ... ], "total": 12, "page": 1, "limit": 20 }
POST /api/v1/publish.php Publish a video to YouTube or TikTok

Publishes a finished job (status=ready) to one or more social accounts. The token must belong to a member account (not an admin token).

Prerequisite: Social accounts (YouTube, TikTok) must first be connected via OAuth in the Publications page of your member area. The account_id for each account is shown there.

Common parameters (multipart/form-data)

apidocs_th_paramTypeapidocs_th_requiredapidocs_th_desc
job_idstringapidocs_requiredJob ID (hex 32 chars) of the finished video
publicationsarrayapidocs_requiredArray of publication objects (max 10)

Publication object

apidocs_th_paramTypeapidocs_th_requiredapidocs_th_desc
account_idintegerapidocs_requiredID of the connected social account — find it in the Publications page of your member area
titlestringVideo title (max 512 chars)
descriptionstringVideo description (max 5 000 chars)
tagsstringComma-separated tags (max 1 024 chars)
visibilitystringpublic | private | unlisted — Default: public
tiktok_modestringdraft | direct — TikTok only — default: direct
tiktok_privacystringdirect modeTikTok only — required in direct mode
allow_commentbooleanTikTok only — allow comments
allow_duetbooleanTikTok only — allow duets
allow_stitchbooleanTikTok only — allow stitches
your_brandbooleanTikTok only — organic promotional content ("Your Brand").
branded_contentbooleanTikTok only — branded content disclosure
tiktok_cover_msintegerTikTok only — cover frame timestamp in ms (0–60 000, default 1 000)

Responses by status

{ "success": true, "results": [ { "account_id": 12, "pub_id": 47, "status": "published", "provider": "youtube", "url": "https://youtu.be/abc123" } ] }

Per-publication error codes

apidocs_th_codeMeaning
job_not_readyJob not finished yet
mode_not_supportedtext_to_mp3 jobs cannot be published
account_not_foundAccount not found or disconnected
tiktok_privacy_requiredPrivacy level required in direct mode
publication_failedProvider returned an error during upload

cURL example

curl -X POST https://rapidvideomaker.com/api/v1/publish.php \ -H "Authorization: Bearer rvm_your_token" \ -H "Content-Type: application/json" \ -d '{ "job_id": "a3f1c8...", "publications": [ { "account_id": 12, "title": "My video", "description": "Created with RapidVideoMaker", "visibility": "public" } ] }'
GET /api/download.php?job_id=<hex32> Download the final file

Returns the final MP4 file as a stream (Content-Type: video/mp4). Does not require a token — the URL is sufficiently opaque. Available only when the status is ready.

The file is kept for 2 hours after the job completes, then deleted. Download it as soon as the status becomes ready.

Optional parameter

ParameterDescription
filenameSuggested name for the downloaded file (e.g. my-video.mp4)

curl example

curl -o "output.mp4" \ "https://rapidvideomaker.com/api/download.php?job_id=a3f1c8...&filename=output.mp4"

Webhooks

Webhooks are an alternative to polling: instead of checking the status every X seconds, you provide a URL and the server calls you automatically when the job completes.

How it works

  1. Pass callback_url in the POST to /api/v1/render.php
  2. The response contains a webhook_secret — keep it to verify signatures
  3. When the job reaches ready or error, the server sends a signed JSON POST to your URL
  4. Verify the signature with HMAC-SHA256(webhook_secret, raw_body)
Constraints: callback_url must be a publicly accessible URL (http or https). Private, loopback and reserved IP addresses are blocked server-side. Timeout: 10 seconds.

Received payload

{ "job_id": "a3f1c8...", "status": "ready", "download_url": "https://rapidvideomaker.com /api/download.php?job_id=a3f1c8...", "timestamp": "2026-04-04T12:00:00+00:00" }
{ "job_id": "a3f1c8...", "status": "error", "error": "FFmpeg xfade failed:...", "timestamp": "2026-04-04T12:00:00+00:00" }

Headers sent by the server

HeaderValue
Content-Typeapplication/json
X-RVM-Signaturesha256=<hmac_hex> — HMAC-SHA256 signature of the raw body
X-RVM-Job-IdThe relevant job_id

Signature verification

# Python import hmac, hashlib def verify_webhook(secret: str, body: bytes, signature_header: str) -> bool: expected = "sha256=" + hmac.new( secret.encode(), body, hashlib.sha256 ).hexdigest() return hmac.compare_digest(expected, signature_header) # Flask/FastAPI endpoint: sig = request.headers.get("X-RVM-Signature", "") if not verify_webhook(WEBHOOK_SECRET, request.data, sig): return "Invalid signature", 403 data = request.get_json()

Error codes

HTTPJSON codeCause
401unauthorizedToken missing, invalid or revoked
400missing_filesNo files received
400too_few_filesFewer than 2 files sent
400too_many_filesMore than 20 files sent
400invalid_mimeUnsupported file — only MP4, MP3 and JPG/PNG images are accepted (MIME verified by magic bytes)
400wrong_file_countThe number of files does not match the chosen mode (e.g. 3 files with mode=add_audio which requires 2)
400wrong_file_typesFile types do not match the chosen mode (e.g. 2 MP4s with mode=add_audio which requires 1 MP4 + 1 MP3; or 1 MP4 + 1 MP3 with mode=image_to_video)
400invalid_file_combinationFile combination without a specified mode does not match any supported mode
400invalid_optionsThe options field is not valid JSON or contains an expected object
400invalid_optionA value in options is invalid (color, font name, position…)
400file_too_largeA file exceeds 500 MB
400invalid_orderThe order array is not a valid permutation
400invalid_job_idIncorrect job_id format (must be 32-char hex)
404job_not_foundJob not found — job_id unknown or never existed
410job_expiredJob expired — existed but the 2h recovery window has passed
429quota_exceeded10 successful jobs/day reached (all modes combined)
429rate_limitedMore than 12 calls/min on /api/v1/jobs.php — wait Retry-After seconds
500server_errorInternal server error

Integration examples

Examples for each mode — install requests with pip install requests.

Common configuration

import requests, time, sys, json API_BASE = "https://rapidvideomaker.com" API_TOKEN = "rvm_your_token_here" HEADERS = {"Authorization": f"Bearer {API_TOKEN}"}

image_to_video mode — image + audio → video

with open("scene.jpg", "rb") as fi, open("narration.mp3", "rb") as fa: resp = requests.post( f"{API_BASE}/api/v1/render.php", headers=HEADERS, files=[ ("videos[]", ("scene.jpg", fi, "image/jpeg")), ("videos[]", ("narration.mp3", fa, "audio/mpeg")), ], data={ "mode": "image_to_video", "options": json.dumps({ "text": "Chapter 1", "font_size": 72, "text_color": "white", "text_position": "bottom", "box": True, "box_color": "black@0.4", "fade_duration": 0.5, "width": 1080, "height": 1920, "image_fit": "contain", # "contain" | "cover" "bg_color": "black", # ignored if image_fit="cover" "enable_image_motion": True, "image_motion_effect": "ken_burns", "motion_intensity": 1.0, # 0.25–2.0 — photo animation amplitude "text_effect": "slide_up", # none | fade_in | slide_up | slide_down | slide_left | bounce (ignored if text_mode=word_by_word) "text_effect_intensity": 1.0, # 0.25–2.0 — text animation intensity # word-by-word reveal (mutually exclusive with text_effect): # "text_mode": "word_by_word", "word_reveal_speed": 2.0, "word_anim": "fade" }), }, )

Edit audio

# Replace audio (deletes original) — mode=add_audio with open("video.mp4", "rb") as fv, open("music.mp3", "rb") as fa: resp = requests.post( f"{API_BASE}/api/v1/render.php", headers=HEADERS, files=[ ("videos[]", ("video.mp4", fv, "video/mp4")), ("videos[]", ("music.mp3", fa, "audio/mpeg")), ], data={"mode": "add_audio"}, ) # Keep original audio + overlay MP3 — mode=mix_audio with open("video.mp4", "rb") as fv, open("music.mp3", "rb") as fa: resp = requests.post( f"{API_BASE}/api/v1/render.php", headers=HEADERS, files=[ ("videos[]", ("video.mp4", fv, "video/mp4")), ("videos[]", ("music.mp3", fa, "audio/mpeg")), ], data={"mode": "mix_audio", "options": json.dumps({"mp3_volume": 0.7})}, # mp3_volume : 0.0 = muted, 1.0 = original volume (default), 2.0 = amplified )

overlay_video mode — picture-in-picture

with open("main.mp4", "rb") as fm, open("overlay.mp4", "rb") as fo: resp = requests.post( f"{API_BASE}/api/v1/render.php", headers=HEADERS, files=[ ("videos[]", ("main.mp4", fm, "video/mp4")), ("videos[]", ("overlay.mp4", fo, "video/mp4")), ], data={ "mode": "overlay_video", "options": json.dumps({ "position": "bottom-right", # top-left | top-right | bottom-left | bottom-right | center "scale": 0.25, # 0.05–1.0 — overlay width fraction "opacity": 1.0, # 0.0–1.0 — transparency "margin": 20, # 0–200 px — gap from frame edge "audio": "main", # main | overlay | mix # green screen: "chroma_key": True, "chroma_similarity": 0.15 }), }, )

Merge videos

with open("clip1.mp4", "rb") as f1, open("clip2.mp4", "rb") as f2: resp = requests.post( f"{API_BASE}/api/v1/render.php", headers=HEADERS, files=[ ("videos[]", ("clip1.mp4", f1, "video/mp4")), ("videos[]", ("clip2.mp4", f2, "video/mp4")), ], data={"transition_type": "fade", "transition_duration": "0.5"}, )

Polling + download (common to all modes)

resp.raise_for_status() job_id = resp.json()["job_id"] print(f"Job created: {job_id}") for _ in range(120): # timeout 10 min (120 × 5s) r = requests.get( f"{API_BASE}/api/v1/jobs.php", headers=HEADERS, params={"job_id": job_id}, ) data = r.json() status = data["status"] if status == "queued": pos = data.get("queue_position", "?") eta = data.get("eta_minutes", "?") print(f"Queued — position {pos}, ETA ~{eta} min") elif status == "processing": print("Processing…") elif status == "ready": download_url = data["download_url"] break elif status == "error": sys.exit(f"Error: {data.get('error')}") time.sleep(5) else: sys.exit("Timeout: job not completed after 10 min") r = requests.get(download_url, stream=True) with open("output.mp4", "wb") as f: for chunk in r.iter_content(65536): f.write(chunk) print("Downloaded: output.mp4")

Two approaches in n8n: polling (status loop) or webhook (server calls n8n). The webhook is recommended — cleaner, zero unnecessary load.

Approach A — Webhook (recommended)

3 nodes only, no loop.

Node 1 — Webhook trigger
Add a Webhook node as the workflow input (method POST). Note the generated URL, e.g. https://your-n8n.com/webhook/rvm-callback.

Node 2 — Create the job (HTTP Request)

MethodPOST
URLhttps://rapidvideomaker.com/api/v1/render.php
AuthenticationHeader Auth — Authorization: Bearer rvm_…
Body Content TypeForm-Data Multipart
Body paramsBinary field videos[] for each clip + Text field callback_url = webhook node URL above

Keep the webhook_secret from the response in a workflow variable to verify the signature upon receipt.

Node 3 — Process the result (in the webhook-triggered workflow)
When n8n receives the server's POST, $json.status is ready or error. If ready, $json.download_url contains the direct download URL.

To verify the signature: compare {{ $headers['x-rvm-signature'] }} with sha256= + HMAC-SHA256 of the raw body from your webhook_secret. Use a Code node with crypto.createHmac('sha256', secret).update(body).digest('hex').

Approach B — Polling (without webhook)

5 nodes, status loop every 10 s.

Node 1 — Create the job — same config as above, without callback_url.

Node 2 — Wait — 10 seconds.

Node 3 — Poll status (HTTP Request GET)

URLhttps://rapidvideomaker.com/api/v1/jobs.php
Query paramsjob_id = {{ $('Node 1').item.json.job_id }}
AuthenticationHeader Auth — same token

Node 4 — If: {{ $json.status }} === 'ready' → true: Node 5 / false: back to Node 2.

Add an iteration counter in a Set node to break the loop after 60 attempts (10 min max) to avoid an infinite loop.

Node 5 — Download: HTTP Request GET on {{ $json.download_url }}, Response Format File.

Step 1 — Create the job (choose a mode)

# fusion JOB=$(curl -s -X POST https://rapidvideomaker.com/api/v1/render.php \ -H "Authorization: Bearer rvm_your_token" \ -F "videos[]=@clip1.mp4" -F "videos[]=@clip2.mp4") # add_audio (replace audio) JOB=$(curl -s -X POST https://rapidvideomaker.com/api/v1/render.php \ -H "Authorization: Bearer rvm_your_token" \ -F "videos[]=@video.mp4" -F "videos[]=@music.mp3" \ -F "mode=add_audio") # mix_audio (overlay music at 70% volume) JOB=$(curl -s -X POST https://rapidvideomaker.com/api/v1/render.php \ -H "Authorization: Bearer rvm_your_token" \ -F "videos[]=@video.mp4" -F "videos[]=@music.mp3" \ -F "mode=mix_audio" -F 'options={"mp3_volume":0.7}') # image_to_video JOB=$(curl -s -X POST https://rapidvideomaker.com/api/v1/render.php \ -H "Authorization: Bearer rvm_your_token" \ -F "videos[]=@scene.jpg" -F "videos[]=@narration.mp3" \ -F "mode=image_to_video" \ -F 'options={"text":"Chapter 1","font_size":72,"text_position":"bottom","fade_duration":0.5,"width":1080,"height":1920}') JOB_ID=$(echo $JOB | grep -o '"job_id":"[^"]*"' | cut -d'"' -f4) echo "Job ID: $JOB_ID"

Step 2 — Polling

while true; do STATUS=$(curl -s \ -H "Authorization: Bearer rvm_your_token" \ "https://rapidvideomaker.com/api/v1/jobs.php?job_id=$JOB_ID") STATE=$(echo $STATUS | grep -o '"status":"[^"]*"' | cut -d'"' -f4) echo "Status: $STATE" [ "$STATE" = "ready" ] && break [ "$STATE" = "error" ] && { echo "Error"; exit 1; } sleep 10 done

Step 3 — Download

DOWNLOAD_URL=$(echo $STATUS | grep -o '"download_url":"[^"]*"' | cut -d'"' -f4) curl -o output.mp4 "$DOWNLOAD_URL"

Best practices

Result files are kept 2 hours after the job completes, then automatically deleted. Download your video as soon as the status becomes ready.