NieuwKoppel elke agent aan UberLLM: één sleutel, elk model, voor de catalogusprijs of minder.
UberLLM

Afbeeldingen, video, muziek en spraak

De sleutel die je voor tekst gebruikt, maakt ook afbeeldingen, video's, muziek en spraak, en zet opnames om in tekst, allemaal van hetzelfde saldo. Kies een model in de modellenoverzicht (of ?type=image, ?type=music, ?type=audio); de pagina van elk model toont de prijs, het type en een werkend verzoek.

Je wilt Route Bronafbeelding gaat in
Een nieuwe afbeelding van een prompt POST /v1/images/generations (geen)
Een bewerkte afbeelding POST /v1/images/edits image (of image_url, input_images, images)
Een video van een prompt POST /v1/video/generations (geen)
Een video die begint met een afbeelding POST /v1/video/generations image_url (eerste frame); end_image_url voor het laatste frame
Een video van referentieafbeeldingen POST /v1/video/generations input_images met "role": "reference"
Een tekstantwoord over een afbeelding POST /v1/chat/completions een image_url deel in messages[].content
Muziek POST /v1/music/generations (geen)
Spraak van tekst POST /v1/audio/speech (geen)
Tekst van een opname POST /v1/audio/transcriptions (geen; de opname gaat in file)

Een bronafbeelding is een https:// link, een data: URI (data:image/png;base64,iVBORw0…) of platte base64 van een PNG, JPEG of WebP, tot 8 per verzoek. De afbeeldingsroutes accepteren ook een multipart upload (zie hieronder). Links moeten openbare https:// links zijn; http:// links worden geweigerd. Een link die doorverwijst (bijvoorbeeld https://picsum.photos/640/360) wordt gevolgd, tot 5 keer en alleen naar andere openbare https:// links, en het model krijgt de link waar het op terechtkomt. Een link die niet naar een afbeelding leidt, of doorverwijst naar een plek die we niet kunnen gebruiken, wordt geweigerd met 400 invalid_image_url voordat er iets wordt vastgehouden of in rekening gebracht. Afbeeldingen die inline worden verzonden, maken het verzoek groter: houd het onder 10 MB, of stuur links.

