NovitàCollega qualsiasi agente a UberLLM: una chiave, tutti i modelli, a prezzo di listino o meno.
UberLLM

Immagini, video, musica e parlato

La chiave che usi per il testo crea anche immagini, video, musica e parlato, e trasforma le registrazioni in testo, dallo stesso saldo. Scegli un modello nella tabella dei modelli (o ?type=image, ?type=music, ?type=audio); la pagina di ogni modello mostra il suo prezzo, il suo tipo e una richiesta funzionante.

Vuoi Route L'immagine sorgente va in
Una nuova immagine da un prompt POST /v1/images/generations (nessuno)
Un'immagine modificata POST /v1/images/edits image (o image_url, input_images, images)
Un video da un prompt POST /v1/video/generations (nessuno)
Un video che parte da un'immagine POST /v1/video/generations image_url (primo frame); end_image_url per l'ultimo frame
Un video da immagini di riferimento POST /v1/video/generations input_images con "role": "reference"
Una risposta testuale su un'immagine POST /v1/chat/completions una parte image_url in messages[].content
Musica POST /v1/music/generations (nessuno)
Parlato da testo POST /v1/audio/speech (nessuno)
Testo da una registrazione POST /v1/audio/transcriptions (nessuno; la registrazione va in file)

Un'immagine sorgente è un link https://, un URI data: (data:image/png;base64,iVBORw0…) o un base64 semplice di un PNG, JPEG o WebP, fino a 8 per richiesta. Le route delle immagini accettano anche un upload multipart (sotto). I link devono essere link pubblici https://; i link http:// sono rifiutati. Un link che reindirizza (ad esempio https://picsum.photos/640/360) viene seguito, fino a 5 volte e solo ad altri link pubblici https://, e il modello riceve il link di destinazione. Un link che non porta a un'immagine, o reindirizza a un luogo che non possiamo usare, viene rifiutato con 400 invalid_image_url prima che venga trattenuto o addebitato alcunché. Le immagini inviate inline rendono la richiesta più grande: mantienila sotto i 10 MB, o invia link.

In un messaggio di chat (image_url in messages[].content), un link (https:// o http://) viene aperto da UberLLM e inviato al modello come URL data:, perché la maggior parte dei modelli di visione accetta solo quello. Ogni immagine può essere fino a 20 MB (40 MB per richiesta), e i reindirizzamenti vengono seguiti fino a 5 volte, solo ad indirizzi pubblici. I modelli Gemini ricevono il link così com'è. Un link che non porta a un'immagine viene rifiutato con 400 invalid_image_url, e nulla viene addebitato.

Crea un'immagine

Le immagini vengono restituite nella risposta.

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 è il costo dell'immagine (anche nell'intestazione X-UberLLM-Cost-USD). Nella rara risposta in cui il prezzo non è ancora noto, è null. Paghi ciò che il provider ha addebitato, mai più di max_cost_usd, ciò che era stato trattenuto per essa.

response_format è url per impostazione predefinita, sia per /v1/images/generations che per /v1/images/edits. Invia "response_format": "b64_json" per ottenere l'immagine all'interno della risposta invece. Quando il modello restituisce l'immagine stessa, UberLLM la conserva come file del tuo account per 24 ore e risponde con il suo link (https://api.uberllm.dev/v1/files/file_…/content), che funziona anche come immagine sorgente per una modifica successiva. Scarica un URL con la stessa chiave:

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

n è 1 a 10.

Modifica un'immagine

Usa un modello che modifica (il suo ID di solito finisce in -edit) e invia l'immagine sorgente 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"}'

La stessa immagine può essere inviata come:

  • "image": "data:image/png;base64,iVBORw0…"
  • "image_url": "https://…"
  • "input_images": ["https://…", "https://…"]: la prima è l'immagine da modificare, il resto sono riferimenti. Una voce può anche essere {"url": "…", "role": "reference"}.
  • "images": [{"image_url": "https://…"}], il formato che gli SDK di OpenAI inviano

Anche il semplice base64 funziona, e "input_image" viene letto come "image".

Oppure carica il file, come con la modifica immagine di 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]

Una modifica senza immagine sorgente viene rifiutata con 400 image_required. Le maschere (inpainting) non sono supportate.

