Images, vidéo, musique et parole
La clé que vous utilisez pour le texte permet aussi de créer des images, des vidéos, de la musique et de la parole, et de transformer des enregistrements en texte, à partir du même solde. Choisissez un modèle dans le tableau des modèles (ou ?type=image, ?type=music, ?type=audio) ; la page de chaque modèle indique son prix, son type et une requête fonctionnelle.
| Vous voulez | Route | L'image source va dans |
|---|---|---|
| Une nouvelle image à partir d'un prompt | POST /v1/images/generations |
(aucune) |
| Une image modifiée | POST /v1/images/edits |
image (ou image_url, input_images, images) |
| Une vidéo à partir d'un prompt | POST /v1/video/generations |
(aucune) |
| Une vidéo qui commence par une image | POST /v1/video/generations |
image_url (première image) ; end_image_url pour la dernière image |
| Une vidéo à partir d'images de référence | POST /v1/video/generations |
input_images avec "role": "reference" |
| Une réponse textuelle à propos d'une image | POST /v1/chat/completions |
une partie image_url dans messages[].content |
| Musique | POST /v1/music/generations |
(aucune) |
| Parole à partir de texte | POST /v1/audio/speech |
(aucune) |
| Texte à partir d'un enregistrement | POST /v1/audio/transcriptions |
(aucune ; l'enregistrement va dans file) |
Une image source est un lien https://, un URI data: (data:image/png;base64,iVBORw0…) ou une base64 simple d'un PNG, JPEG ou WebP, jusqu'à 8 par requête. Les routes d'image acceptent également un téléversement multipart (ci-dessous). Les liens doivent être des liens https:// publics ; les liens http:// sont refusés. Un lien qui redirige (par exemple https://picsum.photos/640/360) est suivi, jusqu'à 5 fois et uniquement vers d'autres liens https:// publics, et le modèle reçoit le lien final. Un lien qui ne mène pas à une image, ou qui redirige vers un endroit que nous ne pouvons pas utiliser, est refusé avec 400 invalid_image_url avant que quoi que ce soit ne soit retenu ou facturé. Les images envoyées en ligne augmentent la taille de la requête : gardez-la en dessous de 10 Mo, ou envoyez des liens.
Dans un message de chat (image_url dans messages[].content), un lien (https:// ou http://) est ouvert par UberLLM et envoyé au modèle comme une URL data:, car la plupart des modèles de vision n'acceptent que cela. Chaque image peut peser jusqu'à 20 Mo (40 Mo par requête), et les redirections sont suivies jusqu'à 5 fois, uniquement vers des adresses publiques. Les modèles Gemini reçoivent le lien tel quel. Un lien qui ne mène pas à une image est refusé avec 400 invalid_image_url, et rien n'est facturé.
Faire une image
Les images reviennent dans la réponse.
curl https://api.uberllm.dev/v1/images/generations \
-H "Authorization: Bearer $UBERLLM_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "venice/z-image-turbo", "prompt": "a red apple on a white table", "n": 1, "size": "1024x1024"}'
{ "created": 1792429247, "data": [{ "url": "https://api.uberllm.dev/v1/media/artifacts/<id>/0" }],
"usage": { "cost_usd": 0.0099, "max_cost_usd": 0.0109 } }
usage.cost_usd est ce que l'image a coûté (également dans l'en-tête X-UberLLM-Cost-USD). Dans la rare réponse où le prix n'est pas encore connu, c'est null. Vous payez ce que le fournisseur a facturé, jamais plus que max_cost_usd, ce qui a été retenu pour cela.
response_format est url par défaut, pour /v1/images/generations et /v1/images/edits. Envoyez "response_format": "b64_json" pour obtenir l'image à l'intérieur de la réponse à la place. Lorsque le modèle renvoie l'image elle-même, UberLLM la conserve comme un fichier de votre compte pendant 24 heures et répond avec son lien (https://api.uberllm.dev/v1/files/file_…/content), qui fonctionne également comme image source pour une édition ultérieure. Téléchargez une URL avec la même clé :
curl -L -o image.png -H "Authorization: Bearer $UBERLLM_API_KEY" \
"https://api.uberllm.dev/v1/media/artifacts/<id>/0"
n est 1 à 10.
Modifier une image
Utilisez un modèle qui modifie (son identifiant se termine généralement par -edit) et envoyez l'image source dans image :
curl https://api.uberllm.dev/v1/images/edits \
-H "Authorization: Bearer $UBERLLM_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "google/nano-banana-pro-edit", "prompt": "make the sky a sunset",
"image": "https://example.com/skyline.png"}'
La même image peut être fournie comme suit :
"image": "data:image/png;base64,iVBORw0…""image_url": "https://…""input_images": ["https://…", "https://…"]: la première est l'image à modifier, les autres sont des références. Une entrée peut aussi être{"url": "…", "role": "reference"}."images": [{"image_url": "https://…"}], le format envoyé par les SDK OpenAI
Le base64 simple fonctionne aussi, et "input_image" est lu comme "image".
Ou téléchargez le fichier, comme avec l'édition d'image d'OpenAI :
curl https://api.uberllm.dev/v1/images/edits \
-H "Authorization: Bearer $UBERLLM_API_KEY" \
-F model=google/nano-banana-pro-edit -F prompt="make the sky a sunset" \
-F [email protected]
Une modification sans image source est refusée avec 400 image_required. Les masques
(inpainting) ne sont pas pris en charge.
Téléchargez une image une seule fois
Téléchargez une image vers /v1/files et utilisez-la dans autant de requêtes que vous le souhaitez pendant 24 heures, au lieu de l'envoyer à chaque requête :
curl https://api.uberllm.dev/v1/files \
-H "Authorization: Bearer $UBERLLM_API_KEY" \
-F [email protected]
{ "id": "file_3f9c…", "object": "file", "bytes": 482113, "content_type": "image/png",
"filename": "skyline.png", "created_at": 1792540800, "expires_at": 1792627200,
"url": "https://api.uberllm.dev/v1/files/file_3f9c…/content" }
Ensuite, placez l'ID, ou le url, partout où une image source est attendue : "image": "file_3f9c…",
"image": {"file_id": "file_3f9c…"}, "image_url": "file_3f9c…", une entrée de
input_images, ou l'image de début d'une vidéo. Seul votre compte peut utiliser ses fichiers.
- PNG, JPEG, WebP, GIF, HEIC ou AVIF, jusqu'à 10 Mo chacun, au maximum 500 conservés par compte à la fois.
- Conservés pendant 24 heures, puis supprimés.
GET /v1/files/{id}en décrit un,GET /v1/files/{id}/contentle télécharge,DELETE /v1/files/{id}le supprime maintenant.
Créer une vidéo
Une vidéo prend une minute ou plus, c'est donc un travail : la démarrer, la vérifier, la télécharger.
1. La démarrer
curl https://api.uberllm.dev/v1/video/generations \
-H "Authorization: Bearer $UBERLLM_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"model": "fal-ai/vidu/q3/text-to-video/turbo", "prompt": "a red ball bouncing on green grass",
"duration_seconds": 5, "models": ["fal-ai/kling-video/v3/standard/text-to-video"]}'
models est facultatif : les modèles de secours sont essayés dans l'ordre uniquement lorsque le premier n'a pas de fournisseur disponible actuellement, afin qu'un moment d'affluence ne fasse pas échouer votre requête (voir Modèles de secours). La vidéo ci-dessus prend environ une minute et coûte environ $0.33.
Pour partir d'une image, utilisez un modèle image-vers-vidéo et ajoutez image_url :
-d '{"model": "bytedance/seedance-2-0-image-to-video", "prompt": "slow push-in as the waves roll",
"image_url": "https://example.com/first-frame.png", "duration_seconds": 5}'
Un modèle qui mélange deux images prend également end_image_url (la dernière image). Un modèle image-vers-vidéo ou référence-vers-vidéo qui ne peut pas créer de vidéo à partir de texte seul est refusé avec 400 image_required lorsque la requête n'a pas d'image, avant que quoi que ce soit ne soit facturé. Un modèle référence-vers-vidéo prend les images dans input_images :
-d '{"model": "pixverse/pixverse-c1-reference-to-video", "prompt": "the cat walks across the room",
"input_images": [{"url": "https://example.com/cat.png", "role": "reference"}],
"aspect_ratio": "16:9", "duration_seconds": 5}'
Autres champs, lorsque le modèle les propose : resolution (480p, 720p, 1080p…), aspect_ratio (16:9, 9:16, 1:1…) et audio (true / false ; un modèle sans réglage de son ne le reçoit pas et crée le clip comme il le fait toujours). Un modèle référence-vers-vidéo a besoin d'une forme, donc lorsque vous omettez aspect_ratio, il est envoyé comme 16:9 (ou la première forme du modèle s'il ne fait pas 16:9) ; le schéma du modèle montre cette valeur par défaut. Les choix de chaque modèle sont listés par GET /v1/models/<model id> (voir Options de chaque modèle).
Chaque modèle ne produit que certaines durées de clip, résolutions et formes. Une requête pour une qu'il ne produit pas est refusée avec un message listant celles qu'il produit, avant que quoi que ce soit ne soit facturé.
HTTP 202:
{
"id": "6f1c…",
"object": "media.job",
"kind": "video",
"status": "queued",
"model": "fal-ai/vidu/q3/text-to-video/turbo",
"estimated_cost_usd": 0.32725,
"estimated_cost_usdc": "327250",
"max_cost_usd": 0.359975,
"max_cost_usdc": "359975",
"poll_url": "/v1/video/generations/6f1c…",
"cancel_url": "/v1/video/generations/6f1c…"
}
model est le modèle qui a pris le travail (un modèle de secours de models lorsque le premier n'a pas pu). Les coûts se terminant par _usdc sont en millionièmes de dollar (327250 = $0.33). estimated_cost_usd est le prix affiché de ce que vous avez demandé : le même nombre validate et POST /v1/quote répondent comme cost_usd. C'est le prix actuel du modèle multiplié par les secondes (à la résolution demandée), les images ou d'autres unités. Le travail est facturé ce que le fournisseur a facturé pour cela, ce qui peut être plus ou moins. max_cost_usd est ce qui est retenu de votre solde pour le travail : le même chiffre pendant qu'il s'exécute et après qu'il se termine, et le maximum que le travail est facturé. Un modèle vidéo dont le prix est par second est facturé par seconde du clip ; un modèle dont le prix est par job coûte cela par vidéo.
2. La vérifier toutes les 5–10 secondes :
curl -H "Authorization: Bearer $UBERLLM_API_KEY" \
https://api.uberllm.dev/v1/video/generations/<job id>
HTTP 202 pendant que le travail s'exécute, HTTP 200 une fois qu'il se termine :
{
"id": "6f1c…",
"status": "succeeded",
"results": [{ "artifact_index": 0, "url": "https://api.uberllm.dev/v1/media/artifacts/6f1c…/0",
"content_type": "video/mp4", "duration_seconds": 5 }],
"usage": { "cost_usdc": "40000", "cost_usd": 0.04 }
}
Le modèle signale le coût exact d'un travail un peu après que le fichier soit prêt, généralement dans les 30 secondes. Jusque-là, un travail succeeded affiche "usage": { "cost_usd": null, "cost_pending": true } ; demandez à nouveau quelques secondes plus tard. Si le coût n'est pas arrivé dans les 30 minutes, le travail est facturé à son prix affiché (estimated_cost_usd) et usage.cost_note l'indique.
3. La télécharger
curl -L -o video.mp4 -H "Authorization: Bearer $UBERLLM_API_KEY" \
https://api.uberllm.dev/v1/media/artifacts/<job id>/0
Demandez d'arrêter un travail dont vous n'avez plus besoin : DELETE /v1/video/generations/<job id>. Nous demandons au fournisseur de l'arrêter ; un travail qu'il avait déjà effectué est facturé comme d'habitude, et un qu'il arrête se termine canceled et ne coûte rien.
Modèles vidéo Fal
Les modèles dont l'ID commence par fal-ai/ fonctionnent sur Fal, en dessous du prix de Fal :
vous payez le prix de Fal moins la réduction du vendeur Fal le moins cher sur la
marketplace en ce moment (15% de réduction sur 30 octobre 2026 ; 25% de réduction chaque fois que ce vendeur vend).
Le prix est fixé par ce que vous demandez — secondes × le taux pour la
résolution, et si le clip a du son — donc le coût du travail est connu au moment où il
commence et estimated_cost_usd est exactement ce qui est facturé. Un travail échoué
ne coûte rien. L'arrêter est un effort optimal : si Fal a déjà créé le clip,
il est facturé.
curl https://api.uberllm.dev/v1/video/generations \
-H "Authorization: Bearer $UBERLLM_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "fal-ai/wan/v2.7/text-to-video", "prompt": "a woman twirls at a garden party at night",
"duration_seconds": 15, "resolution": "720p", "aspect_ratio": "9:16"}'
Prix de Fal par seconde (le vôtre est celui-ci moins la réduction en direct ; la page de chaque modèle,
GET /v1/models et POST /v1/quote, affiche le prix exact maintenant) :
| Modèle | Durées (s) | Prix de Fal par seconde, à partir de |
|---|---|---|
fal-ai/wan/v2.7/text-to-video, …/image-to-video |
2–15 | 720p $0.10 · 1080p $0.15 |
fal-ai/kling-video/v3/standard/text-to-video, …/image-to-video |
3–15 | $0.084 · avec son $0.126 |
fal-ai/kling-video/v3/pro/text-to-video, …/image-to-video |
3–15 | $0.112 · avec son $0.168 |
fal-ai/kling-video/o3/standard/text-to-video, …/image-to-video |
3–15 | $0.084 · avec son $0.112 |
fal-ai/kling-video/v2.6/pro/text-to-video, …/image-to-video |
5, 10 | $0.07 · avec son $0.14 |
fal-ai/veo3.1/fast, fal-ai/veo3.1/fast/image-to-video |
4, 6, 8 | 720p/1080p $0.10 · avec son $0.15 · 4k $0.30 |
fal-ai/veo3.1, fal-ai/veo3.1/image-to-video |
4, 6, 8 | 720p/1080p $0.20 · avec son $0.40 · 4k $0.40 |
fal-ai/veo3.1/lite |
4, 6, 8 | 720p $0.03 · avec son $0.05 · 1080p $0.05 |
fal-ai/ltx-2.3/text-to-video/fast, …/image-to-video/fast |
6, 8, 10 | 1080p $0.06 · 1440p $0.12 · 2160p $0.24 |
fal-ai/ltx-2.3/text-to-video |
6, 8, 10 | 1080p $0.08 |
fal-ai/minimax/hailuo-02/standard/text-to-video, …/image-to-video |
6, 10 | 768P $0.045 · 512P (image) $0.017 |
fal-ai/vidu/q3/text-to-video/turbo, …/image-to-video/turbo |
1–16 | 360p/540p $0.035 · 720p/1080p $0.077 |
fal-ai/vidu/q3/text-to-video, …/image-to-video |
1–16 | 360p/540p $0.07 · 720p/1080p $0.154 |
Chaque prix est celui de Fal ; la réduction est appliquée au début du travail. Si le
vendeur le moins cher refuse un travail, un plus cher le réalise et le travail est facturé au
prix de ce vendeur, jamais plus de estimated_cost_usd sur la réponse.
Certains modèles nous coûtent moins cher que ce qu'indique la réduction du vendeur. Toutes les 6 heures, nous
comparons ce que la marketplace nous a facturé pour chaque modèle au cours des 7 derniers jours
avec le prix de Fal pour les mêmes travaux. Un modèle avec au moins 20 travaux pendant cette période
est alors tarifé à cette part réelle plus 5%, lorsque c'est inférieur. La page du modèle,
/v1/models et la facture utilisent toutes le même prix. Si ce chiffre a plus de
3 jours, la réduction du vendeur s'applique à nouveau.
Les champs sont les nôtres : duration_seconds, resolution, aspect_ratio, audio
et image_url (ou input_images). L'orthographe de Fal (duration,
generate_audio, start_image_url) fonctionne aussi. Kling, Veo et Kling 2.6 ajoutent du son
sauf si vous envoyez "audio": false.
État du travail
status |
HTTP | Signification |
|---|---|---|
queued |
202 | Accepté, en attente qu'un modèle le démarre |
running |
202 | En cours de création ; progress passe de 0 à 1 lorsque le modèle le signale |
succeeded |
200 | Terminé ; téléchargez results[].url |
failed |
200 | Non créé. error.message indique pourquoi ; error.retryable indique si une nouvelle tentative peut fonctionner |
canceled |
200 | Arrêté avant sa création |
expired |
200 | Non terminé à temps |
Ces six sont les seules valeurs. failed, canceled et expired ne coûtent rien. Une
DELETE est une demande d'arrêt, pas une promesse : un travail que le fournisseur termine quand même
se termine succeeded et est facturé.
En cas de forte affluence, nous relançons pour vous. Quand aucun fournisseur n'est libre pour créer votre vidéo,
ou qu'un fournisseur est trop occupé en ce moment, UberLLM relance le travail tout seul, jusqu'à deux fois de plus
dans les 10 minutes. Il conserve le même job id et le même coût maximal. Le
travail prend juste un peu plus de temps. Vous ne voyez failed que lorsque toutes les tentatives ont échoué, et un
travail échoué ne coûte rien.
Attente. Envoyez callback_url et nous vous dirons quand le travail se termine (voir
Callbacks), ou interrogez la poll URL toutes les 5–10 secondes. La plupart des vidéos prennent
1–5 minutes ; certains travaux longs ou 4K prennent plus de temps. Vous n'avez pas besoin de continuer à demander
le règlement de la facture : un travail que personne ne vérifie est quand même terminé et facturé (ou
remboursé) par UberLLM, et apparaît dans
Dashboard → Activity.
Callbacks
Ajoutez callback_url à une tâche vidéo ou musicale : un lien https:// sur un hôte public.
Quand la tâche est définitivement terminée, UberLLM lui envoie un POST avec le même JSON
que GET renvoie pour la tâche.
{ "model": "kling/kling-o3", "prompt": "a red ball bouncing", "duration_seconds": 5,
"callback_url": "https://your-app.example/uberllm/jobs" }
- Quand. Une seule fois, quand la tâche est terminée et que sa facture est finale :
succeededavecusage.cost_usddéfini (jamaiscost_pending), oufailed,canceledouexpired(coût 0). Il n'est pas renvoyé quand le coût arrive, car il n'est envoyé qu'après. Avecoutput_upload_urlaussi, le callback arrive après que l'upload est terminé, doncoutput_upload.statusest déjàdeliveredoufailed. - Headers.
Content-Type: application/json,UberLLM-Event: job.succeeded(oujob.failed,job.canceled,job.expired),UberLLM-Event-Id: <job id>:<status>etUberLLM-Signature: t=<unix seconds>,v1=<hex>. - Retries. Répondez avec n'importe quel 2xx dans les 15 secondes. Toute autre réponse, ou l'absence de réponse, est réessayée après 30 secondes, 2 minutes, 10 minutes, 30 minutes et 2 heures, puis abandonnée. Vous pouvez toujours interroger la tâche à tout moment.
- Repeats. Un message peut arriver plus d'une fois (par exemple quand votre réponse est perdue). L'identifiant d'événement reste le même, donc ignorez ceux que vous avez déjà traités.
- Status. La tâche indique comment elle s'est déroulée :
"callback": { "status": "delivered", "attempts": 1, "http_status": 200, "delivered_at": 1792540990 }.statusestpending,deliveredoufailed(avecerroren toutes lettres). - Le lien doit être
https://sur un hôte public, sans nom d'utilisateur ni mot de passe. Il n'est jamais affiché à nouveau et est supprimé une fois le callback terminé.webhook_urlest lu comme le même champ. Les images et la parole ne le prennent pas : elles reviennent dans la réponse elle-même.
Vérifier la signature
Chaque message est signé avec le secret de webhook de votre compte. Trouvez-le dans Tableau de bord → Clés API → Secret de signature de webhook ; il est créé la première fois que vous l'ouvrez, et Remplacer le secret le change immédiatement.
v1 est le HMAC-SHA256 hexadécimal de <t>.<raw body>, chiffré avec le secret complet
(whsec_…). Calculez-le sur les octets bruts que vous avez reçus (avant d'analyser le JSON),
comparez en temps constant, et refusez un t de plus de 5 minutes.
Node.js (Express) :
import crypto from "node:crypto";
import express from "express";
const app = express();
const SECRET = process.env.UBERLLM_WEBHOOK_SECRET; // whsec_…
app.post("/uberllm/jobs", express.raw({ type: "application/json" }), (req, res) => {
const header = req.get("UberLLM-Signature") || "";
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=", 2)));
const expected = crypto.createHmac("sha256", SECRET)
.update(`${parts.t}.`).update(req.body).digest("hex");
const ok = parts.v1 && parts.v1.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(parts.v1), Buffer.from(expected)) &&
Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
if (!ok) return res.status(400).send("bad signature");
const job = JSON.parse(req.body);
// Drop a repeat: req.get("UberLLM-Event-Id") is the same each time.
console.log(job.id, job.status, job.usage?.cost_usd);
res.sendStatus(204);
});
Python (Flask) :
import hashlib, hmac, os, time
from flask import Flask, request, abort
app = Flask(__name__)
SECRET = os.environ["UBERLLM_WEBHOOK_SECRET"].encode() # whsec_…
@app.post("/uberllm/jobs")
def uberllm_job():
raw = request.get_data() # the raw bytes, before any JSON parsing
parts = dict(p.split("=", 1) for p in request.headers.get("UberLLM-Signature", "").split(",") if "=" in p)
expected = hmac.new(SECRET, parts.get("t", "").encode() + b"." + raw, hashlib.sha256).hexdigest()
if not hmac.compare_digest(parts.get("v1", ""), expected) or abs(time.time() - int(parts.get("t", "0"))) > 300:
abort(400)
job = request.get_json()
# Drop a repeat: request.headers["UberLLM-Event-Id"] is the same each time.
print(job["id"], job["status"], job.get("usage", {}).get("cost_usd"))
return "", 204
Alertes vers un webhook
Les e-mails d'alerte peuvent aussi être envoyés à un webhook : définissez un lien sous
Tableau de bord → Crédits → Pour les développeurs : envoyer aussi les alertes à un webhook,
ou PUT /api/v1/billing/alerts {"alerts_webhook_url": "https://…"} (null le désactive).
Les messages sont signés, réessayés et identifiés de la même manière que les callbacks. Le corps est :
{ "id": "evt_…", "object": "event", "type": "balance.low", "created": 1792540990,
"data": { "balance_usd": 4.2, "threshold_usd": 5, "message": "Your balance is $4.20, below the $5.00 you set. Requests stop once it runs out.",
"add_credits_url": "https://uberllm.dev/dashboard/credits" } }
balance.low: le solde est tombé en dessous du montant que vous avez défini pour l'e-mail de solde faible. Envoyé une fois chaque fois qu'il tombe en dessous ; rien n'est envoyé tant que vous n'avez pas défini un montant.hold.not_released: l'argent mis de côté pour une tâche était toujours retenu plus de 15 minutes après la fin de la tâche.data.jobsles liste (id,kind,model,ended_at,held_usd) etdata.held_usdest le total. L'argent est toujours le vôtre et notre équipe a été informée.
Combien de temps les fichiers sont conservés
Téléchargez un résultat dès qu'il est créé :
- Les fichiers vidéo et musicaux sont conservés pendant environ 3 heures après la fin de la tâche.
- Les liens d'image vers
/v1/media/artifacts/…sont conservés pendant environ 15 minutes ; les liens d'image vers/v1/files/…pendant 24 heures. Demandez"response_format": "b64_json"pour obtenir l'image directement dans la réponse et la conserver vous-même. - Les images que vous téléchargez sur
/v1/filessont conservées pendant 24 heures.
Après cela, le téléchargement répond 404 artifact_expired.
Envoyer les résultats vers votre propre stockage
Ajoutez output_upload_url à une tâche vidéo ou musicale : un lien https:// qui accepte un HTTP PUT, tel qu'un lien de téléchargement S3, R2 pré-signé ou Google Cloud Storage. Lorsque la tâche réussit, UberLLM télécharge le fichier à cet endroit avec son content type. Pour une tâche qui génère plusieurs fichiers, envoyez output_upload_urls, un lien par fichier dans l'ordre (au maximum 4).
{ "model": "kling/kling-o3", "prompt": "a red ball bouncing", "duration_seconds": 5,
"output_upload_url": "https://my-bucket.s3.amazonaws.com/ball.mp4?X-Amz-Signature=…" }
Le statut de la tâche contient alors output_upload :
"output_upload": { "status": "delivered", "attempts": 1, "delivered_at": 1792540990,
"files": [{ "artifact_index": 0, "http_status": 200, "bytes": 5242880 }] }
statusestpendingjusqu'à ce que le téléchargement soit terminé, puisdeliveredoufailed(avecerroren clair).- Un téléchargement échoué est réessayé après 30 secondes, 2, 10 et 30 minutes, puis marqué
failed; le fichier peut toujours être téléchargé depuisresults[].urltant qu'il est conservé. Rendez le lien valide pendant au moins une heure. - Le lien doit être
https://sur un hôte public, sans nom d'utilisateur ni mot de passe. Il n'est jamais affiché en retour et il est supprimé une fois le téléchargement terminé. - Les images ne le prennent pas en charge : une image revient dans la réponse elle-même.
Musique
Les mêmes trois étapes à /v1/music/generations. curl -OJ enregistre la piste sous le nom de fichier que nous envoyons.
Parole
Du texte entre, un fichier audio revient en réponse.
curl https://api.uberllm.dev/v1/audio/speech \
-H "Authorization: Bearer $UBERLLM_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "x-ai/tts-xai-v1", "input": "Hello from UberLLM."}' \
-o speech.mp3
Vous pouvez aussi envoyer voice, response_format (par exemple mp3 ou wav) et speed.
Chaque modèle a ses propres voix (GET /v1/models/{id} les liste). Avec
best-price:speech ou fastest:speech, vous ne savez pas à l'avance quel modèle est utilisé,
donc la voix est adaptée à celui-ci : une voix que le modèle possède est conservée, les noms d'OpenAI sont mappés à la
voix correspondante du modèle s'il en a une (alloy → af_alloy), et toute autre voix, ou
aucune, obtient la première voix du modèle. Lorsque vous nommez un modèle, votre voix est envoyée telle quelle.
C'est facturé par tranche de 1K caractères de input commencée. La réponse est le fichier audio ; son
en-tête X-UberLLM-Cost-USD indique ce qu'il a coûté, et X-Request-Id est l'ID de la requête
telle qu'elle est listée dans l'Activité. "validate": true, max_cost, metadata, models et
provider.max_price fonctionnent comme sur les images (voir Ce que vous payez).
Transformer un enregistrement en texte
Envoyez l'enregistrement sous forme de formulaire avec file et model :
curl https://api.uberllm.dev/v1/audio/transcriptions \
-H "Authorization: Bearer $UBERLLM_API_KEY" \
-F model=venice/whisper-large-v3 \
-F [email protected]
{ "text": "Hello from UberLLM.", "duration": 4.2,
"usage": { "type": "duration", "seconds": 5, "cost_usd": 0.00055, "max_cost_usd": 0.00726 } }
Voici le coût : usage.cost_usd (également dans l'en-tête X-UberLLM-Cost-USD, le seul
endroit pour les réponses text, srt et vtt). Nos propres champs sont également inclus dans le formulaire et ne sont
jamais envoyés au modèle : -F max_cost=0.10, -F "metadata[project]=podcast",
-F validate=true.
Vous pouvez également envoyer language, prompt, temperature et response_format : json
(par défaut), ou text, srt ou vtt pour obtenir du texte brut en retour. C'est facturé à la minute
d'audio. Les fichiers peuvent peser jusqu'à 40 Mo. Le format (mp3, wav, ogg, flac, m4a/mp4,
webm, aac) est lu à partir de l'enregistrement lui-même, et non de son nom de fichier, donc un téléchargement
nommé tmp123.out fonctionne.
Avec le OpenAI SDK
from openai import OpenAI
client = OpenAI(base_url="https://api.uberllm.dev/v1", api_key="<your UberLLM key>")
speech = client.audio.speech.create(model="x-ai/tts-xai-v1", voice="eve", input="Hello from UberLLM.")
speech.write_to_file("speech.mp3")
with open("speech.mp3", "rb") as f:
text = client.audio.transcriptions.create(model="venice/whisper-large-v3", file=f)
print(text.text)
Ce que vous payez
- Le prix de ce qui a été produit : ce que le fournisseur a facturé, sans frais UberLLM. Une réponse d'image ou de transcription et un travail terminé le portent comme
usage.cost_usd(dollars US) ; chaque réponse qui a produit quelque chose le porte également dans l'en-tête de réponseX-UberLLM-Cost-USD(pour la parole, l'en-tête est le seul endroit, puisque la réponse est le fichier audio). Il apparaît dans Tableau de bord → Activité. Le reste de la retenue est remboursé. - Rien pour un travail qui
failed, a étécanceledouexpired, ou pour une demande d'image, de parole ou de transcription qui échoue. - Le vendeur le moins cher qui produit exactement ce que vous avez demandé. De nombreux modèles d'images et de vidéos ont plus d'un vendeur ; chaque demande est envoyée au moins cher qui peut la produire avec vos paramètres, et à un autre si celui-ci ne peut pas la prendre. La liste des modèles et chaque page de modèle affichent le prix chez le vendeur le moins cher actuellement en ligne.
- Voir votre solde et ce qui est retenu avec
GET /v1/balance(ci-dessous).
Vérifier le prix avant de dépenser
Ajoutez "validate": true à toute demande d'image, de vidéo, de musique ou de parole (pour une transcription, envoyez validate=true comme champ de formulaire avec l'enregistrement). La demande est vérifiée comme si elle était réelle (modèle, options, images source), rien n'est produit et rien n'est facturé, et HTTP 200 indique ce qu'elle coûte au prix affiché (cost_usd, le prix indiqué sur la page du modèle ; vous payez ce que le fournisseur a facturé, ce qui peut différer) et ce qui est retenu pendant son exécution (max_cost_usd, le montant maximum facturé) :
{ "object": "quote", "model": "bytedance/seedance-2-0-image-to-video", "kind": "video",
"valid": true, "cost_usd": 0.2117, "max_cost_usd": 0.4235, "unit": "second", "units": 5, "duration_seconds": 5,
"note": "Nothing was charged. …" }
Si le vendeur du modèle a besoin d'un paramètre que vous avez omis (un modèle de référence à la vidéo a besoin de aspect_ratio), la valeur par défaut avec laquelle il est envoyé est affichée sur le schéma du modèle et dans le devis comme defaults_applied, par exemple "defaults_applied": {"aspect_ratio": "16:9"}.
Une demande qui serait refusée est refusée de la même manière, avec la raison.
Retenir un prix pendant 15 minutes
POST /v1/quote prend le même body que la demande d'image, de vidéo, de musique ou de parole et répond avec le coût affiché (cost_usd), ce qui est retenu pendant son exécution (max_cost_usd, le montant maximum facturé) et un quote_id (pour une transcription, envoyez model et duration_seconds ; une transcription elle-même ne prend pas de quote_id, utilisez max_cost pour la plafonner) :
curl https://api.uberllm.dev/v1/quote \
-H "Authorization: Bearer $UBERLLM_API_KEY" -H "Content-Type: application/json" \
-d '{"model": "kling/kling-o3", "prompt": "a red ball bouncing", "duration_seconds": 5}'
{ "object": "quote", "id": "6f1c…", "quote_id": "6f1c…", "model": "kling/kling-o3",
"kind": "video", "cost_usd": 0.225, "max_cost_usd": 0.495, "unit": "job", "units": 1,
"expires_at": 1792541700, "note": "Nothing was charged. …" }
Envoyez la même demande avec "quote_id": "6f1c…" dans les 15 minutes et elle sera facturée ce que le fournisseur a facturé, au maximum max_cost_usd ; si ce qui doit être retenu au démarrage est supérieur à max_cost_usd, elle est annulée sans frais. Rien n'est facturé ou retenu pour le devis lui-même.
- Un devis lance une demande. L'utiliser à nouveau répond
409 quote_used; un travail qui n'a jamais atteint un modèle rend le devis. - La demande doit demander la même chose : même modèle et mêmes paramètres (prompt, size, clip length, source pictures …). Tout autre élément est refusé avec
400 quote_mismatch.max_cost,metadataetoutput_upload_urlpeuvent différer. - Après 15 minutes, il répond
400 quote_expired. Les modèles de chat sont facturés par token et n'ont pas de devis : plafonnez-les avecprovider.max_price.
Modèles de secours et plafond de prix
"models": ["model-b", "model-c"] nomme les modèles à essayer dans l'ordre lorsque le premier ne peut pas être servi immédiatement (no_available_provider) ou est au-dessus de votre provider.max_price. Tout autre refus est répondu immédiatement. Le model de la réponse nomme le modèle qui l'a produite (une réponse vocale est le fichier audio lui-même : l'Activité montre son modèle). Une demande avec quote_id ne prend pas de models, et une transcription ne prend ni l'un ni l'autre.
"provider": {"max_price": 0.05} est le coût maximum d'une unité (le unit de tarification du modèle : image, seconde, mégapixel, minute, 1K caractères ou demande), en dollars US. Un modèle au-dessus de ce montant est refusé avec 400 max_price_exceeded, et la demande n'est jamais facturée au-delà de max_price fois ses unités. D'autres options provider sont refusées avec 400 unsupported_routing_option, car le choix du vendeur pour les demandes de médias ne peut pas être dirigé par demande.
"model": "best-price:image" (ou image-edit, text-to-video, image-to-video, music, speech, transcription) choisit le modèle en ligne le moins cher pour le travail ; fastest: le plus rapide mesuré. Voir Alias de modèles.
Tags
"metadata": {"project": "spring-ads"} (jusqu'à 16 valeurs textuelles) est stocké avec le travail, renvoyé sur son statut, affiché dans l'Activité et dans l'export CSV, et n'est jamais envoyé à un modèle. Sur une transcription, envoyez-les comme champs de formulaire : -F "metadata[project]=spring-ads". Voir Tags.
Définir un plafond
Envoyez "max_cost" (dollars US, par exemple 0.50) avec toute demande d'image, de vidéo, de musique ou de parole (un champ de formulaire sur une transcription). Si cela peut coûter plus que cela, c'est refusé avec 400 max_cost_too_low et rien n'est facturé. Pour un travail vidéo ou musical, le plafond exact est défini au démarrage du travail ; si le coût dépasse ensuite votre max_cost, le travail est annulé sans frais. Avec "validate": true, le devis ajoute within_max_cost.
Réessayer sans payer deux fois
Envoyez un en-tête Idempotency-Key (toute chaîne unique jusqu'à 255 caractères, comme un UUID) lorsque vous démarrez un travail vidéo ou musical. Si votre connexion est interrompue et que vous envoyez la même demande à nouveau avec la même clé dans les 24 heures, vous récupérez le même travail au lieu d'un second. La même clé avec une demande différente est refusée avec 409 idempotency_key_reused. Les demandes d'image, de parole et de transcription ne prennent pas de clé : une demande qui a échoué n'a pas été facturée, donc envoyez-la à nouveau.
Votre solde
curl -H "Authorization: Bearer $UBERLLM_API_KEY" https://api.uberllm.dev/v1/balance
{ "balance_usd": 12.40, "held_usd": 0.35, "available_usd": 12.05 }
held_usd est mis de côté pour les requêtes en cours ; il revient (moins le coût réel) une fois qu'elles sont terminées. available_usd est ce que les nouvelles requêtes peuvent utiliser.
Options de chaque modèle
GET /v1/models?type=image (ou video, music, speech, transcription, audio pour les deux, chat, all ; plusieurs séparés par des virgules ; sans type la liste est limitée aux modèles de chat) liste les modèles que vous pouvez appeler dès maintenant, chacun avec son type, l'endpoints où l'envoyer et son pricing. GET /v1/models/<model id> fournit un modèle, et pour un modèle média, un objet parameters : un schéma JSON du corps de la requête avec les durées de clip, résolutions, rapports d'aspect et tailles qu'il accepte, et l'image source dont il a besoin.
curl -H "Authorization: Bearer $UBERLLM_API_KEY" \
https://api.uberllm.dev/v1/models/pixverse/pixverse-c1-reference-to-video
pricing inclut usd (le coût maximum d'un unit actuellement) et list_usd (le prix du fournisseur du modèle) ; un modèle de chat a input_usd, output_usd et leurs prix list_ par 1M tokens.
Erreurs
| HTTP | code | signification |
|---|---|---|
| 400 | wrong_endpoint |
par ex. un modèle d'image envoyé à /v1/video/generations, un modèle de chat envoyé à une route d'image, ou un modèle de parole envoyé à /v1/audio/transcriptions ; le message indique le bon chemin. Rien n'est facturé, et l'Activité le liste comme refusé |
| 400 | invalid_request |
la requête est erronée (le message explique pourquoi, par ex. une durée que le modèle n'offre pas ou une image source manquante) |
| 400 | image_required |
une édition, ou un modèle image-vers-vidéo ou référence-vers-vidéo, sans image source |
| 400 | max_cost_too_low |
cela peut coûter plus que votre max_cost ; rien n'a été facturé |
| 409 | idempotency_key_reused |
ce Idempotency-Key a été utilisé pour une requête différente |
| 400 | quote_mismatch / quote_expired |
la requête n'est pas celle citée, ou le devis a plus de 15 minutes |
| 404 | quote_not_found |
aucun devis de ce type sur votre compte |
| 409 | quote_used |
ce devis a déjà démarré une requête |
| 400 | max_price_exceeded |
le modèle coûte plus par unité que votre provider.max_price |
| 400 | unsupported_routing_option |
une option provider autre que max_price |
| 404 | file_not_found |
aucun fichier téléversé de ce type sur votre compte, ou il a expiré |
| 413 | file_too_large |
un téléversement de plus de 10 Mo |
| 429 | too_many_files |
500 téléversements sont déjà conservés ; supprimez-en quelques-uns ou attendez qu'ils expirent |
| 503 | files_unavailable |
les téléversements ne sont pas disponibles pour le moment |
| 413 | request_too_large |
la requête dépasse la limite de taille (40 Mo) ; envoyez les images sous forme de liens https:// et divisez les longs enregistrements |
| 402 | insufficient_credits |
votre solde ne peut pas couvrir le coût maximum du travail ; le message indique le montant nécessaire |
| 404 | model_not_found / not_found |
aucun modèle de ce type, ou aucun travail de ce type sur votre compte |
| 404 | artifact_expired |
le fichier n'est plus conservé (voir ci-dessus) |
| 429 | rate_limited |
attendez les secondes indiquées dans Retry-After (quelques secondes s'il est absent), puis réessayez ; X-RateLimit-Remaining indique le nombre de requêtes restantes cette minute |
| 503 | no_available_provider |
aucun vendeur ne peut le faire pour le moment ; UberLLM a déjà essayé les autres. Le message nomme les modèles du même type qui fonctionnent actuellement |