In een chatbericht (image_url in messages[].content) wordt een link (https:// of http://) geopend door UberLLM en naar het model gestuurd als een data: URL, omdat de meeste vision modellen alleen dat accepteren. Elke afbeelding kan tot 20 MB zijn (40 MB per verzoek), en doorverwijzingen worden tot 5 keer gevolgd, alleen naar openbare adressen. Gemini modellen krijgen de link zoals deze is. Een link die niet naar een afbeelding leidt, wordt geweigerd met 400 invalid_image_url, en er wordt niets in rekening gebracht.

Maak een afbeelding

Afbeeldingen komen terug in het antwoord.

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 zijn de kosten van de afbeelding (ook in de X-UberLLM-Cost-USD header). In het zeldzame antwoord waar de prijs nog niet bekend is, is het null. Je betaalt wat de provider in rekening bracht, nooit meer dan max_cost_usd, wat ervoor gereserveerd was.

response_format is standaard url, voor zowel /v1/images/generations als /v1/images/edits. Stuur "response_format": "b64_json" om de afbeelding in het antwoord te krijgen. Wanneer het model de afbeelding zelf teruggeeft, bewaart UberLLM deze als een bestand van je account voor 24 uur en antwoordt met de link ernaartoe (https://api.uberllm.dev/v1/files/file_…/content), die ook werkt als de bronafbeelding voor een latere bewerking. Download een url met dezelfde key:

curl -L -o image.png -H "Authorization: Bearer $UBERLLM_API_KEY" \
  "https://api.uberllm.dev/v1/media/artifacts/<id>/0"

n is 1 tot 10.

Een afbeelding bewerken

Gebruik een model dat bewerkt (de ID eindigt meestal op -edit) en stuur de bronafbeelding in 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"}'

Dezelfde afbeelding kan worden ingevoerd als:

  • "image": "data:image/png;base64,iVBORw0…"
  • "image_url": "https://…"
  • "input_images": ["https://…", "https://…"]: de eerste is de te bewerken afbeelding, de rest zijn referenties. Een invoer kan ook {"url": "…", "role": "reference"} zijn.
  • "images": [{"image_url": "https://…"}], de vorm die de OpenAI SDK's versturen

Gewone base64 werkt ook, en "input_image" wordt gelezen als "image".

Of upload het bestand, net als bij OpenAI's afbeeldingsbewerking:

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]

Een bewerking zonder bronafbeelding wordt geweigerd met 400 image_required. Maskers (inpainting) worden niet ondersteund.

Upload één keer een afbeelding

Upload een afbeelding naar /v1/files en gebruik deze zo vaak als je wilt in verzoeken gedurende 24 uur, in plaats van deze bij elk verzoek mee te sturen:

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" }

Plaats vervolgens de id, of de url, overal waar een bronafbeelding hoort: "image": "file_3f9c…", "image": {"file_id": "file_3f9c…"}, "image_url": "file_3f9c…", een invoer van input_images, of een startframe van een video. Alleen jouw account kan de bestanden ervan gebruiken.

  • PNG, JPEG, WebP, GIF, HEIC of AVIF, tot 10 MB per stuk, maximaal 500 tegelijk per account bewaard.
  • Bewaard gedurende 24 uur, daarna verwijderd. GET /v1/files/{id} beschrijft er één, GET /v1/files/{id}/content downloadt deze, DELETE /v1/files/{id} verwijdert deze nu.

Maak een video

Een video duurt een minuut of langer, dus het is een klus: start hem, controleer hem, download hem.

1. Start hem

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 is optioneel: backupmodellen worden alleen in volgorde geprobeerd als de eerste op dit moment geen vrije provider heeft, zodat een druk moment je verzoek niet laat mislukken (zie Fallbackmodellen). De video hierboven duurt ongeveer een minuut en kost ongeveer $0.33.

Om te starten vanuit een afbeelding, gebruik een image-to-video model en voeg image_url toe:

  -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}'

Een model dat twee afbeeldingen combineert, neemt ook end_image_url (het laatste frame). Een image-to-video of reference-to-video model dat geen video kan maken van alleen tekst, wordt geweigerd met 400 image_required wanneer het verzoek geen afbeelding bevat, voordat er iets in rekening wordt gebracht. Een reference-to-video model neemt de afbeeldingen in 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}'

Andere velden, waar het model ze aanbiedt: resolution (480p, 720p, 1080p…), aspect_ratio (16:9, 9:16, 1:1…) en audio (true / false; een model zonder geluidsinstelling krijgt deze niet toegestuurd en maakt de clip zoals het altijd doet). Een reference-to-video model heeft een vorm nodig, dus wanneer je aspect_ratio weglaat, wordt het verzonden als 16:9 (of de eerste vorm van het model als het geen 16:9 maakt); het schema van het model toont die standaardwaarde. De keuzes van elk model worden vermeld door GET /v1/models/<model id> (zie Opties van elk model).

Elk model maakt slechts bepaalde cliplengtes, resoluties en vormen. Een verzoek voor een die het niet maakt, wordt geweigerd met een bericht dat de beschikbare opties vermeldt, voordat er iets in rekening wordt gebracht.

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 is het model dat de klus heeft aangenomen (een backup van models toen de eerste dat niet kon). Kosten die eindigen op _usdc zijn in miljoensten van een dollar (327250 = $0.33). estimated_cost_usd is de vermelde prijs van wat je hebt gevraagd: hetzelfde nummer validate en POST /v1/quote antwoord als cost_usd. Het is de huidige prijs van het model vermenigvuldigd met de seconden (bij de gevraagde resolutie), afbeeldingen of andere eenheden. De klus wordt gefactureerd voor wat de provider ervoor in rekening bracht, wat meer of minder kan zijn. max_cost_usd is wat van je saldo wordt gereserveerd voor de klus: hetzelfde bedrag terwijl het loopt en nadat het eindigt, en het maximale bedrag dat voor de klus in rekening wordt gebracht. Een videomodel waarvan de prijs per second is, wordt per seconde van de clip gefactureerd; een model geprijsd per job kost dat per video.