Carica un'immagine una volta

Carica un'immagine su /v1/files e usala in tutte le richieste che vuoi per 24 ore, invece di inviarla in ogni richiesta:

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

Poi inserisci l'id, o il url, ovunque vada un'immagine sorgente: "image": "file_3f9c…", "image": {"file_id": "file_3f9c…"}, "image_url": "file_3f9c…", una voce di input_images, o il fotogramma iniziale di un video. Solo il tuo account può usare i suoi file.

  • PNG, JPEG, WebP, GIF, HEIC o AVIF, fino a 10 MB ciascuno, massimo 500 conservati per account alla volta.
  • Conservati per 24 ore, poi eliminati. GET /v1/files/{id} ne descrive uno, GET /v1/files/{id}/content lo scarica, DELETE /v1/files/{id} lo elimina ora.

Crea un video

Un video richiede un minuto o più, quindi è un lavoro: avvialo, controllalo, scaricalo.

1. Avvialo

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 è facoltativo: i modelli di backup vengono provati in ordine solo quando il primo non ha un provider disponibile al momento, così un momento di traffico non fa fallire la tua richiesta (vedi Modelli di fallback). Il video qui sopra richiede circa un minuto e costa circa $0.33.

Per iniziare da un'immagine, usa un modello da immagine a video e aggiungi 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 modello che fonde due immagini prende anche end_image_url (l'ultimo fotogramma). Un modello da immagine a video o da riferimento a video che non può creare video solo da testo viene rifiutato con 400 image_required quando la richiesta non ha immagini, prima che venga addebitato qualcosa. Un modello da riferimento a video prende le immagini 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}'

Altri campi, dove il modello li offre: resolution (480p, 720p, 1080p…), aspect_ratio (16:9, 9:16, 1:1…) e audio (true / false; a un modello senza impostazioni audio non viene inviato e crea il clip come fa sempre). Un modello da riferimento a video ha bisogno di una forma, quindi quando lasci fuori aspect_ratio viene inviato come 16:9 (o la prima forma del modello se non crea 16:9); lo schema del modello mostra quel valore predefinito. Le scelte di ogni modello sono elencate da GET /v1/models/<model id> (vedi Opzioni di ogni modello).

Ogni modello crea solo determinate lunghezze, risoluzioni e forme di clip. Una richiesta per una che non crea viene rifiutata con un messaggio che elenca quelle che crea, prima che venga addebitato qualcosa.

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 è il modello che ha preso il lavoro (un backup da models quando il primo non ha potuto). I costi che terminano in _usdc sono in milionesimi di dollaro (327250 = $0.33). estimated_cost_usd è il prezzo di listino di ciò che hai chiesto: lo stesso numero validate e POST /v1/quote rispondono come cost_usd. È il prezzo attuale del modello moltiplicato per i secondi (alla risoluzione richiesta), le immagini o altre unità. Il lavoro viene fatturato per quanto il provider ha addebitato per esso, che può essere più o meno. max_cost_usd è ciò che viene trattenuto dal tuo saldo per il lavoro: la stessa cifra mentre è in esecuzione e dopo che termina, e il massimo che il lavoro viene fatturato. Un modello video il cui prezzo è per second viene fatturato per secondo del clip; uno il cui prezzo è per job costa tanto per video.

2. Controllalo ogni 5–10 secondi:

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

HTTP 202 mentre il lavoro è in esecuzione, HTTP 200 una volta terminato:

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

Il modello riporta l'addebito esatto di un lavoro poco dopo che il file è pronto, di solito entro 30 secondi. Fino ad allora un lavoro succeeded mostra "usage": { "cost_usd": null, "cost_pending": true }; riprova qualche secondo dopo. Se l'addebito non è arrivato entro 30 minuti, il lavoro viene fatturato al suo prezzo di listino (estimated_cost_usd) e usage.cost_note lo indica.

3. Scaricalo

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

Chiedi di fermare un lavoro di cui non hai più bisogno: DELETE /v1/video/generations/<job id>. Chiediamo al provider di fermarlo; un lavoro che aveva già fatto viene fatturato come al solito, e uno che ferma termina canceled e non costa nulla.

Modelli video Fal

