Expédiez votre premier travail multimédia en quelques minutes.
Fournissez à MediaRuntime une URL de média accessible, demandez exactement les sorties nécessaires et recevez un webhook terminal signé. Utilisez l'endpoint de téléversement facultatif uniquement si vous ne disposez pas déjà d'une URL source.
Soumettez un média URL, puis attendez le webhook.
MediaRuntime accepte directement une URL HTTP(S) publique ou une URL de lecture signée à durée limitée. Elle doit rester accessible jusqu'au téléchargement de l'entrée par le worker. Une soumission réussie renvoie immédiatement QUEUED ; les sorties terminées arrivent ensuite via votre webhook signé.
# Submit a public or time-limited HTTPS source directly.
# Keep the URL fetchable until MediaRuntime has downloaded the input.
curl -sS -X POST "https://mediaruntime.com/v1/jobs" \
-H "X-API-Key: $MEDIARUNTIME_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"file_url": "https://cdn.example.com/media/launch-trailer.mp4",
"metadata": { "asset_id": "asset_0426", "media_type": "video" },
"outputs": [{
"type": "mp4",
"preset": "mp4_720p_h264_aac",
"poster_time_sec": 2
}]
}'# Optional: use this when you have local bytes but no fetchable source 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')
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
# Then use "$FILE_URI" as file_url in POST /v1/jobs.{
"job_id": "job_1320c28b72104811b075a26a99496cf6",
"status": "QUEUED",
"tier": "standard",
"msg": "accepted"
}Conservez les clés API sur votre serveur.
Créez une clé à partir de Compte → Clés API. La clé brute est affichée une fois et appartient à votre gestionnaire de secrets, jamais dans le code du navigateur, les binaires mobiles, les journaux ou le contrôle de source.
En-tête
Envoyez X-API-Key à chaque demande /v1.
Stockage
Stockez MEDIARUNTIME_API_KEY dans un gestionnaire de secrets côté serveur.
Rotation
Créez un remplacement, déployez-le, vérifiez le trafic, puis révoquez l'ancienne clé.
Laissez les métadonnées assurer l'intégration.
Le modèle de production utilisé par wMedia reste volontairement simple : soumettez une entrée, des recettes de sortie et suffisamment de métadonnées opaques pour rattacher l'événement terminal à votre propre enregistrement de base de données.
{
"file_url": "https://cdn.example.com/media/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 }
]
}
]
}| Champ | Tapez | Remarques |
|---|---|---|
| file_url | chaîne | Une entrée HTTP(S) publique, signée à durée limitée HTTP(S) ou une entrée gs:// accessible. Utilisez ceci ou entrées, jamais les deux. |
| inputs | tableau | Distribution par lots pour 1 à 25 entrées. Chacun peut transporter input_id et métadonnées. |
| outputs | tableau | 1 à 10 recettes de sortie. Chacun nécessite type ; préréglage est fortement recommandé. |
| metadata | objet | Jusqu'à 32 Ko de JSON. Persistance et écho sur meta.request_metadata. |
| moderation | objet | Premium médias visuels contrôles : sexuel, violent, dangereux. |
| watermark | objet | Superposition de médias visuels Premium. Le compte doit déjà avoir un logo PNG. |
Distribution par lots
Utilisez un lot lorsque chaque entrée nécessite le même sorties. métadonnées par entrée est fusionné dans chaque tâche enfant ; le travail parent devient votre référence de lot.
{
"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" }]
}Nouvelles tentatives sécurisées avec Idempotency-Key
Une demande expirée est ambiguë : la tâche peut avoir été mise en file d'attente et seule la réponse a été perdue. Envoyez un en-tête Idempotency-Key et réessayez en toute sécurité : la même clé renvoie la tâche d'origine au lieu de la mettre en file d'attente et de facturer une seconde tâche. Sans l'en-tête, le comportement est inchangé et chaque POST crée une nouvelle tâche.
# One key per logical job. Generate it in your client, not inside the retry loop.
KEY=$(uuidgen) # or a deterministic id you can regenerate: asset_0426:mp4_720p:v1
curl -sS -X POST "https://mediaruntime.com/v1/jobs" \
-H "X-API-Key: $MEDIARUNTIME_API_KEY" \
-H "Idempotency-Key: $KEY" \
-H "Content-Type: application/json" \
-d '{
"file_url": "https://example.com/source.mp4",
"metadata": { "asset_id": "asset_0426" },
"outputs": [{ "type": "mp4", "preset": "mp4_720p_h264_aac" }]
}'
# Timed out? Send the exact same request again with the SAME key.
# You get the original job_id back - no second job, no second charge.Règles clés
- Un UUID fonctionne ; un identifiant déterministe comme
asset_0426:mp4_720p:v1est préférable, car vous pouvez le régénérer après un crash. - Les clés sont associées à votre compte et honorées pendant 24 heures.
- La réutilisation d’une clé avec un corps différent renvoie 422 – généralement une clé réutilisée dans une boucle.
- Une nouvelle tentative envoyée alors que la première est toujours en cours renvoie 409 ; réessayez après une courte pause.
- Soumettre volontairement le même fichier deux fois ? Utilisez deux clés différentes.
Choisissez l'artefact que vous souhaitez que le moteur produise.
`type` et `preset` forment une recette exécutable. Utilisez la valeur exacte de `type` indiquée ci-dessous : un nom de `preset` seul ne change pas le type de sortie, et une combinaison incompatible peut être refusée ou mal acheminée.
{
"file_url": "https://cdn.example.com/landscape-interview.mp4",
"outputs": [{
"type": "social",
"preset": "social_vertical_blur"
}]
}social_vertical_blur crée un H.264/AAC MP4 1080×1920. La source est mise à l'échelle pour s'adapter sans recadrage ; une copie floue remplit la toile 9:16 derrière elle. Utilisez-le pour les Reels, TikTok et les Shorts. Il s'agit d'une recette Premium car la sortie verticale a une hauteur de 1920 pixels.Fichiers vidéo et affiches
| Préréglage | Envoyer | Exécution du travail | Niveau de base |
|---|---|---|---|
| mp4_720p_h264_aac (type: mp4) | Vidéo | 720p H.264/AAC MP4 avec démarrage rapide pour la lecture Web. | Standard |
| mp4_ladder_v1 (type: mp4) | Vidéo | Trois rendus MP4 à 1080p, 720p et 480p, plus une affiche pour chaque rendu. | Standard |
| transmux_mp4_fast (type: mp4) | Audio/vidéo compatible | Copie les flux existants dans un faststart MP4 sans réencodage. Si la copie échoue et que le repli est activé, le moteur ré-encode avec le 720p H.264 préréglage. | Standard |
| poster_frame_v1 (type: mp4) | Vidéo | Un JPG 720p capturé à poster_time_sec. La requête type est toujours mp4. | Standard |
| mp4_hevc_1080p (type: mp4) | Vidéo | 1080p HEVC/H.265 + AAC MP4 avec la balise hvc1 pour la lecture Apple. | Premium |
| mp4_av1_smart (type: mp4) | Vidéo | 1080p AV1 + Opus MP4 optimisés pour l'efficacité de la compression ; l'encodage est gourmand en CPU. | Premium |
| mov_prores_422 (type: mp4) | Vidéo | Maître d'édition ProRes 422 HQ + PCM MOV. Préserve les dimensions de la source et produit un gros fichier mezzanine. | Premium |
Vidéo sociale
| Préréglage | Envoyer | Exécution du travail | Niveau de base |
|---|---|---|---|
| social_vertical_blur (type: social) | Vidéo | 1080 × 1920 H.264/AAC MP4. Ajuste la source sur un arrière-plan flou de 9:16 pour les Reels, TikTok et Shorts. | Premium |
GIF animé
| Préréglage | Envoyer | Exécution du travail | Niveau de base |
|---|---|---|---|
| gif_hq (type: gif) | Vidéo | GIF animé à une largeur de 480 px et à 15 ips en utilisant la génération de palette pour une meilleure qualité de couleur. | Standard |
Extraction de trame
| Préréglage | Envoyer | Exécution du travail | Niveau de base |
|---|---|---|---|
| extract_frames_1 (type: frames) | Vidéo | Séquence d'images numérotées JPG échantillonnée à 1 image par seconde. | Standard |
| extract_frames_5 (type: frames) | Vidéo | Séquence d'images numérotées JPG échantillonnée à 5 images par seconde. | Standard |
| scene_detect_v1 (type: frames) | Voir les règles de saisie | Detects shot boundaries and exports one keyframe per shot (scene_00001.jpg…), plus scenes.txt with each boundary's timestamp and score. Frame count equals shot count — a single continuous take yields one. | Varie |
| perceptual_hash_v1 (type: frames) | Voir les règles de saisie | Fingerprints the video by sampling one frame per second and reducing each to a 64-bit dHash, written to phash.json. Compare two videos with Hamming distance to answer “is this the same content?” — it survives re-encoding and rescaling, where a checksum does not. Useful for deduplication and reupload detection. Not crop-invariant. | Varie |
Diffusion en continu
| Préréglage | Envoyer | Exécution du travail | Niveau de base |
|---|---|---|---|
| hls_ladder_v1 (type: hls) | Vidéo | Package HLS VOD avec variantes 1080p et 720p H.264/AAC, liste de lecture principale et segments de 6 secondes. | Standard |
Audio
| Préréglage | Envoyer | Exécution du travail | Niveau de base |
|---|---|---|---|
| audio_copy_fast (type: audio) | Audio ou vidéo avec audio | Copie le flux audio source sans réencodage. Si la copie échoue et que la restauration de secours est activée, le moteur écrit à la place AAC à 128 kbit/s. | Standard |
| audio_aac_128k (type: audio) | Audio ou vidéo avec audio | 128 kbps AAC dans un fichier M4A à démarrage rapide. | Standard |
| audio_mp3_128k (type: audio) | Audio ou vidéo avec audio | Fichier MP3 de 128 kbit/s. | Standard |
| audio_opus_96k (type: audio) | Audio ou vidéo avec audio | Fichier Opus de 96 kbps, bien adapté à la transmission vocale. | Standard |
| audio_loudnorm_aac_128k (type: audio) | Audio ou vidéo avec audio | Normalise vers -16 LUFS, puis écrit 128 kbps AAC. | Standard |
| audio_trim_silence_aac_128k (type: audio) | Audio ou vidéo avec audio | Supprime les silences de début et de fin, puis écrit AAC à 128 kbit/s. | Premium |
| audio_loudnorm_trim_aac_128k (type: audio) | Audio ou vidéo avec audio | Coupe le silence des limites, normalise vers -16 LUFS, puis écrit 128 kbps AAC. | Premium |
| audio_whisper_prep (type: audio) | Audio ou vidéo avec audio | PCM mono 16 kHz WAV préparé pour Whisper, ASR ou d'autres pipelines vocaux. | Premium |
Dérivés d'images
| Préréglage | Envoyer | Exécution du travail | Niveau de base |
|---|---|---|---|
| image_multi_v1 (type: image) | Images | Crée le tableau d'images que vous demandez en utilisant fit, fill, cover ou contain. Le niveau dépend du format, de la taille, du nombre, du recadrage intelligent et de la suppression de l'arrière-plan. | Standard ou Premium |
| media_report_v1 (type: image) | Voir les règles de saisie | Inspects the file without transcoding it and writes media_report.json: container and stream detail, encoder strings, GOP/keyframe structure, and EXIF including GPS coordinates converted to decimal degrees. This READS metadata rather than stripping it — image renditions still strip metadata as before. Accepts video or images; audio-only inputs are not supported. | Varie |
transmux_mp4_fast évite un encodage qui change la qualité lorsque la source est compatible avec MP4. Si la copie échoue et que le repli est activé, le moteur ré-encode avec mp4_720p_h264_aac ; utilisez ce préréglage directement lorsque vous avez besoin de caractéristiques de sortie prévisibles.Utilisez une seule source. Produisez les formats et les artefacts side-car dont votre produit a besoin.
L'extension source ne sélectionne pas la sortie. Les type et préréglage choisissent la recette exécutable, de sorte qu'une vidéo téléchargée peut devenir une vidéo de lecture, un média audio uniquement, des transcriptions, des aperçus GIF, des affiches ou des séquences d'images dans la même tâche asynchrone.
| Origine | Livrable | Recette |
|---|---|---|
| JPG, PNG ou WebP | JPG, PNG, WebP ou dérivés AVIF | image + image_multi_v1 ; choisissez les images[].format, les dimensions, mode et la qualité. |
| Vidéo | Web MP4, HLS, vidéo sociale ou maître de montage | Choisissez le mp4, hls ou social préréglage correspondant. |
| Vidéo | Séquence d'images animée GIF, affiche ou JPG | Utilisez gif_hq, poster_frame_v1, extract_frames_1/5 ou connectez gif_preview à une sortie vidéo. |
| Vidéo ou audio | M4A, MP3, Opus ou parole WAV | Choisissez l'audio_* préréglage correspondant ; la vidéo entrées voit son flux audio extrait. |
| Discours vidéo ou audio | SRT, WebVTT ou les deux | Ajoutez des sous-titres à une sortie audio ou vidéo et choisissez srt, vtt ou les deux. |
{
"file_url": "https://cdn.example.com/media/product-photo.png",
"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 }
]
}]
}{
"file_url": "https://cdn.example.com/media/interview.mov",
"outputs": [
{ "type": "mp4", "preset": "mp4_720p_h264_aac", "path_suffix": "web-video" },
{ "type": "audio", "preset": "audio_mp3_128k", "path_suffix": "audio-only" }
]
}{
"file_url": "https://cdn.example.com/media/interview.mp4",
"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
}
}]
}{
"file_url": "https://cdn.example.com/media/trailer.mp4",
"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
}
}]
}{
"file_url": "https://cdn.example.com/media/feature-film.mp4",
"metadata": { "media_type": "video", "asset_id": "stream-0426" },
"outputs": [{
"type": "hls",
"preset": "hls_ladder_v1",
"path_suffix": "stream"
}]
}{
"file_url": "https://cdn.example.com/media/clip.mp4",
"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_ladder_v1 crée une liste de lecture principale, des listes de lecture de variantes H.264/AAC 1080p et 720p et des segments multimédias de 6 secondes. Utilisez la liste de lecture principale URL signalée ou déplacez l'ensemble complet ensemble.gif_hq crée un GIF principal complet de 480 pixels, 15 ips. gif_preview ajoute un GIF plus court et explicitement chronométré à une autre sortie vidéo. Les préréglages d’images renvoient des séquences JPG numérotées à une ou cinq images par seconde.Commutateurs utiles
audio_aac_128k renvoie M4A, audio_mp3_128k renvoie MP3, audio_opus_96k renvoie Opus et audio_whisper_prep renvoie 16 kHz mono WAV. Définissez subtitles.format sur srt, vtt ou both. Pour une seule image d'affiche, envoyez type: mp4 avec preset: poster_frame_v1 et le poster_time_sec souhaité.
Commencez avec un préréglage ; ignorer uniquement ce qui compte.
Les préréglages maintiennent les requêtes lisibles et donnent au moteur une base de référence stable. Ajoutez des options de rendu explicite, de sous-titres, d'aperçu, de codec ou de débit uniquement lorsque le produit l'exige.
{
"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 }
]
}]
}{
"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"
}
}]
}{ "watermark": { "enabled": true } } ; MediaRuntime résout le logo appartenant au serveur.Ajoutez une analyse de sécurité visuelle à plusieurs niveaux au travail.
La modération utilise un pipeline en couches pour une image ou une entrée vidéo : les signaux clairs empruntent la voie rapide, tandis que les signaux incertains ou à risque plus élevé sont escaladés pour une analyse plus approfondie. Il renvoie au mieux les preuves et les décisions via le contrat actuel de rapport uniquement sans bloquer le transcodage.
{
"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"
}]
}{
"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 | Value | Notes |
|---|---|---|
| Plan | Premium | The API returns 403 unless the account is Premium or auto-upgrade is allowed. |
| Mode | report | This is the only accepted mode today. Do not send block. |
| Checks | sexual, violence, dangerous | Send one to three checks. Omitting checks selects all three. |
| Inputs | One image or video | Audio-only inputs, batches, and Sandbox moderation are rejected. |
| Video sampling | Fixed interval, bounded frames | The service chooses the interval and cap; read the actual values from result.video and result.evidence. |
| Decision | allow, review, or block signal | Report mode never blocks execution. Use result.verdict, decisions, flagged_checks, and evidence in your own policy. |
| Artifact | meta/moderation_result.json | Included in the output ZIP and exposed through meta.moderation_result.url when available. |
| Billing | Separate moderation units | Estimated and settled with the job at usage.breakdown.moderation_units. |
review comme un signal de file d'attente humaine, de suspension de publication ou d'une autre stratégie que vous contrôlez. Un rapport complet ne garantit pas que les médias sont sûrs, légaux ou conformes aux politiques.Vérifiez les octets bruts avant de faire confiance à l'événement.
Les événements de terminal sont livrés au moins une fois et la commande n'est pas garantie. Vérifiez HMAC-SHA256, rejetez les horodatages obsolètes, dédupliquez event_id, accusez réception rapidement et déplacez les travaux lourds vers une file d'attente.
{
"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"
}
}
}delivery.bundle.download.url ou récupérez meta.engine_result_url pour énumérer les chemins de sortie et d'artefact individuels.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);
});Règles de livraison
- Renvoyez les 2xx uniquement après vérification de la signature et mise en file d'attente/déduplication durable.
- Traitez event_id comme la clé de l’idempotence.
- Utilisez meta.request_metadata pour trouver votre entité sans deuxième table de recherche.
- Téléchargement conservé sorties avant delivery.expiresAt.
- Un événement FAILED ou REJECTED a error.code/message et aucun bundle utilisable.
Sondez lorsqu’un webhook n’est pas pratique.
La soumission revient immédiatement avec un job_id. Les webhooks restent le moyen avec la latence la plus faible pour apprendre qu'une tâche est terminée, mais des sondages sont disponibles pour le développement local, les environnements sans point de terminaison public, la réconciliation et les questions d'assistance.
curl -sS "https://mediaruntime.com/v1/jobs/$JOB_ID" \
-H "X-API-Key: $MEDIARUNTIME_API_KEY"# Newest first. Filter by status and page with the cursor.
curl -sS "https://mediaruntime.com/v1/jobs?status=COMPLETED&limit=25" \
-H "X-API-Key: $MEDIARUNTIME_API_KEY"
# Next page: pass the previous response's next_cursor
curl -sS "https://mediaruntime.com/v1/jobs?limit=25&cursor=$NEXT_CURSOR" \
-H "X-API-Key: $MEDIARUNTIME_API_KEY"{
"job_id": "job_2ee8db582cdf4a2fafb49d52218b3159",
"status": "COMPLETED",
"tier": {
"requested": "premium",
"required": "standard",
"effective": "premium",
"billed": "standard",
"reasons": []
},
"usage": { "units_total": 4 },
"billing": {
"status": "PAID",
"currency": "USD",
"unit_price_cents": 1,
"final_units": 4,
"final_amount_cents": 4
},
"bundle": {
"available": true,
"download_url": "https://mediaruntime.com/v1/jobs/job_2ee8.../bundle?token=...",
"expires_at": "2026-08-18T03:14:27Z",
"size_bytes": 13056793,
"retention_days": 7
},
"media": {
"format": "mov,mp4,m4a,3gp,3g2,mj2",
"duration_sec": 61.5,
"bit_rate": 8000000,
"video": {
"codec": "h264",
"profile": "High",
"width": 1080,
"height": 1920,
"encoded_width": 1920,
"encoded_height": 1080,
"fps": 29.97,
"rotation_deg": 90,
"is_rotated": true,
"orientation": "portrait"
},
"audio": { "codec": "aac", "sample_rate_hz": 48000, "channels": 2, "layout": "stereo" },
"streams": { "video": 1, "audio": 1, "other": 0 }
},
"metadata": { "asset_id": "asset_0426", "media_type": "video" },
"error": null,
"completed_at": "2026-08-11T03:14:53Z"
}requested est le niveau de la clé API qui a soumis le travail. required est ce dont le travail a réellement besoin, effective est la voie sur laquelle il s'est déroulé et billed est ce qui vous a été facturé. Une clé premium exécutant un travail standard montre requested: premium avec billed: standard — vous êtes facturé pour le travail, pas pour la clé.media rapporte ce que MediaRuntime a trouvé dans votre entrée lorsqu'il l'a sondé lors de la soumission - la même sonde qui décide si une tâche est acceptée. Lorsqu'une tâche est REJETÉE pour un appariement incompatible, cela explique pourquoi : un MP3 envoyé vers une sortie d'image affiche streams.video: 0, et une image fixe envoyée vers une sortie d'images n'a pas de duration_sec. Remarque : video.width/height sont des dimensions d'AFFICHAGE avec rotation appliquée, donc un clip de téléphone en mode portrait indique 1 080 x 1 920 même si les encoded_width/encoded_height sont en 1 920 x 1 080. Chaque champ est facultatif : un champ manquant signifie que la sonde ne l'a pas signalé, jamais zéro.GET /v1/jobs/{job_id}/moderation renvoie uniquement le verdict, afin qu'un client qui interroge l'API pour prendre une décision ne récupère pas à chaque fois les détails de facturation et du bundle. La réponse contient verdict, decision et confidence pour chaque contrôle, ainsi que les likelihoods d'escalade. Un contrôle marqué review_only est consultatif : il peut produire un verdict review, mais ne peut pas bloquer seul. L'endpoint renvoie **404 lorsque la tâche existe mais que la modération n'a jamais été demandée** ; une réponse vide réussie serait impossible à distinguer d'une tâche modérée sans résultat. Les seuils de décision ne sont pas publiés.GET /v1/jobs/{job_id}/media-report renvoie le document media_report_v1 sans télécharger le bundle. report le transporte en ligne ; un rapport inhabituellement volumineux n'est pas stocké en ligne et la réponse définit ensuite report sur null avec un download_url qui est toujours résolu, donc gérez les deux. Renvoie 404 lorsque le travail ne contient aucun rapport.| Champ | Tapez | Remarques |
|---|---|---|
| statut | chaîne | QUEUED, TRAITEMENT, COMPLETED, FAILED ou REJETÉ. |
| niveau | objet | demandé / requis / effectif / facturé, ainsi que les raisons pour lesquelles la prime était requise. |
| utilisation.units_total | entier | Unités facturables pour le travail. |
| facturation | objet | Devise, prix unitaire, unités et montants estimés par rapport aux unités finales. |
| bundle.download_url | chaîne | Offre groupée URL expirant et limitée à la tâche. Point de terminaison à tâche unique uniquement. |
| médias | objet | Quelle était réellement la contribution, comme sondé lors de la soumission. Nul sur les emplois plus anciens. |
| media.video.largeur/hauteur | entier | Afficher les dimensions, rotation déjà appliquée. |
| médias.duration_sec | numéro | Absent pour les images fixes, qui n’ont pas de chronologie. |
| metadata | objet | L'objet métadonnées que vous avez soumis a été renvoyé. |
| erreur | chaîne | Rempli sur FAILED ou REJETÉ ; nul sinon. |
Règles de sondage
- Préférez les webhooks ; sondage uniquement lorsque vous ne pouvez pas en recevoir un.
- Page avec next_cursor, jamais de décalage : les lignes se déplacent à mesure que les tâches sont mises à jour.
- Un identifiant de travail que vous ne possédez pas renvoie 404, le même qu'un identifiant qui n'existe pas.
- Les lignes de la liste omettent le bundle URL ; récupérez le travail unique à télécharger.
- Reculez entre les sondages. Le statut d'un terminal ne changera pas.
Prépayé, payé au fur et à mesure et réglé à partir de l'utilisation réelle.
Ajoutez une carte, approvisionnez le portefeuille et soumettez votre travail sans abonnement récurrent. MediaRuntime réserve une estimation avant l'exécution et règle les frais finaux lorsque le travail atteint un état terminal.
| Planifier | Prix d'utilisation de départ | Recharge minimale | Recharge automatique par défaut |
|---|---|---|---|
| Standard Pay-As-You-Go | De $0.02 | $20.00 | $20.00 à $2.00 disponible |
| Premium Pay-As-You-Go | De $0.05 | $60.00 | $60.00 à $5.00 disponible |
billing et usage pour le rapprochement.Règles du portefeuille
- Le crédit disponible est égal au crédit du portefeuille moins les fonds réservés à l'exécution des tâches.
- Un crédit disponible insuffisant renvoie HTTP 402 avant exécution.
- Le rechargement automatique est facultatif et nécessite une carte enregistrée.
- Une requête Premium uniquement renvoie 403 lorsque la mise à niveau n'est pas autorisée.
Réessayez les échecs de transport, pas le travail invalide.
La plupart des erreurs API utilisent un champ de détail de niveau supérieur. Il peut s'agir d'une chaîne ou d'un objet structuré, alors enregistrez l'intégralité de la réponse avec votre ID de corrélation, mais n'enregistrez jamais la clé API.
| Statut | Signification | Ce que votre intégration devrait faire |
|---|---|---|
| 400 | La demande est logiquement invalide ou l'estimateur l'a rejetée. | Corrigez la demande ; ne réessayez pas inchangé. |
| 401 | La clé API n'est pas valide, a expiré ou est révoquée. | Corrigez ou faites pivoter la clé ; ne réessayez pas aveuglément. |
| 402 | Le compte, le portefeuille ou le contrôle préalable de facturation ne peuvent pas couvrir le travail. | Financez le portefeuille ou résolvez d’abord la facturation. |
| 403 | Le plan, le rôle ou la porte de fonctionnalité n'autorise pas la demande. | Changer le plan/la demande ; ne réessayez pas inchangé. |
| 413 | Le corps de la requête HTTP dépasse 2 Mio. | Téléchargez les médias séparément et envoyez uniquement les URL. |
| 422 | Le JSON ne correspond pas au schéma API. | Corrigez le champ nommé. |
| 429 | Le compte ou la clé est à débit limité. | Réessayez avec un recul exponentiel et une gigue. |
| 500/502/503 | Une dépendance transitoire de plate-forme a échoué ou une voie est mise en pause. | Réessayez en toute sécurité avec backoff ; préservez votre corrélation métadonnées. |
{
"detail": "Insufficient wallet balance for this job"
}La petite surface dont la plupart des intégrations ont besoin.
Tous les points de terminaison de serveur à serveur ci-dessous utilisent X-API-Key. Le bundle tokenisé URL est la seule exception car il comporte son propre identifiant de courte durée, limité à l'emploi.
| Méthode | Chemin | Objectif |
|---|---|---|
| POSTER | /v1/upload-url | Créez éventuellement une cible de téléchargement de 15 minutes lorsque vous ne disposez pas déjà d'un média récupérable URL. |
| POSTER | /v1/jobs | Mettez en file d'attente une tâche multimédia à entrée unique ou par lots. |
| OBTENIR | /v1/jobs/{job_id} | Statut, décision de niveau, utilisation, facturation et lien groupé pour une tâche. |
| OBTENIR | /v1/jobs | Listez vos emplois, les plus récents en premier. Prend en charge ?status= et la pagination du curseur. |
| OBTENIR | /v1/jobs/{job_id}/moderation | Verdict de modération d'une tâche. Renvoie 404 lorsque la modération n'a pas été demandée. |
| OBTENIR | /v1/jobs/{job_id}/media-report | Rapport médico-légal pour un travail. 404 lorsque media_report_v1 n’a pas été demandé. |
| OBTENIR | /v1/jobs/{job_id}/bundle?token=... | Échangez le jeton limité à la tâche contre un ensemble ; aucune clé API n’est requise. |
| POSTER | /v1/jobs/{job_id}/retry-webhook | Réessayez le webhook du terminal pour une tâche que vous possédez. |
| POSTER | /v1/account/watermark-logo/upload-url | Créez une cible de téléchargement pour le logo du compte PNG. |
| POSTER | /v1/account/watermark-logo/confirm | Confirmez le logo et ses paramètres de placement. |