2. Controleer hem elke 5–10 seconden:

curl -H "Authorization: Bearer $UBERLLM_API_KEY" \
  https://api.uberllm.dev/v1/video/generations/<job id>

HTTP 202 terwijl de klus loopt, HTTP 200 zodra deze eindigt:

{
  "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 }
}

Het model rapporteert de exacte kosten van een klus iets nadat het bestand klaar is, meestal binnen 30 seconden. Tot die tijd toont een succeeded klus "usage": { "cost_usd": null, "cost_pending": true }; vraag het een paar seconden later opnieuw. Als de kosten niet binnen 30 minuten zijn binnengekomen, wordt de klus gefactureerd tegen de vermelde prijs (estimated_cost_usd) en usage.cost_note vermeldt dit.

3. Download hem

curl -L -o video.mp4 -H "Authorization: Bearer $UBERLLM_API_KEY" \
  https://api.uberllm.dev/v1/media/artifacts/<job id>/0

Vraag om een klus te stoppen die je niet langer nodig hebt: DELETE /v1/video/generations/<job id>. We vragen de provider om deze te stoppen; een klus die al was gemaakt, wordt zoals gewoonlijk gefactureerd, en een die wordt gestopt, eindigt canceled en kost niets.

Fal videomodellen

De modellen waarvan de ID begint met fal-ai/ draaien op Fal, onder Fal's eigen prijs: je betaalt Fal's prijs min de korting van de goedkoopste Fal-verkoper op de marktplaats op dit moment (15% korting op 30 oktober 2026; 25% korting wanneer die verkoper verkoopt). De prijs wordt bepaald door wat je aanvraagt — seconden × het tarief voor de resolutie, en of de clip geluid heeft — dus de kosten van de taak zijn bekend wanneer deze begint en estimated_cost_usd is precies wat er in rekening wordt gebracht. Een mislukte taak kost niets. Het stoppen van een taak is een 'best effort': als Fal de clip al heeft gemaakt, wordt deze in rekening gebracht.

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"}'

Fal's eigen prijs per seconde (de jouwe is dit min de live korting; de pagina van elk model, GET /v1/models en POST /v1/quote, tonen de exacte prijs nu):

Model Lengtes (s) Fal's prijs per seconde, vanaf
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 · met geluid $0.126
fal-ai/kling-video/v3/pro/text-to-video, …/image-to-video 3–15 $0.112 · met geluid $0.168
fal-ai/kling-video/o3/standard/text-to-video, …/image-to-video 3–15 $0.084 · met geluid $0.112
fal-ai/kling-video/v2.6/pro/text-to-video, …/image-to-video 5, 10 $0.07 · met geluid $0.14
fal-ai/veo3.1/fast, fal-ai/veo3.1/fast/image-to-video 4, 6, 8 720p/1080p $0.10 · met geluid $0.15 · 4k $0.30
fal-ai/veo3.1, fal-ai/veo3.1/image-to-video 4, 6, 8 720p/1080p $0.20 · met geluid $0.40 · 4k $0.40
fal-ai/veo3.1/lite 4, 6, 8 720p $0.03 · met geluid $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 (afbeelding) $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

Elke prijs is Fal's eigen prijs; de korting gaat ervan af wanneer de taak begint. Als de goedkoopste verkoper een taak weigert, voert een duurdere deze uit en wordt de taak gefactureerd tegen de prijs van die verkoper, nooit meer dan estimated_cost_usd op het antwoord.

Sommige modellen kosten ons minder dan de korting van de verkoper aangeeft. Elke 6 uur vergelijken we wat de marktplaats ons in rekening heeft gebracht voor elk model gedurende de laatste 7 dagen met Fal's prijs voor dezelfde taken. Een model met ten minste 20 taken in die periode wordt dan geprijsd op dat werkelijke aandeel plus 5%, wanneer dat lager is. De modelpagina, /v1/models en de factuur gebruiken allemaal dezelfde prijs. Als dat cijfer ouder is dan 3 dagen, is de korting van de verkoper opnieuw van toepassing.