I modelli il cui ID inizia con fal-ai/ funzionano su Fal, a un prezzo inferiore a quello di Fal: paghi il prezzo di Fal meno lo sconto del venditore Fal più economico sul marketplace in questo momento (15% di sconto su 30 ottobre 2026; 25% di sconto ogni volta che quel venditore vende). Il prezzo è fissato in base a ciò che chiedi — secondi × la tariffa per la risoluzione e se il clip ha l'audio — quindi il costo del lavoro è noto quando inizia e estimated_cost_usd è esattamente ciò che viene fatturato. Un lavoro fallito non costa nulla. Interromperne uno è un tentativo: se Fal ha già realizzato il clip, viene fatturato.

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

Il prezzo al secondo di Fal (il tuo è questo meno lo sconto in tempo reale; la pagina di ogni modello, GET /v1/models e POST /v1/quote mostrano il prezzo esatto ora):

Modello Durate (s) Prezzo al secondo di Fal, da
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 · con audio $0.126
fal-ai/kling-video/v3/pro/text-to-video, …/image-to-video 3–15 $0.112 · con audio $0.168
fal-ai/kling-video/o3/standard/text-to-video, …/image-to-video 3–15 $0.084 · con audio $0.112
fal-ai/kling-video/v2.6/pro/text-to-video, …/image-to-video 5, 10 $0.07 · con audio $0.14
fal-ai/veo3.1/fast, fal-ai/veo3.1/fast/image-to-video 4, 6, 8 720p/1080p $0.10 · con audio $0.15 · 4k $0.30
fal-ai/veo3.1, fal-ai/veo3.1/image-to-video 4, 6, 8 720p/1080p $0.20 · con audio $0.40 · 4k $0.40
fal-ai/veo3.1/lite 4, 6, 8 720p $0.03 · con audio $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 (immagine) $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

Ogni prezzo è quello di Fal; lo sconto viene applicato quando il lavoro inizia. Se il venditore più economico rifiuta un lavoro, uno più costoso lo esegue e il lavoro viene fatturato al prezzo di quel venditore, mai più di estimated_cost_usd sulla risposta.

Alcuni modelli ci costano meno di quanto indichi lo sconto del venditore. Ogni 6 ore confrontiamo quanto il marketplace ci ha addebitato per ogni modello negli ultimi 7 giorni con il prezzo di Fal per gli stessi lavori. Un modello con almeno 20 lavori in quel periodo viene quindi prezzato a quella quota reale più 5%, quando è inferiore. La pagina del modello, /v1/models e la fattura utilizzano tutte lo stesso prezzo. Se quella cifra ha più di 3 giorni, lo sconto del venditore si applica di nuovo.

I campi sono nostri: duration_seconds, resolution, aspect_ratio, audio e image_url (o input_images). Anche la grafia di Fal (duration, generate_audio, start_image_url) funziona. Kling, Veo e Kling 2.6 aggiungono l'audio a meno che tu non invii "audio": false.

Stato del job

status HTTP Significato
queued 202 Accettato, in attesa che un modello lo avvii
running 202 In creazione; progress passa da 0 a 1 quando il modello lo segnala
succeeded 200 Completato; scarica results[].url
failed 200 Non creato. error.message spiega perché; error.retryable indica se riprovare può funzionare
canceled 200 Interrotto prima della creazione
expired 200 Non completato in tempo

Questi sei sono gli unici valori. failed, canceled e expired non costano nulla. Un DELETE è una richiesta di interruzione, non una promessa: un job che il provider completa comunque termina succeeded e viene addebitato.

Un momento di sovraccarico viene ritentato per te. Quando nessun provider è libero per creare il tuo video, o uno è troppo occupato in questo momento, UberLLM riavvia il job da solo, fino a due volte in più entro 10 minuti. Mantiene lo stesso ID del job e lo stesso costo massimo. Il job richiede solo un po' più di tempo. Vedi failed solo quando tutti i tentativi sono falliti, e un job fallito non costa nulla.

In attesa. Invia callback_url e ti diciamo quando il job termina (vedi Callback), oppure interroga il poll URL ogni 5–10 secondi. La maggior parte dei video richiede 1–5 minuti; alcuni job lunghi o 4K richiedono più tempo. Non devi continuare a chiedere perché il conto venga saldato: un job che nessuno controlla viene comunque completato e addebitato (o rimborsato) da UberLLM, e compare in Dashboard → Attività.

