API v1ProductionServer-to-server

Ship your first media job in minutes.

Upload once, request exactly the outputs you need, and receive a signed terminal webhook. The examples below match the live API contract used by production integrations.

The contract at a glance
1
Upload
POST /v1/upload-url, then PUT bytes
2
Submit
POST /v1/transcode with file_uri
3
Acknowledge
Store the returned job_id
4
Complete
Verify and process the signed webhook
Quickstart

Upload, submit, then wait for the webhook.

MediaRuntime is asynchronous. A successful submission returns QUEUED immediately; completed outputs arrive later through your signed webhook.

cURL
Upload and submit
# 1. Ask MediaRuntime for a short-lived upload URL.
UPLOAD=$(curl -sS -X POST "https://mediaruntime.com/v1/upload-url" \
  -H "X-API-Key: $MEDIARUNTIME_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"filename":"launch-trailer.mp4","content_type":"video/mp4"}')

UPLOAD_URL=$(printf '%s' "$UPLOAD" | jq -r .upload_url)
FILE_URI=$(printf '%s' "$UPLOAD" | jq -r .file_uri)
UPLOAD_CONTENT_TYPE=$(printf '%s' "$UPLOAD" | jq -r '.upload_headers["Content-Type"]')
UPLOAD_AUTH=$(printf '%s' "$UPLOAD" | jq -r '.upload_headers.Authorization // empty')

# 2. Upload the bytes. Send every returned upload header.
UPLOAD_ARGS=(-H "Content-Type: $UPLOAD_CONTENT_TYPE")
if [[ -n "$UPLOAD_AUTH" ]]; then
  UPLOAD_ARGS+=(-H "Authorization: $UPLOAD_AUTH")
fi
curl -sS -X PUT "$UPLOAD_URL" \
  "${UPLOAD_ARGS[@]}" \
  --upload-file ./launch-trailer.mp4

# 3. Queue an asynchronous job using the returned file_uri.
curl -sS -X POST "https://mediaruntime.com/v1/transcode" \
  -H "X-API-Key: $MEDIARUNTIME_API_KEY" \
  -H "Content-Type: application/json" \
  -d "$(jq -n --arg file_uri "$FILE_URI" '{
    file_url: $file_uri,
    metadata: { asset_id: "asset_0426", media_type: "video" },
    outputs: [{
      type: "mp4",
      preset: "mp4_720p_h264_aac",
      poster_time_sec: 2
    }]
  }')"
Use the returned file_uri
Never construct an input bucket path yourself. The upload endpoint chooses the correct account-scoped location and returns the exact URI for the job.
Keep job_id as your durable key
Persist it alongside your own entity ID. Your metadata is echoed back in the webhook, making reconciliation straightforward.
JSON
Immediate response
{
  "job_id": "job_1320c28b72104811b075a26a99496cf6",
  "status": "QUEUED",
  "tier": "standard",
  "msg": "accepted"
}
Authentication

Keep API keys on your server.

Create a key from Account → API Keys. The raw key is shown once and belongs in your secret manager, never in browser code, mobile binaries, logs, or source control.

Header

Send X-API-Key on every /v1 request.

Storage

Store MEDIARUNTIME_API_KEY in a server-side secret manager.

Rotation

Create a replacement, deploy it, verify traffic, then revoke the old key.

X-API-Key: sk_live_…
Create jobs

Make metadata do the integration work.

The production pattern used by wMedia is intentionally simple: submit an input, output recipes, and enough opaque metadata to reconnect the terminal event to your own database record.