De velden zijn van ons: duration_seconds, resolution, aspect_ratio, audio en image_url (of input_images). Fal's eigen spelling (duration, generate_audio, start_image_url) werkt ook. Kling, Veo en Kling 2.6 voegen geluid toe, tenzij je "audio": false stuurt.

Taakstatus

status HTTP Betekenis
queued 202 Geaccepteerd, wacht op een model om te beginnen
running 202 Wordt gemaakt; progress gaat van 0 naar 1 wanneer het model dit meldt
succeeded 200 Klaar; download results[].url
failed 200 Niet gemaakt. error.message zegt waarom; error.retryable zegt of opnieuw proberen kan werken
canceled 200 Gestopt voordat het gemaakt was
expired 200 Niet op tijd afgerond

Dit zijn de enige zes waarden. failed, canceled en expired kosten niets. Een DELETE is een verzoek om te stoppen, geen belofte: een taak die de provider toch afmaakt, eindigt succeeded en wordt gefactureerd.

Een druk moment wordt voor u opnieuw geprobeerd. Wanneer geen provider vrij is om uw video te maken, of er een te druk is op dit moment, start UberLLM de taak zelf opnieuw, tot twee keer extra binnen 10 minuten. Het behoudt dezelfde job id en dezelfde maximale kosten. De taak duurt dan alleen iets langer. U ziet failed alleen wanneer elke poging is mislukt, en een mislukte taak kost niets.

Wachten. Stuur callback_url en wij vertellen u wanneer de taak eindigt (zie Callbacks), of vraag de poll URL elke 5–10 seconden op. De meeste video's duren 1–5 minuten; sommige lange of 4K taken duren langer. U hoeft niet te blijven vragen om de rekening te vereffenen: een taak die niemand controleert, wordt nog steeds afgerond en gefactureerd (of terugbetaald) door UberLLM, en verschijnt in Dashboard → Activiteit.

Callbacks

Voeg callback_url toe aan een video- of muziektaak: een https:// link op een openbare host. Wanneer de taak definitief is beëindigd, stuurt UberLLM er een POST naartoe met dezelfde JSON die GET op de taak retourneert.

{ "model": "kling/kling-o3", "prompt": "a red ball bouncing", "duration_seconds": 5,
  "callback_url": "https://your-app.example/uberllm/jobs" }
  • Wanneer. Eenmalig, wanneer de taak is beëindigd en de factuur definitief is: succeeded met usage.cost_usd ingesteld (nooit cost_pending), of failed, canceled of expired (kosten 0). Het wordt niet opnieuw verzonden wanneer de kosten arriveren, omdat het pas daarna wordt verzonden. Ook bij output_upload_url komt de callback nadat de upload is voltooid, dus output_upload.status is al delivered of failed.
  • Headers. Content-Type: application/json, UberLLM-Event: job.succeeded (of job.failed, job.canceled, job.expired), UberLLM-Event-Id: <job id>:<status> en UberLLM-Signature: t=<unix seconds>,v1=<hex>.
  • Opnieuw proberen. Antwoord met een 2xx binnen 15 seconden. Elk ander antwoord, of geen antwoord, wordt opnieuw geprobeerd na 30 seconden, 2 minuten, 10 minuten, 30 minuten en 2 uur, waarna het wordt opgegeven. U kunt de taak op elk moment nog steeds poll.
  • Herhalingen. Een bericht kan meer dan eens arriveren (bijvoorbeeld wanneer uw antwoord verloren gaat). De event id blijft hetzelfde, dus negeer een bericht dat u al hebt verwerkt.
  • Status. De taak toont hoe het is gegaan: "callback": { "status": "delivered", "attempts": 1, "http_status": 200, "delivered_at": 1792540990 }. status is pending, delivered of failed (met error in gewone taal).
  • De link moet https:// zijn op een openbare host, zonder gebruikersnaam of wachtwoord erin. Het wordt nooit teruggestuurd en wordt verwijderd zodra de callback eindigt. webhook_url wordt gelezen als hetzelfde veld. Afbeeldingen en spraak nemen het niet aan: ze komen terug in het antwoord zelf.