Callback

Aggiungi callback_url a un lavoro video o musicale: un link https:// su un host pubblico. Quando il lavoro è terminato definitivamente, UberLLM gli invia un POST con lo stesso JSON che GET sul lavoro restituisce.

{ "model": "kling/kling-o3", "prompt": "a red ball bouncing", "duration_seconds": 5,
  "callback_url": "https://your-app.example/uberllm/jobs" }
  • Quando. Una volta, quando il lavoro è terminato e il suo conto è definitivo: succeeded con usage.cost_usd impostato (mai cost_pending), o failed, canceled o expired (costo 0). Non viene inviato di nuovo quando arriva il costo, perché viene inviato solo dopo. Anche con output_upload_url, il callback arriva dopo che l'upload è stato completato, quindi output_upload.status è già delivered o failed.
  • Header. Content-Type: application/json, UberLLM-Event: job.succeeded (o job.failed, job.canceled, job.expired), UberLLM-Event-Id: <job id>:<status> e UberLLM-Signature: t=<unix seconds>,v1=<hex>.
  • Tentativi. Rispondi con qualsiasi 2xx entro 15 secondi. Qualsiasi altra risposta, o nessuna risposta, viene ritentata dopo 30 secondi, 2 minuti, 10 minuti, 30 minuti e 2 ore, dopodiché si rinuncia. Puoi comunque interrogare il lavoro in qualsiasi momento.
  • Ripetizioni. Un messaggio può arrivare più di una volta (ad esempio quando la tua risposta viene persa). L'ID dell'evento rimane lo stesso, quindi scarta quelli che hai già gestito.
  • Stato. Il lavoro mostra come è andato: "callback": { "status": "delivered", "attempts": 1, "http_status": 200, "delivered_at": 1792540990 }. status è pending, delivered o failed (con error in parole semplici).
  • Il link deve essere https:// su un host pubblico, senza nome utente o password al suo interno. Non viene mai mostrato di nuovo e viene eliminato una volta terminato il callback. webhook_url viene letto come lo stesso campo. Immagini e parlato non lo accettano: tornano nella risposta stessa.

Verifica la firma

Ogni messaggio è firmato con il segreto webhook del tuo account. Lo trovi in Dashboard → API keys → Webhook signing secret; viene creato la prima volta che lo apri, e Replace secret lo sostituisce immediatamente.

v1 è l'HMAC-SHA256 esadecimale di <t>.<raw body>, con chiave l'intero segreto (whsec_…). Calcolalo sui byte grezzi che hai ricevuto (prima di analizzare il JSON), confronta in tempo costante e rifiuta un t più vecchio di 5 minuti.

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

Avvisi a un webhook

Le email di avviso possono anche andare a un webhook: imposta un link sotto Dashboard → Credits → For developers: also send alerts to a webhook, o PUT /api/v1/billing/alerts {"alerts_webhook_url": "https://…"} (null lo disattiva). I messaggi sono firmati, ritentati e identificati allo stesso modo dei callback. Il corpo è:

{ "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: il saldo è sceso al di sotto dell'importo impostato per l'email di saldo basso. Inviato una volta ogni volta che scende al di sotto; nulla viene inviato finché non imposti un importo.
  • hold.not_released: denaro accantonato per un lavoro era ancora trattenuto più di 15 minuti dopo la fine del lavoro. data.jobs li elenca (id, kind, model, ended_at, held_usd) e data.held_usd è il totale. Il denaro è ancora tuo e il nostro team è stato informato.

Per quanto tempo i file vengono conservati

Scarica un risultato subito dopo che è stato creato:

  • I file video e musicali sono conservati per circa 3 ore dopo la fine del lavoro.
  • I link alle immagini a /v1/media/artifacts/… sono conservati per circa 15 minuti; i link alle immagini a /v1/files/… per 24 ore. Chiedi "response_format": "b64_json" per ottenere l'immagine all'interno della risposta e conservarla tu stesso.
  • Le immagini che carichi su /v1/files sono conservate per 24 ore.

Dopodiché il download risponde 404 artifact_expired.

Invia i risultati al tuo spazio di archiviazione

Aggiungi output_upload_url a un lavoro video o musicale: un link https:// che accetta un HTTP PUT, come un link di caricamento S3, R2 presigned o Google Cloud Storage. Quando il lavoro ha successo, UberLLM carica il file lì con il suo content type. Per un lavoro che crea più file, invia output_upload_urls, un link per file in ordine (al massimo 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=…" }

Lo stato del lavoro ha quindi output_upload:

"output_upload": { "status": "delivered", "attempts": 1, "delivered_at": 1792540990,
  "files": [{ "artifact_index": 0, "http_status": 200, "bytes": 5242880 }] }
  • status è pending finché il caricamento non è completato, poi delivered o failed (con error in parole semplici).
  • Un caricamento fallito viene riprovato dopo 30 secondi, 2, 10 e 30 minuti, poi contrassegnato failed; il file può ancora essere scaricato da results[].url finché è conservato. Rendi il link valido per almeno un'ora.
  • Il link deve essere https:// su un host pubblico, senza nome utente o password. Non viene mai mostrato di nuovo, ed è eliminato una volta terminato il caricamento.
  • Le immagini non lo accettano: un'immagine torna nella risposta stessa.

Musica

Gli stessi tre passaggi a /v1/music/generations. curl -OJ salva la traccia con il nome del file che inviamo.

Voce

Il testo entra, un file audio torna come risposta.

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

Puoi anche inviare voice, response_format (ad esempio mp3 o wav) e speed. Ogni modello ha le sue voci (GET /v1/models/{id} le elenca). Con best-price:speech o fastest:speech non sai in anticipo quale modello viene usato, quindi la voce viene adattata ad esso: una voce che il modello ha viene mantenuta, i nomi di OpenAI sono mappati alla voce corrispondente del modello, dove ne ha una (alloy → af_alloy), e qualsiasi altra voce, o nessuna, ottiene la prima voce del modello. Quando nomini un modello, la tua voce viene inviata così com'è. Viene fatturato per ogni 1K caratteri di input iniziati. La risposta è il file audio; il suo header X-UberLLM-Cost-USD dice quanto è costato, e X-Request-Id è l'id della richiesta come elencato in Attività. "validate": true, max_cost, metadata, models e provider.max_price funzionano come sulle immagini (vedi Cosa paghi).

Trasforma una registrazione in testo

Invia la registrazione come un modulo con file e 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 } }

usage.cost_usd è quanto costa (anche nell'intestazione X-UberLLM-Cost-USD, l'unico posto per le risposte text, srt e vtt). Anche i nostri campi vanno nel modulo e non vengono mai inviati al modello: -F max_cost=0.10, -F "metadata[project]=podcast", -F validate=true.

Puoi anche inviare language, prompt, temperature e response_format: json (il predefinito), oppure text, srt o vtt per ottenere testo semplice in risposta. Viene fatturato al minuto di audio. I file possono essere fino a 40 MB. Il formato (mp3, wav, ogg, flac, m4a/mp4, webm, aac) viene letto dalla registrazione stessa, non dal nome del file, quindi un caricamento chiamato tmp123.out funziona.

Con l'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)

Cosa paghi

  • Il prezzo di ciò che è stato prodotto: quanto addebitato dal provider, senza commissioni UberLLM. Una risposta di immagine o trascrizione e un lavoro completato lo riportano come usage.cost_usd (dollari USA); ogni risposta che ha prodotto qualcosa lo riporta anche nell'header di risposta X-UberLLM-Cost-USD (per il parlato, l'header è l'unico posto, poiché la risposta è il file audio). Appare in Dashboard → Attività. Il resto dell'importo bloccato viene restituito.
  • Nulla per un lavoro che failed, è stato canceled o expired, o per una richiesta di immagine, parlato o trascrizione che fallisce.
  • Il venditore più economico che produce esattamente ciò che hai chiesto. Molti modelli di immagine e video hanno più di un venditore; ogni richiesta va a quello più economico che può produrla con le tue impostazioni, e a un altro se quello non può accettarla. L'elenco dei modelli e ogni pagina del modello mostrano il prezzo del venditore più economico disponibile ora.
  • Vedi il tuo saldo e ciò che è bloccato con GET /v1/balance (sotto).