JSON
Production-style request
{
  "file_url": "gs://bucket/path/source.mp4",
  "metadata": {
    "producer": "my-api",
    "entity_id": "video_01J8Y4",
    "owner_id": "user_482",
    "media_type": "video",
    "trace_id": "req_f839"
  },
  "moderation": {
    "enabled": true,
    "mode": "report",
    "checks": ["sexual", "violence", "dangerous"]
  },
  "watermark": { "enabled": true },
  "outputs": [
    {
      "type": "mp4",
      "preset": "mp4_720p_h264_aac",
      "path_suffix": "web",
      "poster_time_sec": 2,
      "gif_preview": {
        "enabled": true,
        "width": 320,
        "fps": 8,
        "start_time": 2,
        "duration": 2.5
      }
    },
    {
      "type": "image",
      "preset": "image_multi_v1",
      "path_suffix": "covers",
      "images": [
        { "width": 1280, "height": 720, "mode": "fit", "format": "webp", "quality": 84 },
        { "width": 480, "height": 480, "mode": "cover", "format": "webp", "quality": 80 }
      ]
    }
  ]
}
Field
file_url
Type
string
Notes
One gs:// or public HTTPS input. Use this or inputs, never both.
Field
inputs
Type
array
Notes
Batch fan-out for 1–25 inputs. Each may carry input_id and metadata.
Field
outputs
Type
array
Notes
1–10 output recipes. Each requires type; preset is strongly recommended.
Field
metadata
Type
object
Notes
Up to 32 KiB of JSON. Persisted and echoed at meta.request_metadata.
Field
moderation
Type
object
Notes
Premium visual-media checks: sexual, violence, dangerous.
Field
watermark
Type
object
Notes
Premium visual-media overlay. The account must already have a PNG logo.

Batch fan-out

Use a batch when every input needs the same outputs. Per-input metadata is merged into each child job; the parent job becomes your batch reference.

JSON
Two inputs, one output recipe
{
  "inputs": [
    {
      "file_url": "https://cdn.example.com/a.mp4",
      "input_id": "asset-a",
      "metadata": { "position": 0 }
    },
    {
      "file_url": "https://cdn.example.com/b.mp4",
      "input_id": "asset-b",
      "metadata": { "position": 1 }
    }
  ],
  "metadata": { "batch_id": "import_2026_08_09" },
  "outputs": [{ "type": "mp4", "preset": "mp4_720p_h264_aac" }]
}
Output presets

Choose the artifact you want the engine to produce.

The type and preset form one executable recipe. Use the exact type shown below: a preset name alone does not change the output type, and mismatched pairs can be rejected or routed incorrectly.

JSON
Vertical social video
{
  "file_url": "https://cdn.example.com/landscape-interview.mp4",
  "outputs": [{
    "type": "social",
    "preset": "social_vertical_blur"
  }]
}
What Social executes
social_vertical_blur creates a 1080×1920 H.264/AAC MP4. The source is scaled to fit without cropping; a blurred copy fills the 9:16 canvas behind it. Use it for Reels, TikTok, and Shorts. It is a Premium recipe because the vertical output is 1920 pixels high.

Video files and posters

Preset
mp4_720p_h264_aac (type: mp4)
Send
Video
Job execution
720p H.264/AAC MP4 with faststart for web playback.
Base tier
Standard
Preset
mp4_ladder_v1 (type: mp4)
Send
Video
Job execution
Three MP4 renditions at 1080p, 720p, and 480p, plus a poster for each rendition.
Base tier
Standard
Preset
transmux_mp4_fast (type: mp4)
Send
Compatible audio/video
Job execution
Copies existing streams into a faststart MP4 without re-encoding. If copy fails and fallback is enabled, the engine re-encodes with the 720p H.264 preset.
Base tier
Standard
Preset
poster_frame_v1 (type: mp4)
Send
Video
Job execution
One 720p JPG captured at poster_time_sec. The request type is still mp4.
Base tier
Standard
Preset
mp4_hevc_1080p (type: mp4)
Send
Video
Job execution
1080p HEVC/H.265 + AAC MP4 with the hvc1 tag for Apple playback.
Base tier
Premium
Preset
mp4_av1_smart (type: mp4)
Send
Video
Job execution
1080p AV1 + Opus MP4 optimized for compression efficiency; encoding is CPU-intensive.
Base tier
Premium
Preset
mov_prores_422 (type: mp4)
Send
Video
Job execution
ProRes 422 HQ + PCM MOV editing master. Preserves source dimensions and produces a large mezzanine file.
Base tier
Premium