Controleer de handtekening

Elk bericht is ondertekend met het webhook secret van uw account. U vindt het in Dashboard → API keys → Webhook signing secret; het wordt aangemaakt de eerste keer dat u het opent, en Replace secret wisselt het direct.

v1 is de hex HMAC-SHA256 van <t>.<raw body>, gesleuteld met het volledige secret (whsec_…). Bereken het over de ruwe bytes die u hebt ontvangen (voordat u de JSON parseert), vergelijk in constante tijd, en weiger een t die ouder is dan 5 minuten.

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

Meldingen naar een webhook

De waarschuwings-e-mails kunnen ook naar een webhook gaan: stel een link in onder Dashboard → Credits → Voor ontwikkelaars: stuur meldingen ook naar een webhook, of PUT /api/v1/billing/alerts {"alerts_webhook_url": "https://…"} (null schakelt het uit). De berichten worden ondertekend, opnieuw geprobeerd en op dezelfde manier geïdentificeerd als callbacks. De body is:

{ "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: het saldo is onder het bedrag gedaald dat u hebt ingesteld voor de e-mail voor laag saldo. Eenmalig verzonden elke keer dat het eronder zakt; er wordt niets verzonden totdat u een bedrag instelt.
  • hold.not_released: geld dat voor een taak was gereserveerd, werd nog steeds vastgehouden meer dan 15 minuten nadat de taak was beëindigd. data.jobs somt ze op (id, kind, model, ended_at, held_usd) en data.held_usd is het totaal. Het geld is nog steeds van u en ons team is op de hoogte gebracht.

Hoe lang bestanden worden bewaard

Download een resultaat snel nadat het is gemaakt:

  • Video- en muziekbestanden worden ongeveer 3 uur bewaard nadat de taak is voltooid.
  • Afbeeldingslinks naar /v1/media/artifacts/… worden ongeveer 15 minuten bewaard; afbeeldingslinks naar /v1/files/… voor 24 uur. Vraag om "response_format": "b64_json" om de afbeelding in het antwoord te krijgen en zelf te bewaren.
  • Afbeeldingen die je uploadt naar /v1/files worden 24 uur bewaard.

Daarna antwoordt de download 404 artifact_expired.

Stuur resultaten naar je eigen opslag

Voeg output_upload_url toe aan een video- of muziektaak: een https:// link die een HTTP PUT accepteert, zoals een presigned S3, R2 of Google Cloud Storage upload link. Wanneer de taak slaagt, uploadt UberLLM het bestand daarheen met zijn content type. Voor een taak die meerdere bestanden maakt, stuur output_upload_urls, één link per bestand in volgorde (maximaal 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=…" }

De status van de taak heeft dan output_upload:

"output_upload": { "status": "delivered", "attempts": 1, "delivered_at": 1792540990,
  "files": [{ "artifact_index": 0, "http_status": 200, "bytes": 5242880 }] }
  • status is pending totdat de upload is voltooid, daarna delivered of failed (met error in gewone woorden).
  • Een mislukte upload wordt opnieuw geprobeerd na 30 seconden, 2, 10 en 30 minuten, en wordt dan gemarkeerd als failed; het bestand kan nog steeds worden gedownload van results[].url zolang het wordt bewaard. Maak de link minstens een uur geldig.
  • De link moet https:// zijn op een openbare host, zonder gebruikersnaam of wachtwoord erin. Deze wordt nooit terug getoond en wordt verwijderd zodra de upload eindigt.
  • Afbeeldingen accepteren dit niet: een afbeelding komt terug in het antwoord zelf.

Muziek

Dezelfde drie stappen op /v1/music/generations. curl -OJ slaat het nummer op onder de bestandsnaam die we meesturen.

Spraak

Tekst gaat erin, een audiobestand komt terug als antwoord.

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

Je kunt ook voice, response_format (bijvoorbeeld mp3 of wav) en speed meesturen. Elk model heeft zijn eigen stemmen (GET /v1/models/{id} somt ze op). Met best-price:speech of fastest:speech weet je van tevoren niet welk model wordt gebruikt, dus de stem wordt daarop aangepast: een stem die het model heeft, wordt behouden, de namen van OpenAI worden gekoppeld aan de overeenkomstige stem van het model waar het die heeft (alloy → af_alloy), en elke andere stem, of geen, krijgt de eerste stem van het model. Wanneer je een model benoemt, wordt je stem zoals hij is meegestuurd. Het wordt gefactureerd per begonnen 1K tekens van input. Het antwoord is het audiobestand; de X-UberLLM-Cost-USD header ervan vermeldt wat het kostte, en X-Request-Id is de ID van het verzoek zoals vermeld in Activiteit. "validate": true, max_cost, metadata, models en provider.max_price werken net als bij afbeeldingen (zie Wat je betaalt).

Zet een opname om in tekst

Stuur de opname als een formulier met file en 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 } }