Controlla il prezzo prima di spendere

Aggiungi "validate": true a qualsiasi richiesta di immagine, video, musica o parlato (per una trascrizione, invia validate=true come campo del modulo con la registrazione). La richiesta viene controllata come se fosse reale (modello, opzioni, immagini sorgente), nulla viene prodotto e nulla viene addebitato, e HTTP 200 indica quanto costa al prezzo di listino (cost_usd, il prezzo mostrato sulla pagina del modello; paghi quanto addebitato dal provider, che può differire) e quanto viene bloccato mentre è in esecuzione (max_cost_usd, il massimo che viene fatturato):

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

Se il venditore del modello necessita di un'impostazione che hai omesso (un modello da riferimento a video necessita di aspect_ratio), il valore predefinito con cui viene inviato è mostrato nello schema del modello e nel preventivo come defaults_applied, ad esempio "defaults_applied": {"aspect_ratio": "16:9"}.

Una richiesta che verrebbe rifiutata viene rifiutata allo stesso modo, con la motivazione.

Blocca un prezzo per 15 minuti

POST /v1/quote accetta lo stesso corpo della richiesta di immagine, video, musica o parlato e risponde con il costo di listino (cost_usd), quanto viene bloccato mentre è in esecuzione (max_cost_usd, il massimo che viene fatturato) e un quote_id (per una trascrizione, invia model e duration_seconds; una trascrizione di per sé non richiede quote_id, usa max_cost per limitarla):

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

Invia la stessa richiesta con "quote_id": "6f1c…" entro 15 minuti e ti verrà fatturato quanto addebitato dal provider, al massimo max_cost_usd; se ciò che deve essere bloccato all'inizio supera max_cost_usd, viene annullato senza alcun costo. Nulla viene addebitato o bloccato per il preventivo stesso.

  • Un preventivo avvia una richiesta. Usarlo di nuovo risponde 409 quote_used; un lavoro che non ha mai raggiunto un modello restituisce il preventivo.
  • La richiesta deve chiedere la stessa cosa: stesso modello e le stesse impostazioni (prompt, dimensione, durata del clip, immagini sorgente…). Qualsiasi altra cosa viene rifiutata con 400 quote_mismatch. max_cost, metadata e output_upload_url possono differire.
  • Dopo 15 minuti risponde 400 quote_expired. I modelli di chat sono fatturati per token e non hanno preventivo: limitati con provider.max_price.

Modelli di fallback e un limite di prezzo

"models": ["model-b", "model-c"] nomina i modelli da provare in ordine quando il primo non può essere servito in questo momento (no_available_provider) o supera il tuo provider.max_price. Qualsiasi altro rifiuto riceve una risposta immediata. Il model della risposta nomina il modello che l'ha prodotta (una risposta vocale è il file audio stesso: Attività mostra il suo modello). Una richiesta con quote_id non richiede models, e una trascrizione non richiede nessuno dei due.

"provider": {"max_price": 0.05} è il costo massimo che una singola unità (il unit di prezzo del modello: immagine, secondo, megapixel, minuto, 1K caratteri o richiesta) può avere, in dollari USA. Un modello che lo supera viene rifiutato con 400 max_price_exceeded, e la richiesta non viene mai fatturata oltre max_price volte le sue unità. Altre opzioni provider vengono rifiutate con 400 unsupported_routing_option, perché la scelta del venditore per le richieste multimediali non può essere indirizzata per richiesta.

"model": "best-price:image" (o image-edit, text-to-video, image-to-video, music, speech, transcription) seleziona il modello live più economico per il lavoro; fastest: quello misurato più veloce. Vedi Alias dei modelli.

Tag

"metadata": {"project": "spring-ads"} (fino a 16 valori di testo) viene memorizzato con il lavoro, restituito sul suo stato, mostrato in Attività e nell'esportazione CSV, e mai inviato a un modello. Su una trascrizione, inviali come campi del modulo: -F "metadata[project]=spring-ads". Vedi Tag.

Imposta un limite massimo