Social video

Preset
social_vertical_blur (type: social)
Send
Video
Job execution
1080×1920 H.264/AAC MP4. Fits the source over a blurred 9:16 background for Reels, TikTok, and Shorts.
Base tier
Premium

Animated GIF

Preset
gif_hq (type: gif)
Send
Video
Job execution
Animated GIF at 480px width and 15 fps using palette generation for better color quality.
Base tier
Standard

Frame extraction

Preset
extract_frames_1 (type: frames)
Send
Video
Job execution
Numbered JPG frame sequence sampled at 1 frame per second.
Base tier
Standard
Preset
extract_frames_5 (type: frames)
Send
Video
Job execution
Numbered JPG frame sequence sampled at 5 frames per second.
Base tier
Standard

Streaming

Preset
hls_ladder_v1 (type: hls)
Send
Video
Job execution
HLS VOD package with 1080p and 720p H.264/AAC variants, master playlist, and 6-second segments.
Base tier
Standard

Audio

Preset
audio_copy_fast (type: audio)
Send
Audio or video with audio
Job execution
Copies the source audio stream without re-encoding. If copy fails and fallback is enabled, the engine writes 128 kbps AAC instead.
Base tier
Standard
Preset
audio_aac_128k (type: audio)
Send
Audio or video with audio
Job execution
128 kbps AAC in a faststart M4A file.
Base tier
Standard
Preset
audio_mp3_128k (type: audio)
Send
Audio or video with audio
Job execution
128 kbps MP3 file.
Base tier
Standard
Preset
audio_opus_96k (type: audio)
Send
Audio or video with audio
Job execution
96 kbps Opus file, well suited to speech delivery.
Base tier
Standard
Preset
audio_loudnorm_aac_128k (type: audio)
Send
Audio or video with audio
Job execution
Normalizes toward -16 LUFS, then writes 128 kbps AAC.
Base tier
Standard
Preset
audio_trim_silence_aac_128k (type: audio)
Send
Audio or video with audio
Job execution
Removes leading and trailing silence, then writes 128 kbps AAC.
Base tier
Premium
Preset
audio_loudnorm_trim_aac_128k (type: audio)
Send
Audio or video with audio
Job execution
Trims boundary silence, normalizes toward -16 LUFS, then writes 128 kbps AAC.
Base tier
Premium
Preset
audio_whisper_prep (type: audio)
Send
Audio or video with audio
Job execution
16 kHz mono PCM WAV prepared for Whisper, ASR, or other speech pipelines.
Base tier
Premium

Image derivatives

Preset
image_multi_v1 (type: image)
Send
Image
Job execution
Creates the images array you request using fit, fill, cover, or contain. Tier depends on format, size, count, smart crop, and background removal.
Base tier
Standard or Premium
Stream-copy compatibility
transmux_mp4_fast avoids a quality-changing encode when the source is MP4-compatible. If copy fails and fallback is enabled, the engine re-encodes with mp4_720p_h264_aac; use that preset directly when you require predictable output characteristics.
Tier can rise with overrides
The table shows the base Workspace tier. Extra outputs, WebP/AVIF images, large image sets, smart crop, background removal, watermarking, moderation, advanced subtitles, or GIF previews can require Premium.
Format conversion

Upload once. Produce the formats and sidecar artifacts your product needs.

The source extension does not select the output. The type and preset choose the executable recipe, so one uploaded video can become playback video, audio-only media, transcripts, GIF previews, posters, or frame sequences in the same asynchronous job.