Dit zijn de kosten: usage.cost_usd (ook in de X-UberLLM-Cost-USD header, de enige plek voor text, srt en vtt antwoorden). Onze eigen velden gaan ook in het formulier en worden nooit naar het model gestuurd: -F max_cost=0.10, -F "metadata[project]=podcast", -F validate=true.

Je kunt ook language, prompt, temperature en response_format meesturen: json (de standaard), of text, srt of vtt om platte tekst terug te krijgen. Er wordt gefactureerd per minuut audio. Bestanden kunnen tot 40 MB groot zijn. Het formaat (mp3, wav, ogg, flac, m4a/mp4, webm, aac) wordt gelezen uit de opname zelf, niet uit de bestandsnaam, dus een upload genaamd tmp123.out werkt.

Met de 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)

Wat je betaalt

  • De prijs van wat is gemaakt: wat de provider in rekening bracht, zonder UberLLM-kosten. Een afbeelding of transcriptieantwoord en een voltooide taak dragen dit als usage.cost_usd (Amerikaanse dollars); elk antwoord dat iets heeft gemaakt, draagt het ook in de X-UberLLM-Cost-USD response header (voor spraak is de header de enige plek, aangezien het antwoord het audiobestand is). Het verschijnt in Dashboard → Activiteit. De rest van de reservering komt terug.
  • Niets voor een taak die failed, canceled of expired was, of voor een afbeelding-, spraak- of transcriptieverzoek dat mislukt.
  • De goedkoopste verkoper die precies maakt wat je hebt gevraagd. Veel afbeelding- en videomodellen hebben meer dan één verkoper; elk verzoek gaat naar de goedkoopste die het met jouw instellingen kan maken, en naar een andere als die het niet kan verwerken. De modellijst en elke modelpagina tonen de prijs bij de goedkoopste verkoper die nu live is.
  • Bekijk je saldo en wat is gereserveerd met GET /v1/balance (hieronder).

Controleer de prijs voordat je uitgeeft

Voeg "validate": true toe aan elk afbeelding-, video-, muziek- of spraakverzoek (voor een transcriptie, stuur validate=true als een form field met de opname). Het verzoek wordt gecontroleerd alsof het echt was (model, opties, bronafbeeldingen), er wordt niets gemaakt en niets in rekening gebracht, en HTTP 200 geeft aan wat het kost tegen de vermelde prijs (cost_usd, de prijs die op de modelpagina wordt getoond; je betaalt wat de provider in rekening bracht, wat kan verschillen) en wat wordt gereserveerd terwijl het draait (max_cost_usd, het maximale dat wordt gefactureerd):

{ "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. …" }