Invia "max_cost" (dollari USA, ad esempio 0.50) con qualsiasi richiesta di immagine, video, musica o parlato (un campo del modulo su una trascrizione). Se può costare più di così, viene rifiutato con 400 max_cost_too_low e nulla viene addebitato. Per un lavoro video o musicale il limite massimo esatto viene impostato all'inizio del lavoro; se poi supera il tuo max_cost, il lavoro viene annullato senza alcun costo. Con "validate": true, il preventivo aggiunge within_max_cost.

Riprova senza pagare due volte

Invia un header Idempotency-Key (qualsiasi stringa unica fino a 255 caratteri, come un UUID) quando avvii un lavoro video o musicale. Se la tua connessione cade e invii di nuovo la stessa richiesta con la stessa chiave entro 24 ore, ottieni lo stesso lavoro invece di un secondo. La stessa chiave con una richiesta diversa viene rifiutata con 409 idempotency_key_reused. Le richieste di immagine, parlato e trascrizione non accettano una chiave: una che ha fallito non è stata addebitata, quindi inviala di nuovo.

Il tuo 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 è accantonato per le richieste ancora in corso; torna indietro (meno il costo reale) quando terminano. available_usd è ciò che le nuove richieste possono usare.

Opzioni di ogni modello

GET /v1/models?type=image (o video, music, speech, transcription, audio per entrambi, chat, all; diversi uniti da virgole; senza type l'elenco è solo di modelli di chat) elenca i modelli che puoi chiamare subito, ognuno con il suo type, l' endpoints a cui inviarlo e il suo pricing. GET /v1/models/<model id> fornisce un modello, e per un modello multimediale un oggetto parameters: uno schema JSON del corpo della richiesta con le lunghezze dei clip, le risoluzioni, i rapporti d'aspetto e le dimensioni che accetta, e quale immagine sorgente richiede.

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

pricing ha usd (il massimo che un unit costa ora) e list_usd (il prezzo proprio del creatore del modello); un modello di chat ha input_usd, output_usd e i loro prezzi list_ per 1M tokens.

Errori

HTTP codice significato
400 wrong_endpoint ad es. un modello di immagine inviato a /v1/video/generations, un modello di chat inviato a una route di immagine, o un modello vocale inviato a /v1/audio/transcriptions; il messaggio indica il percorso corretto. Non viene addebitato nulla, e l'Attività lo elenca come rifiutato
400 invalid_request la richiesta è errata (il messaggio spiega il motivo, ad es. una durata non offerta dal modello o un'immagine sorgente mancante)
400 image_required una modifica, o un modello da immagine a video o da riferimento a video, senza immagine sorgente
400 max_cost_too_low potrebbe costare più del tuo max_cost; non è stato addebitato nulla
409 idempotency_key_reused quel Idempotency-Key è stato usato per una richiesta diversa
400 quote_mismatch / quote_expired la richiesta non è quella quotata, o la quotazione ha più di 15 minuti
404 quote_not_found nessuna quotazione di questo tipo sul tuo account
409 quote_used quella quotazione ha già avviato una richiesta
400 max_price_exceeded il modello costa di più per unità rispetto al tuo provider.max_price
400 unsupported_routing_option un'opzione provider diversa da max_price
404 file_not_found nessun file caricato di questo tipo sul tuo account, o è scaduto
413 file_too_large un caricamento superiore a 10 MB
429 too_many_files sono già conservati 500 caricamenti; elimina alcuni o attendi che scadano
503 files_unavailable i caricamenti non sono disponibili al momento
413 request_too_large la richiesta supera il limite di dimensione (40 MB); invia le immagini come link https:// e dividi le registrazioni lunghe
402 insufficient_credits il tuo saldo non può coprire il costo massimo del lavoro; il messaggio indica quanto è necessario
404 model_not_found / not_found nessun modello di questo tipo, o nessun lavoro di questo tipo sul tuo account
404 artifact_expired il file non è più conservato (vedi sopra)
429 rate_limited attendi i secondi in Retry-After (pochi secondi se assente), quindi riprova; X-RateLimit-Remaining mostra quante richieste rimangono in questo minuto
503 no_available_provider nessun venditore può farlo al momento; UberLLM ha già provato gli altri. Il messaggio indica i modelli dello stesso tipo che funzionano ora