Source
JPG, PNG, or WebP
Deliverable
JPG, PNG, WebP, or AVIF derivatives
Recipe
image + image_multi_v1; choose images[].format, dimensions, mode, and quality.
Source
Video
Deliverable
Web MP4, HLS, social video, or editing master
Recipe
Choose the matching mp4, hls, or social preset.
Source
Video
Deliverable
Animated GIF, poster, or JPG frame sequence
Recipe
Use gif_hq, poster_frame_v1, extract_frames_1/5, or attach gif_preview to a video output.
Source
Video or audio
Deliverable
M4A, MP3, Opus, or speech WAV
Recipe
Choose the corresponding audio_* preset; video inputs have their audio stream extracted.
Source
Video or audio speech
Deliverable
SRT, WebVTT, or both
Recipe
Add subtitles to an audio or video output and choose srt, vtt, or both.
JSON
PNG → JPG + WebP
{
  "file_url": "gs://value-returned-by-upload-url",
  "metadata": {
    "media_type": "image",
    "asset_id": "product-photo-0426"
  },
  "outputs": [{
    "type": "image",
    "preset": "image_multi_v1",
    "path_suffix": "converted",
    "images": [
      { "width": 1600, "height": 1200, "mode": "fit", "format": "jpg", "quality": 88 },
      { "width": 1600, "height": 1200, "mode": "fit", "format": "webp", "quality": 82 }
    ]
  }]
}
JSON
One video → MP4 + MP3
{
  "file_url": "gs://value-returned-by-upload-url",
  "outputs": [
    { "type": "mp4", "preset": "mp4_720p_h264_aac", "path_suffix": "web-video" },
    { "type": "audio", "preset": "audio_mp3_128k", "path_suffix": "audio-only" }
  ]
}
JSON
Video → M4A + SRT + WebVTT
{
  "file_url": "gs://value-returned-by-upload-url",
  "metadata": { "media_type": "video", "asset_id": "interview-0426" },
  "outputs": [{
    "type": "audio",
    "preset": "audio_aac_128k",
    "path_suffix": "audio-and-transcript",
    "subtitles": {
      "enabled": true,
      "languages": ["auto"],
      "format": "both",
      "model": "ggml-base.bin",
      "translate_to_english": false
    }
  }]
}
JSON
Video → MP4 + poster + GIF preview
{
  "file_url": "gs://value-returned-by-upload-url",
  "metadata": { "media_type": "video", "asset_id": "trailer-0426" },
  "outputs": [{
    "type": "mp4",
    "preset": "mp4_720p_h264_aac",
    "path_suffix": "web",
    "poster_time_sec": 4,
    "poster_format": "jpg",
    "gif_preview": {
      "enabled": true,
      "width": 480,
      "fps": 10,
      "start_time": 4,
      "duration": 3
    }
  }]
}
JSON
Video → HLS adaptive streaming package
{
  "file_url": "gs://value-returned-by-upload-url",
  "metadata": { "media_type": "video", "asset_id": "stream-0426" },
  "outputs": [{
    "type": "hls",
    "preset": "hls_ladder_v1",
    "path_suffix": "stream"
  }]
}
JSON
Video → standalone GIF + JPG frame sequence
{
  "file_url": "gs://value-returned-by-upload-url",
  "metadata": { "media_type": "video", "asset_id": "clip-0426" },
  "outputs": [
    { "type": "gif", "preset": "gif_hq", "path_suffix": "animated-preview" },
    { "type": "frames", "preset": "extract_frames_1", "path_suffix": "sampled-frames" }
  ]
}
HLS is a package, not one video file
hls_ladder_v1 creates a master playlist, 1080p and 720p H.264/AAC variant playlists, and 6-second media segments. Use the reported master-playlist URL, or move the complete bundle together.
Dedicated GIF versus attached preview
gif_hq creates a full 480px, 15 fps primary GIF. gif_preview adds a shorter, explicitly timed GIF to another video output. Frame presets return numbered JPG sequences at one or five frames per second.
Every artifact stays attached to the job
Read the completed job’s output manifest or use its branded bundle URL. The audio, transcripts, poster, preview, and playback files are included without uploading the source again. Do not construct filenames or storage paths.
Estimate the complete output set
MediaRuntime estimates every requested output and feature before execution. GIF previews, WebP/AVIF derivatives, advanced subtitles, and multi-output jobs can require Premium; the API does not silently omit them.

Useful switches