Als de verkoper van het model een instelling nodig heeft die je hebt weggelaten (een reference-to-video model heeft aspect_ratio nodig), wordt de standaardwaarde waarmee het wordt verzonden getoond op het schema van het model en in de offerte als defaults_applied, bijv. "defaults_applied": {"aspect_ratio": "16:9"}.

Een verzoek dat zou worden geweigerd, wordt op dezelfde manier geweigerd, met de reden.

Reserveer een prijs voor 15 minuten

POST /v1/quote gebruikt dezelfde body als het afbeelding-, video-, muziek- of spraakverzoek en antwoordt met de vermelde kosten (cost_usd), wat wordt gereserveerd terwijl het draait (max_cost_usd, het maximale dat wordt gefactureerd) en een quote_id (voor een transcriptie, stuur model en duration_seconds; een transcriptie zelf neemt geen quote_id, gebruik max_cost om het te begrenzen):

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. …" }

Stuur hetzelfde verzoek met "quote_id": "6f1c…" binnen 15 minuten en het wordt gefactureerd wat de provider in rekening bracht, maximaal max_cost_usd; als wat moet worden gereserveerd wanneer het start boven max_cost_usd is, wordt het kosteloos geannuleerd. Er wordt niets in rekening gebracht of gereserveerd voor de offerte zelf.

  • Eén offerte start één verzoek. Het opnieuw gebruiken ervan antwoordt 409 quote_used; een taak die nooit een model heeft bereikt, geeft de offerte terug.
  • Het verzoek moet om hetzelfde vragen: hetzelfde model en dezelfde instellingen (prompt, size, clip length, source pictures …). Iets anders wordt geweigerd met 400 quote_mismatch. max_cost, metadata en output_upload_url mogen verschillen.
  • Na 15 minuten antwoordt het 400 quote_expired. Chatmodellen worden per token gefactureerd en hebben geen offerte: begrens ze met provider.max_price.

Fallback modellen en een prijsplafond

"models": ["model-b", "model-c"] noemt modellen om in volgorde te proberen wanneer de eerste nu niet kan worden geleverd (no_available_provider) of boven je provider.max_price is. Elke andere weigering wordt direct beantwoord. De model van het antwoord noemt het model dat het heeft gemaakt (een spraakantwoord is het audiobestand zelf: Activiteit toont het model). Een verzoek met quote_id neemt geen models, en een transcriptie neemt geen van beide.

"provider": {"max_price": 0.05} is het maximale dat één eenheid (de pricing unit van het model: image, second, megapixel, minute, 1K characters of request) mag kosten, in Amerikaanse dollars. Een model daarboven wordt geweigerd met 400 max_price_exceeded, en het verzoek wordt nooit meer gefactureerd dan max_price keer zijn eenheden. Andere provider opties worden geweigerd met 400 unsupported_routing_option, omdat de verkoperskeuze voor mediaverzoeken niet per verzoek kan worden gestuurd.

"model": "best-price:image" (of image-edit, text-to-video, image-to-video, music, speech, transcription) kiest het goedkoopste live model voor de taak; fastest: de snelst gemeten. Zie Model aliassen.

Tags

"metadata": {"project": "spring-ads"} (maximaal 16 tekstwaarden) wordt opgeslagen bij de taak, geretourneerd bij de status, getoond in Activiteit en in de CSV-export, en nooit naar een model gestuurd. Bij een transcriptie, stuur ze als form fields: -F "metadata[project]=spring-ads". Zie Tags.

Stel een plafond in

Stuur "max_cost" (Amerikaanse dollars, bijv. 0.50) mee met elk afbeelding-, video-, muziek- of spraakverzoek (een form field bij een transcriptie). Als het meer kan kosten dan dat, wordt het geweigerd met 400 max_cost_too_low en wordt er niets in rekening gebracht. Voor een video- of muziektaak wordt het exacte plafond ingesteld wanneer de taak start; als het dan boven je max_cost uitkomt, wordt de taak kosteloos geannuleerd. Met "validate": true, voegt de offerte within_max_cost toe.

Opnieuw proberen zonder twee keer te betalen

Stuur een Idempotency-Key header (elke unieke string tot 255 characters, zoals een UUID) wanneer je een video- of muziektaak start. Als je verbinding wegvalt en je stuurt hetzelfde verzoek opnieuw met dezelfde key binnen 24 uur, krijg je dezelfde taak terug in plaats van een tweede. Dezelfde key met een ander verzoek wordt geweigerd met 409 idempotency_key_reused. Afbeelding-, spraak- en transcriptieverzoeken nemen geen key: een mislukte werd niet in rekening gebracht, dus stuur het opnieuw.

Je saldo

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 is gereserveerd voor lopende verzoeken; het komt terug (minus de werkelijke kosten) wanneer ze zijn voltooid. available_usd is wat nieuwe verzoeken kunnen gebruiken.

Opties van elk model

GET /v1/models?type=image (of video, music, speech, transcription, audio voor beide, chat, all; meerdere samengevoegd met komma's; zonder type is de lijst alleen chatmodellen) toont de modellen die je nu kunt aanroepen, elk met zijn type, de endpoints om het naartoe te sturen en zijn pricing. GET /v1/models/<model id> geeft één model, en voor een mediamodel een parameters object: een JSON Schema van de request body met de clip lengtes, resoluties, beeldverhoudingen en groottes die het accepteert, en welke bronafbeelding het nodig heeft.

curl -H "Authorization: Bearer $UBERLLM_API_KEY" \
  https://api.uberllm.dev/v1/models/pixverse/pixverse-c1-reference-to-video

pricing heeft usd (het meeste wat één unit nu kost) en list_usd (de eigen prijs van de modelmaker); een chatmodel heeft input_usd, output_usd en hun list_ prijzen per 1M tokens.

Fouten

HTTP code betekenis
400 wrong_endpoint bijv. een afbeeldingsmodel verzonden naar /v1/video/generations, een chatmodel verzonden naar een afbeeldingsroute, of een spraakmodel verzonden naar /v1/audio/transcriptions; het bericht noemt het juiste pad. Er wordt niets in rekening gebracht, en Activiteit vermeldt het als geweigerd
400 invalid_request het verzoek is fout (het bericht zegt waarom, bijv. een duur die het model niet aanbiedt of een ontbrekende bronafbeelding)
400 image_required een bewerking, of een afbeelding-naar-video- of referentie-naar-video-model, zonder bronafbeelding
400 max_cost_too_low het kan meer kosten dan uw max_cost; er werd niets in rekening gebracht
409 idempotency_key_reused die Idempotency-Key werd gebruikt voor een ander verzoek
400 quote_mismatch / quote_expired het verzoek is niet het geciteerde, of de offerte is ouder dan 15 minuten
404 quote_not_found geen dergelijke offerte op uw account
409 quote_used die offerte heeft al een verzoek gestart
400 max_price_exceeded het model kost meer per eenheid dan uw provider.max_price
400 unsupported_routing_option een provider optie anders dan max_price
404 file_not_found geen dergelijk geüpload bestand op uw account, of het is verlopen
413 file_too_large een upload van meer dan 10 MB
429 too_many_files 500 uploads worden al bewaard; verwijder er enkele of wacht tot ze verlopen
503 files_unavailable uploads zijn momenteel niet beschikbaar
413 request_too_large het verzoek overschrijdt de groottelimiet (40 MB); stuur afbeeldingen als https:// links en splits lange opnames
402 insufficient_credits uw saldo kan de maximale kosten van de taak niet dekken; het bericht zegt hoeveel er nodig is
404 model_not_found / not_found geen dergelijk model, of geen dergelijke taak op uw account
404 artifact_expired het bestand wordt niet langer bewaard (zie hierboven)
429 rate_limited wacht de seconden in Retry-After (een paar seconden als het afwezig is), probeer het dan opnieuw; X-RateLimit-Remaining toont hoeveel verzoeken er deze minuut nog over zijn
503 no_available_provider geen enkele verkoper kan het nu maken; UberLLM heeft de anderen al geprobeerd. Het bericht noemt de modellen van dezelfde soort die nu werken