audio_aac_128k returns M4A, audio_mp3_128k returns MP3, audio_opus_96k returns Opus, and audio_whisper_prep returns 16 kHz mono WAV. Set subtitles.format to srt, vtt, or both. For only one poster image, send type: mp4 with preset: poster_frame_v1 and the desired poster_time_sec.

Recipes

Start with a preset; override only what matters.

Presets keep requests readable and give the engine a stable baseline. Add explicit rendition, subtitle, preview, codec, or bitrate options only when the product requires them.

JSON
Responsive image derivatives
{
  "file_url": "https://cdn.example.com/source.jpg",
  "outputs": [{
    "type": "image",
    "preset": "image_multi_v1",
    "images": [
      { "width": 1200, "height": 630, "mode": "cover", "format": "webp", "quality": 84 },
      { "width": 320, "height": 320, "mode": "cover", "format": "webp", "quality": 78 }
    ]
  }]
}
JSON
Audio plus transcript files
{
  "file_url": "https://cdn.example.com/interview.wav",
  "outputs": [{
    "type": "audio",
    "preset": "audio_aac_128k",
    "subtitles": {
      "enabled": true,
      "languages": ["auto"],
      "format": "both",
      "model": "ggml-base.bin"
    }
  }]
}
Premium feature routing
Moderation, watermarking, advanced codecs, multiple outputs, GIF previews, and some subtitle features can require Premium. The API rejects work your account cannot run rather than silently dropping features.
Watermark setup
Upload and confirm one account PNG from the Account page. Then send only { "watermark": { "enabled": true } }; MediaRuntime resolves the server-owned logo.
Moderation

Attach a sampled visual-safety report to the job.

Moderation runs before transcoding for one image or video input. The current production contract is report-only: the report records evidence and decisions, but it does not block the transcode.

JSON
Request all visual checks
{
  "file_url": "https://cdn.example.com/upload.mp4",
  "metadata": { "media_type": "video", "asset_id": "asset_0426" },
  "moderation": {
    "enabled": true,
    "mode": "report",
    "checks": ["sexual", "violence", "dangerous"]
  },
  "outputs": [{
    "type": "mp4",
    "preset": "mp4_720p_h264_aac"
  }]
}
JSON
Result on the completed job and webhook
{
  "moderation": {
    "requested": {
      "enabled": true,
      "mode": "report",
      "checks": ["sexual", "violence", "dangerous"],
      "media_type": "video",
      "phase": "phase1_video_report"
    },
    "result": {
      "ok": true,
      "media_type": "video",
      "verdict": "review",
      "flagged_checks": ["violence"],
      "scores": {
        "violence": { "yes": 0.82, "no": 0.18 }
      },
      "decisions": {
        "violence": { "decision": "review", "raw_decision": "review" }
      },
      "evidence": {
        "frames_sampled": 8,
        "frames_flagged": [{
          "frame_index": 3,
          "timestamp_sec": 20,
          "verdict": "review",
          "flagged_checks": ["violence"]
        }]
      },
      "video": {
        "frame_interval_sec": 10,
        "max_frames": 24
      }
    }
  },
  "meta": {
    "moderation_result": {
      "url": "https://storage.googleapis.com/.../moderation_result.json"
    }
  },
  "usage": {
    "breakdown": { "moderation_units": 120 }
  }
}
Contract
Plan
Value
Premium
Notes
The API returns 403 unless the account is Premium or auto-upgrade is allowed.
Contract
Mode
Value
report
Notes
This is the only accepted mode today. Do not send block.
Contract
Checks
Value
sexual, violence, dangerous
Notes
Send one to three checks. Omitting checks selects all three.
Contract
Inputs
Value
One image or video
Notes
Audio-only inputs, batches, and Sandbox moderation are rejected.
Contract
Video sampling
Value
Fixed interval, bounded frames
Notes
The service chooses the interval and cap; read the actual values from result.video and result.evidence.
Contract
Decision
Value
allow, review, or block signal
Notes
Report mode never blocks execution. Use result.verdict, decisions, flagged_checks, and evidence in your own policy.
Contract
Artifact
Value
meta/moderation_result.json
Notes
Included in the output ZIP and exposed through meta.moderation_result.url when available.
Contract
Billing
Value
Separate moderation units
Notes
Estimated and settled with the job at usage.breakdown.moderation_units.
Your application owns enforcement
Treat review as a signal for a human queue, publishing hold, or another policy you control. A completed report is not a promise that the media is safe, legal, or policy-compliant.
Models can be wrong
Scores are classifier outputs, not facts. Keep the evidence, version your downstream thresholds, provide an appeal path where appropriate, and avoid fully automated high-impact decisions.
Webhooks

Verify the raw bytes before trusting the event.

Terminal events are delivered at least once and ordering is not guaranteed. Verify HMAC-SHA256, reject stale timestamps, deduplicate event_id, acknowledge quickly, and move heavy work to a queue.

JSON
COMPLETED event
{
  "event_id": "webhook_evt_job_1320c28b72104811b075a26a99496cf6",
  "job_id": "job_1320c28b72104811b075a26a99496cf6",
  "account_id": "acc_xxx",
  "status": "COMPLETED",
  "completedAt": "2026-08-09T02:41:23Z",
  "billing": { "status": "PAID", "estimatedUnits": 31 },
  "usage": { "units_total": 31, "breakdown": {} },
  "delivery": {
    "mode": "PULL",
    "retentionDays": 7,
    "expiresAt": "2026-08-16T02:41:23Z",
    "bundle": {
      "type": "zip",
      "filename": "outputs.zip",
      "download": {
        "url": "https://mediaruntime.com/v1/jobs/job_1320c28b72104811b075a26a99496cf6/bundle?token=...",
        "expiresAt": "2026-08-16T02:41:23Z"
      }
    }
  },
  "meta": {
    "engine_result_url": "https://storage.googleapis.com/...",
    "outputs_root_gs": "gs://.../jobs/acc_xxx/job_.../outputs",
    "request_metadata": {
      "producer": "my-api",
      "entity_id": "video_01J8Y4",
      "media_type": "video"
    }
  }
}
Where outputs live
Download the complete ZIP from delivery.bundle.download.url, or fetch meta.engine_result_url to enumerate individual output and artifact paths.
Node
Express raw-body verification
import crypto from "node:crypto";
import express from "express";

const app = express();

// Register this route before any express.json() middleware.
app.post("/webhooks/mediaruntime", express.raw({ type: "application/json" }), (req, res) => {
  const eventId = req.get("X-Transcoder-Id") || "";
  const signatureHeader = req.get("X-Transcoder-Signature") || "";
  const parts = Object.fromEntries(
    signatureHeader.split(",").map((part) => part.trim().split("="))
  );

  const timestamp = parts.t || "";
  const received = parts.v1 || "";
  const ageSeconds = Math.abs(Date.now() / 1000 - Number(timestamp));
  if (!eventId || !timestamp || !received || ageSeconds > 300) {
    return res.sendStatus(401);
  }

  const expected = crypto
    .createHmac("sha256", process.env.MEDIARUNTIME_WEBHOOK_SECRET)
    .update(timestamp + "." + eventId + ".")
    .update(req.body) // raw Buffer; never JSON.stringify(req.body)
    .digest("hex");

  const a = Buffer.from(expected, "utf8");
  const b = Buffer.from(received, "utf8");
  if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
    return res.sendStatus(401);
  }

  const event = JSON.parse(req.body.toString("utf8"));
  // Enqueue work and deduplicate on event.event_id in your database.
  console.log(event.event_id, event.job_id, event.status);
  return res.sendStatus(200);
});
Configure the endpoint in Account
Open Account → Webhooks, enter your HTTPS endpoint, and store the signing secret when it is shown. The public API integration requires only your API key and webhook signing secret.

Delivery rules

  • Return any 2xx only after signature verification and durable enqueue/deduplication.
  • Treat event_id as the idempotency key.
  • Use meta.request_metadata to find your entity without a second lookup table.
  • Download retained outputs before delivery.expiresAt.
  • A FAILED or REJECTED event has error.code/message and no usable bundle.
Billing and pricing

Prepaid, pay as you go, and settled from actual usage.

Add a card, fund the wallet, and submit work without a recurring subscription. MediaRuntime reserves an estimate before execution and settles the final charge when the job reaches a terminal state.

Plan
Standard Pay-As-You-Go
Public rate
$0.02/min
Minimum top-up
$20.00
Default auto-top-up
$20.00 at $2.00 available
Plan
Premium Pay-As-You-Go
Public rate
$0.05/min
Minimum top-up
$60.00
Default auto-top-up
$60.00 at $5.00 available
How reservation and settlement work
Submission reserves the estimated charge plus a 15% safety buffer. Completion charges actual billable usage and releases unused reservation. Pending Stripe top-ups become wallet credit only after the signed payment webhook confirms them.
Why cost can differ from source duration
Multiple outputs and features such as advanced codecs, subtitles, GIFs, moderation, and watermarking add billable work or require Premium. Use the job estimate for planning and the terminal billing and usage fields for reconciliation.

Wallet rules

  • Available credit equals wallet credit minus funds reserved for running jobs.
  • Insufficient available credit returns HTTP 402 before execution.
  • Auto-top-up is optional and requires a card on file.
  • A Premium-only request returns 403 when upgrade is not permitted.
Use the account's billing snapshot
The table is the public USD rate card. Negotiated volume accounts can carry account-specific pricing. Do not derive the final charge from duration alone; persist the estimate and terminal billing snapshot returned for the job.
Errors and retries

Retry transport failures, not invalid work.

Most API errors use a top-level detail field. It may be a string or a structured object, so log the entire response alongside your correlation ID—but never log the API key.

Status
400
Meaning
The request is logically invalid or the estimator rejected it.
What your integration should do
Fix the request; do not retry unchanged.
Status
401
Meaning
The API key is invalid, expired, or revoked.
What your integration should do
Correct or rotate the key; do not blindly retry.
Status
402
Meaning
The account, wallet, or billing preflight cannot cover the job.
What your integration should do
Fund the wallet or resolve billing first.
Status
403
Meaning
The plan, role, or feature gate does not permit the request.
What your integration should do
Change the plan/request; do not retry unchanged.
Status
413
Meaning
The HTTP request body exceeds 2 MiB.
What your integration should do
Upload media separately and send URLs only.
Status
422
Meaning
The JSON does not match the API schema.
What your integration should do
Correct the named field.
Status
429
Meaning
The account or key is rate limited.
What your integration should do
Retry with exponential backoff and jitter.
Status
500/502/503
Meaning
A transient platform dependency failed or a lane is paused.
What your integration should do
Retry safely with backoff; preserve your correlation metadata.
JSON
Typical error body
{
  "detail": "Insufficient wallet balance for this job"
}
Safe retry policy
Retry 429 and transient 5xx responses with capped exponential backoff and jitter. Do not automatically resubmit an accepted job after losing the HTTP response unless your application can detect duplicates; keep your own trace ID in metadata for reconciliation.
API reference

The small surface most integrations need.

All server-to-server endpoints below use X-API-Key. The tokenized bundle URL is the only exception because it carries its own short-lived, job-scoped credential.

Method
POST
Path
/v1/upload-url
Purpose
Create a 15-minute upload target in the input bucket.
Method
POST
Path
/v1/transcode
Purpose
Queue a single-input or batch media job.
Method
GET
Path
/v1/jobs/{job_id}/bundle?token=...
Purpose
Redeem the job-scoped token for a bundle; no API key is required.
Method
POST
Path
/v1/jobs/{job_id}/retry-webhook
Purpose
Retry the terminal webhook for a job you own.
Method
POST
Path
/v1/account/watermark-logo/upload-url
Purpose
Create an upload target for the account PNG logo.
Method
POST
Path
/v1/account/watermark-logo/confirm
Purpose
Confirm the logo and its placement settings.