NovoConecte qualquer agente ao UberLLM: uma chave, todos os modelos, pelo preço de tabela ou menos.
UberLLM

Imagens, vídeo, música e fala

A chave que você usa para texto também cria imagens, vídeos, música e fala, e transforma gravações em texto, a partir do mesmo saldo. Escolha um modelo na tabela de modelos (ou ?type=image, ?type=music, ?type=audio); a página de cada modelo mostra seu preço, seu tipo e uma requisição funcional.

Você quer Rota Imagem de origem vai em
Uma nova imagem a partir de um prompt POST /v1/images/generations (nenhum)
Uma imagem editada POST /v1/images/edits image (ou image_url, input_images, images)
Um vídeo a partir de um prompt POST /v1/video/generations (nenhum)
Um vídeo que começa a partir de uma imagem POST /v1/video/generations image_url (primeiro quadro); end_image_url para o último quadro
Um vídeo a partir de imagens de referência POST /v1/video/generations input_images com "role": "reference"
Uma resposta de texto sobre uma imagem POST /v1/chat/completions uma parte image_url em messages[].content
Música POST /v1/music/generations (nenhum)
Fala a partir de texto POST /v1/audio/speech (nenhum)
Texto a partir de uma gravação POST /v1/audio/transcriptions (nenhum; a gravação vai em file)

Uma imagem de origem é um link https://, uma URI data: (data:image/png;base64,iVBORw0…) ou base64 simples de um PNG, JPEG ou WebP, até 8 por requisição. As rotas de imagem também aceitam um upload multipart (abaixo). Links devem ser links https:// públicos; links http:// são recusados. Um link que redireciona (por exemplo https://picsum.photos/640/360) é seguido, até 5 vezes e apenas para outros links https:// públicos, e o modelo recebe o link para onde ele aponta. Um link que não leva a uma imagem, ou redireciona para algum lugar que não podemos usar, é recusado com 400 invalid_image_url antes que algo seja retido ou cobrado. Imagens enviadas inline tornam a requisição maior: mantenha-a abaixo de 10 MB, ou envie links.

Em uma mensagem de chat (image_url em messages[].content), um link (https:// ou http://) é aberto pelo UberLLM e enviado ao modelo como uma URL data:, porque a maioria dos modelos de visão aceita apenas isso. Cada imagem pode ter até 20 MB (40 MB por requisição), e os redirecionamentos são seguidos até 5 vezes, apenas para endereços públicos. Modelos Gemini recebem o link como ele é. Um link que não leva a uma imagem é recusado com 400 invalid_image_url, e nada é cobrado.

Criar uma imagem

Imagens são retornadas na resposta.

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 é o custo da imagem (também no cabeçalho X-UberLLM-Cost-USD). Na rara resposta onde o preço ainda não é conhecido, é null. Você paga o que o provedor cobrou, nunca mais do que max_cost_usd, o que foi reservado para isso.

response_format é url por padrão, tanto para /v1/images/generations quanto para /v1/images/edits. Envie "response_format": "b64_json" para obter a imagem dentro da resposta, em vez disso. Quando o modelo retorna a imagem em si, o UberLLM a mantém como um arquivo da sua conta por 24 horas e responde com seu link (https://api.uberllm.dev/v1/files/file_…/content), que também funciona como a imagem de origem para uma edição posterior. Baixe uma URL com a mesma chave:

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

n é 1 para 10.

Editar uma imagem

Use um modelo que edita (seu ID geralmente termina em -edit) e envie a imagem de origem em 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"}'

A mesma imagem pode ser enviada como:

  • "image": "data:image/png;base64,iVBORw0…"
  • "image_url": "https://…"
  • "input_images": ["https://…", "https://…"]: a primeira é a imagem a ser editada, o restante são referências. Uma entrada também pode ser {"url": "…", "role": "reference"}.
  • "images": [{"image_url": "https://…"}], o formato que os SDKs da OpenAI enviam

Base64 simples também funciona, e "input_image" é lido como "image".

Ou faça o upload do arquivo, como na edição de imagem da 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]

Uma edição sem imagem de origem é recusada com 400 image_required. Máscaras (inpainting) não são suportadas.

Faça upload de uma imagem uma vez

Faça upload de uma imagem para /v1/files e use-a em quantas requisições quiser por 24 horas, em vez de enviá-la em cada requisição:

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

Em seguida, coloque o id, ou o url, em qualquer lugar onde uma imagem de origem seja esperada: "image": "file_3f9c…", "image": {"file_id": "file_3f9c…"}, "image_url": "file_3f9c…", uma entrada de input_images, ou o frame inicial de um vídeo. Apenas sua conta pode usar seus arquivos.

  • PNG, JPEG, WebP, GIF, HEIC ou AVIF, até 10 MB cada, no máximo 500 mantidos por conta por vez.
  • Mantidos por 24 horas, depois excluídos. GET /v1/files/{id} descreve um, GET /v1/files/{id}/content o baixa, DELETE /v1/files/{id} o exclui agora.

Criar um vídeo

Um vídeo leva um minuto ou mais, então é uma tarefa: inicie, acompanhe e baixe.

1. Inicie

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 é opcional: modelos de backup são tentados em ordem somente quando o primeiro não tem um provedor gratuito no momento, para que um momento de pico não faça sua solicitação falhar (veja Modelos de fallback). O vídeo acima leva cerca de um minuto e custa cerca de $0.33.

Para começar a partir de uma imagem, use um modelo image-to-video e adicione 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}'

Um modelo que combina duas imagens também recebe end_image_url (o último quadro). Um modelo image-to-video ou reference-to-video que não consegue criar vídeo apenas a partir de texto é recusado com 400 image_required quando a solicitação não tem imagem, antes de qualquer cobrança. Um modelo reference-to-video recebe as imagens em 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}'

Outros campos, quando o modelo os oferece: resolution (480p, 720p, 1080p…), aspect_ratio (16:9, 9:16, 1:1…) e audio (true / false; um modelo sem configuração de som não recebe esse campo e cria o clipe como sempre faz). Um modelo reference-to-video precisa de um formato, então quando você omite aspect_ratio ele é enviado como 16:9 (ou o primeiro formato do modelo, se ele não criar 16:9); o schema do modelo mostra esse padrão. As opções de cada modelo são listadas por GET /v1/models/<model id> (veja Opções de cada modelo).

Cada modelo cria apenas certas durações de clipe, resoluções e formatos. Uma solicitação para um valor que ele não cria é recusada com uma mensagem que lista os valores que ele cria, antes de qualquer cobrança.

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 é o modelo que assumiu a tarefa (um backup de models quando o primeiro não conseguiu). Custos que terminam em _usdc estão em milionésimos de dólar (327250 = $0.33). estimated_cost_usd é o preço listado do que você pediu: o mesmo número que validate e POST /v1/quote retornam como cost_usd. É o preço atual do modelo multiplicado pelos segundos (na resolução solicitada), imagens ou outras unidades. A tarefa é cobrada pelo que o provedor cobrou por ela, o que pode ser mais ou menos. max_cost_usd é o que é reservado do seu saldo pela tarefa: o mesmo valor enquanto ela roda e depois que termina, e o valor máximo que pode ser cobrado pela tarefa. Um modelo de vídeo cujo preço é por second é cobrado por segundo do clipe; um com preço por job custa esse valor por vídeo.

2. Acompanhe a cada 5–10 segundos:

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

HTTP 202 enquanto a tarefa roda, HTTP 200 quando ela termina:

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

O modelo informa a cobrança exata de uma tarefa um pouco depois de o arquivo ficar pronto, normalmente em até 30 segundos. Até lá, uma tarefa succeeded mostra "usage": { "cost_usd": null, "cost_pending": true }; consulte novamente alguns segundos depois. Se a cobrança não chegar em até 30 minutos, a tarefa é cobrada pelo preço listado (estimated_cost_usd) e usage.cost_note informa isso.

3. Baixe

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

Peça para interromper uma tarefa que você não precisa mais: DELETE /v1/video/generations/<job id>. Pedimos ao provedor para interrompê-la; uma tarefa que ele já tinha feito é cobrada normalmente, e uma que ele interrompe termina canceled e não custa nada.

Modelos de vídeo Fal

Os modelos cujo ID começa com fal-ai/ rodam no Fal, abaixo do preço próprio do Fal: você paga o preço do Fal menos o desconto do vendedor Fal mais barato no marketplace agora (15% de desconto em 30 de outubro de 2026; 25% de desconto sempre que esse vendedor estiver vendendo). O preço é fixado pelo que você pede — segundos × a taxa para a resolução, e se o clipe tem som — então o custo do trabalho é conhecido quando ele começa e estimated_cost_usd é exatamente o que é cobrado. Um trabalho falho não custa nada. Parar um é o melhor esforço: se o Fal já fez o clipe, ele é cobrado.

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

Preço próprio do Fal por segundo (o seu é este menos o desconto ao vivo; a página de cada modelo, GET /v1/models e POST /v1/quote mostram o preço exato agora):

Modelo Duração (s) Preço do Fal por segundo, a 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 · com som $0.126
fal-ai/kling-video/v3/pro/text-to-video, …/image-to-video 3–15 $0.112 · com som $0.168
fal-ai/kling-video/o3/standard/text-to-video, …/image-to-video 3–15 $0.084 · com som $0.112
fal-ai/kling-video/v2.6/pro/text-to-video, …/image-to-video 5, 10 $0.07 · com som $0.14
fal-ai/veo3.1/fast, fal-ai/veo3.1/fast/image-to-video 4, 6, 8 720p/1080p $0.10 · com som $0.15 · 4k $0.30
fal-ai/veo3.1, fal-ai/veo3.1/image-to-video 4, 6, 8 720p/1080p $0.20 · com som $0.40 · 4k $0.40
fal-ai/veo3.1/lite 4, 6, 8 720p $0.03 · com som $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 (imagem) $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

Cada preço é próprio do Fal; o desconto é aplicado quando o trabalho começa. Se o vendedor mais barato recusar um trabalho, um mais caro o fará e o trabalho será cobrado pelo preço desse vendedor, nunca mais do que estimated_cost_usd na resposta.

Alguns modelos nos custam menos do que o desconto do vendedor indica. A cada 6 horas comparamos o que o marketplace nos cobrou por cada modelo nos últimos 7 dias com o preço do Fal para os mesmos trabalhos. Um modelo com pelo menos 20 trabalhos nesse período é então precificado com essa participação real mais 5%, quando isso for menor. A página do modelo, /v1/models e a fatura usam o mesmo preço. Se esse valor tiver mais de 3 dias, o desconto do vendedor se aplica novamente.

Os campos são nossos: duration_seconds, resolution, aspect_ratio, audio e image_url (ou input_images). A grafia própria do Fal (duration, generate_audio, start_image_url) também funciona. Kling, Veo e Kling 2.6 adicionam som a menos que você envie "audio": false.

Status do trabalho

status HTTP Significado
queued 202 Aceito, aguardando um modelo para iniciá-lo
running 202 Sendo feito; progress vai de 0 para 1 quando o modelo o reporta
succeeded 200 Concluído; baixe results[].url
failed 200 Não feito. error.message diz o porquê; error.retryable diz se tentar novamente pode funcionar
canceled 200 Parado antes de ser feito
expired 200 Não concluído a tempo

Estes seis são os únicos valores. failed, canceled e expired não custam nada. Um DELETE é um pedido para parar, não uma promessa: um trabalho que o provedor termina de qualquer forma termina succeeded e é cobrado.

Um momento de pico é retentado para você. Quando nenhum provedor está livre para fazer seu vídeo, ou um está muito ocupado no momento, o UberLLM inicia o trabalho novamente por conta própria, até mais duas vezes dentro de 10 minutos. Ele mantém o mesmo job id e o mesmo custo máximo. O trabalho apenas leva um pouco mais de tempo. Você vê failed apenas quando todas as tentativas falharam, e um trabalho falho não custa nada.

Aguardando. Envie callback_url e nós avisaremos quando o trabalho terminar (veja Callbacks), ou consulte a poll URL a cada 5–10 segundos. A maioria dos vídeos leva 1–5 minutos; alguns trabalhos longos ou 4K levam mais tempo. Você não precisa continuar perguntando para a conta ser liquidada: um trabalho que ninguém verifica ainda é concluído e cobrado (ou reembolsado) pelo UberLLM, e aparece em Painel → Atividade.

Callbacks

Adicione callback_url a um trabalho de vídeo ou música: um link https:// em um host público. Quando o trabalho tiver terminado de vez, o UberLLM envia um POST para ele com o mesmo JSON que GET no trabalho retorna.

{ "model": "kling/kling-o3", "prompt": "a red ball bouncing", "duration_seconds": 5,
  "callback_url": "https://your-app.example/uberllm/jobs" }
  • Quando. Uma vez, quando o trabalho tiver terminado e sua fatura for final: succeeded com usage.cost_usd definido (nunca cost_pending), ou failed, canceled ou expired (custo 0). Não é enviado novamente quando o custo chega, porque é enviado apenas depois. Com output_upload_url também, o callback chega depois que o upload é concluído, então output_upload.status já está delivered ou failed.
  • Cabeçalhos. Content-Type: application/json, UberLLM-Event: job.succeeded (ou job.failed, job.canceled, job.expired), UberLLM-Event-Id: <job id>:<status> e UberLLM-Signature: t=<unix seconds>,v1=<hex>.
  • Tentativas. Responda com qualquer 2xx dentro de 15 segundos. Qualquer outra resposta, ou nenhuma resposta, é tentada novamente após 30 segundos, 2 minutos, 10 minutos, 30 minutos e 2 horas, e então desistida. Você ainda pode consultar o trabalho a qualquer momento.
  • Repetições. Uma mensagem pode chegar mais de uma vez (por exemplo, quando sua resposta é perdida). O ID do evento permanece o mesmo, então descarte um que você já tenha processado.
  • Status. O trabalho mostra como foi: "callback": { "status": "delivered", "attempts": 1, "http_status": 200, "delivered_at": 1792540990 }. status é pending, delivered ou failed (com error em palavras simples).
  • O link deve ser https:// em um host público, sem nome de usuário ou senha nele. Ele nunca é exibido de volta e é excluído assim que o callback termina. webhook_url é lido como o mesmo campo. Imagens e fala não o utilizam: eles retornam na própria resposta.

Verifique a assinatura

Toda mensagem é assinada com o segredo de webhook da sua conta. Encontre-o em Dashboard → API keys → Webhook signing secret; ele é criado na primeira vez que você o abre, e Replace secret o troca imediatamente.

v1 é o HMAC-SHA256 hexadecimal de <t>.<raw body>, com chave de todo o segredo (whsec_…). Calcule-o sobre os bytes brutos que você recebeu (antes de analisar o JSON), compare em tempo constante e recuse um t com mais de 5 minutos.

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

Alertas para um webhook

Os e-mails de alerta também podem ir para um webhook: defina um link em Dashboard → Credits → For developers: also send alerts to a webhook, ou PUT /api/v1/billing/alerts {"alerts_webhook_url": "https://…"} (null o desativa). As mensagens são assinadas, retentadas e identificadas da mesma forma que os callbacks. O 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: o saldo caiu abaixo do valor que você definiu para o e-mail de saldo baixo. Enviado uma vez cada vez que cai abaixo; nada é enviado até que você defina um valor.
  • hold.not_released: dinheiro reservado para um trabalho ainda estava retido mais de 15 minutos após o término do trabalho. data.jobs os lista (id, kind, model, ended_at, held_usd) e data.held_usd é o total. O dinheiro ainda é seu e nossa equipe foi informada.

Por quanto tempo os arquivos são mantidos

Baixe um resultado logo depois que ele for gerado:

  • Arquivos de vídeo e música são mantidos por cerca de 3 horas após a conclusão da tarefa.
  • Links de imagem para /v1/media/artifacts/… são mantidos por cerca de 15 minutos; links de imagem para /v1/files/… por 24 horas. Peça por "response_format": "b64_json" para obter a imagem dentro da resposta e guardá-la você mesmo.
  • Imagens que você envia para /v1/files são mantidas por 24 horas.

Depois disso, o download responde 404 artifact_expired.

Envie resultados para seu próprio armazenamento

Adicione output_upload_url a uma tarefa de vídeo ou música: um link https:// que aceita um HTTP PUT, como um link de upload S3, R2 pré-assinado ou do Google Cloud Storage. Quando a tarefa é bem-sucedida, o UberLLM faz o upload do arquivo para lá com seu tipo de conteúdo. Para uma tarefa que gera vários arquivos, envie output_upload_urls, um link por arquivo em ordem (no máximo 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=…" }

O status da tarefa então tem output_upload:

"output_upload": { "status": "delivered", "attempts": 1, "delivered_at": 1792540990,
  "files": [{ "artifact_index": 0, "http_status": 200, "bytes": 5242880 }] }
  • status é pending até o upload ser concluído, então delivered ou failed (com error em palavras simples).
  • Um upload falho é tentado novamente após 30 segundos, 2, 10 e 30 minutos, então marcado failed; o arquivo ainda pode ser baixado de results[].url enquanto é mantido. Torne o link válido por pelo menos uma hora.
  • O link deve ser https:// em um host público, sem nome de usuário ou senha. Ele nunca é exibido de volta e é excluído assim que o upload termina.
  • Imagens não aceitam isso: uma imagem retorna na própria resposta.

Música

Os mesmos três passos em /v1/music/generations. curl -OJ salva a faixa com o nome de arquivo que enviamos.

Fala

O texto entra, um arquivo de áudio retorna como resposta.

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

Você também pode enviar voice, response_format (por exemplo mp3 ou wav) e speed. Cada modelo tem suas próprias vozes (GET /v1/models/{id} as lista). Com best-price:speech ou fastest:speech você não sabe antecipadamente qual modelo é usado, então a voz é ajustada a ele: uma voz que o modelo possui é mantida, os nomes da OpenAI são mapeados para a voz correspondente do modelo onde ele tiver uma (alloy → af_alloy), e qualquer outra voz, ou nenhuma, recebe a primeira voz do modelo. Quando você nomeia um modelo, sua voz é enviada como está. É cobrado por 1K caracteres iniciados de input. A resposta é o arquivo de áudio; seu cabeçalho X-UberLLM-Cost-USD informa o custo, e X-Request-Id é o ID da requisição conforme listado em Atividade. "validate": true, max_cost, metadata, models e provider.max_price funcionam como nas imagens (veja O que você paga).

Converter uma gravação em texto

Envie a gravação como um formulário com 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 é o custo (também no cabeçalho X-UberLLM-Cost-USD, o único lugar para respostas text, srt e vtt). Nossos próprios campos também vão no formulário e nunca são enviados ao modelo: -F max_cost=0.10, -F "metadata[project]=podcast", -F validate=true.

Você também pode enviar language, prompt, temperature e response_format: json (o padrão), ou text, srt ou vtt para receber texto simples de volta. É cobrado por minuto de áudio. Arquivos podem ter até 40 MB. O formato (mp3, wav, ogg, flac, m4a/mp4, webm, aac) é lido da própria gravação, não do nome do arquivo, então um upload chamado tmp123.out funciona.

Com o 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)

O que você paga

  • O preço do que foi feito: o que o provedor cobrou, sem taxa UberLLM. Uma resposta de imagem ou transcrição e um trabalho concluído o carregam como usage.cost_usd (dólares americanos); toda resposta que gerou algo também o carrega no cabeçalho de resposta X-UberLLM-Cost-USD (para fala, o cabeçalho é o único lugar, já que a resposta é o arquivo de áudio). Ele aparece em Painel → Atividade. O restante da retenção é devolvido.
  • Nada por um trabalho que failed, foi canceled ou expired, ou por uma solicitação de imagem, fala ou transcrição que falhe.
  • O vendedor mais barato que faz exatamente o que você pediu. Muitos modelos de imagem e vídeo têm mais de um vendedor; cada solicitação vai para o mais barato que pode fazê-lo com suas configurações, e para outro se aquele não puder aceitá-la. A lista de modelos e cada página de modelo mostram o preço no vendedor mais barato ativo agora.
  • Veja seu saldo e o que está retido com GET /v1/balance (abaixo).

Verifique o preço antes de gastar

Adicione "validate": true a qualquer solicitação de imagem, vídeo, música ou fala (para uma transcrição, envie validate=true como um campo de formulário com a gravação). A solicitação é verificada como se fosse real (modelo, opções, imagens de origem), nada é feito e nada é cobrado, e HTTP 200 diz o que custa ao preço listado (cost_usd, o preço mostrado na página do modelo; você paga o que o provedor cobrou, o que pode diferir) e o que é retido enquanto executa (max_cost_usd, o máximo que é cobrado):

{ "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 o vendedor do modelo precisar de uma configuração que você deixou de fora (um modelo de referência para vídeo precisa de aspect_ratio), o padrão com o qual é enviado é mostrado no esquema do modelo e na cotação como defaults_applied, por exemplo "defaults_applied": {"aspect_ratio": "16:9"}.

Uma solicitação que seria recusada é recusada da mesma forma, com o motivo.

Manter um preço por 15 minutos

POST /v1/quote aceita o mesmo corpo que a solicitação de imagem, vídeo, música ou fala e responde com o custo listado (cost_usd), o que é retido enquanto executa (max_cost_usd, o máximo que é cobrado) e um quote_id (para uma transcrição, envie model e duration_seconds; uma transcrição em si não leva quote_id, use max_cost para limitá-la):

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

Envie a mesma solicitação com "quote_id": "6f1c…" dentro de 15 minutos e ela é cobrada pelo que o provedor cobrou, no máximo max_cost_usd; se o que deve ser retido ao iniciar estiver acima de max_cost_usd, ela é cancelada sem custo. Nada é cobrado ou retido pela cotação em si.

  • Uma cotação inicia uma solicitação. Usá-la novamente responde 409 quote_used; um trabalho que nunca atingiu um modelo devolve a cotação.
  • A solicitação deve pedir a mesma coisa: o mesmo modelo e as mesmas configurações (prompt, tamanho, duração do clipe, imagens de origem …). Qualquer outra coisa é recusada com 400 quote_mismatch. max_cost, metadata e output_upload_url podem diferir.
  • Após 15 minutos, ele responde 400 quote_expired. Modelos de chat são cobrados por token e não têm cotação: limite-os com provider.max_price.

Modelos de fallback e um limite de preço

"models": ["model-b", "model-c"] nomeia modelos para tentar em ordem quando o primeiro não pode ser atendido agora (no_available_provider) ou está acima do seu provider.max_price. Qualquer outra recusa é respondida imediatamente. O model da resposta nomeia o modelo que a fez (uma resposta de fala é o próprio arquivo de áudio: Atividade mostra seu modelo). Uma solicitação com quote_id não leva models, e uma transcrição não leva nenhum dos dois.

"provider": {"max_price": 0.05} é o máximo que uma unidade (o unit de precificação do modelo: imagem, segundo, megapixel, minuto, 1K caracteres ou solicitação) pode custar, em dólares americanos. Um modelo acima disso é recusado com 400 max_price_exceeded, e a solicitação nunca é cobrada acima de max_price vezes suas unidades. Outras opções de provider são recusadas com 400 unsupported_routing_option, porque a escolha do vendedor para solicitações de mídia não pode ser direcionada por solicitação.

"model": "best-price:image" (ou image-edit, text-to-video, image-to-video, music, speech, transcription) escolhe o modelo ativo mais barato para o trabalho; fastest: o mais rápido medido. Veja Aliases de modelo.

Tags

"metadata": {"project": "spring-ads"} (até 16 valores de texto) é armazenado com o trabalho, retornado em seu status, mostrado em Atividade e na exportação CSV, e nunca enviado a um modelo. Em uma transcrição, envie-os como campos de formulário: -F "metadata[project]=spring-ads". Veja Tags.

Defina um teto

Envie "max_cost" (dólares americanos, por exemplo 0.50) com qualquer solicitação de imagem, vídeo, música ou fala (um campo de formulário em uma transcrição). Se puder custar mais do que isso, é recusado com 400 max_cost_too_low e nada é cobrado. Para um trabalho de vídeo ou música, o teto exato é definido quando o trabalho começa; se então exceder seu max_cost, o trabalho é cancelado sem custo. Com "validate": true, a cotação adiciona within_max_cost.

Tentar novamente sem pagar duas vezes

Envie um cabeçalho Idempotency-Key (qualquer string única de até 255 caracteres, como um UUID) ao iniciar um trabalho de vídeo ou música. Se sua conexão cair e você enviar a mesma solicitação novamente com a mesma chave dentro de 24 horas, você receberá o mesmo trabalho de volta em vez de um segundo. A mesma chave com uma solicitação diferente é recusada com 409 idempotency_key_reused. Solicitações de imagem, fala e transcrição não aceitam uma chave: uma que falhou não foi cobrada, então envie-a novamente.

Seu 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 está reservado para solicitações ainda em andamento; ele volta (menos o custo real) quando elas terminam. available_usd é o que novas solicitações podem usar.

Opções de cada modelo

GET /v1/models?type=image (ou video, music, speech, transcription, audio para ambos, chat, all; vários unidos por vírgulas; sem type a lista é apenas de modelos de chat) lista os modelos que você pode chamar agora, cada um com seu type, o endpoints para enviá-lo e seu pricing. GET /v1/models/<model id> fornece um modelo, e para um modelo de mídia um objeto parameters: um JSON Schema do corpo da requisição com os comprimentos de clipe, resoluções, proporções de tela e tamanhos que ele aceita, e qual imagem de origem ele precisa.

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

pricing tem usd (o máximo que um unit custa agora) e list_usd (o preço próprio do criador do modelo); um modelo de chat tem input_usd, output_usd e seus preços list_ por 1M tokens.

Erros

HTTP code significado
400 wrong_endpoint ex.: um modelo de imagem enviado para /v1/video/generations, um modelo de chat enviado para uma rota de imagem, ou um modelo de fala enviado para /v1/audio/transcriptions; a mensagem indica o caminho correto. Nada é cobrado, e a Atividade o lista como recusado
400 invalid_request a requisição está errada (a mensagem diz o porquê, ex.: uma duração que o modelo não oferece ou uma imagem de origem ausente)
400 image_required uma edição, ou um modelo de imagem para vídeo ou de referência para vídeo, sem imagem de origem
400 max_cost_too_low pode custar mais do que seu max_cost; nada foi cobrado
409 idempotency_key_reused esse Idempotency-Key foi usado para uma requisição diferente
400 quote_mismatch / quote_expired a requisição não é a orçada, ou o orçamento tem mais de 15 minutos
404 quote_not_found nenhum orçamento assim na sua conta
409 quote_used esse orçamento já iniciou uma requisição
400 max_price_exceeded o modelo custa mais por unidade do que seu provider.max_price
400 unsupported_routing_option uma opção de provider diferente de max_price
404 file_not_found nenhum arquivo enviado assim na sua conta, ou ele expirou
413 file_too_large um envio com mais de 10 MB
429 too_many_files 500 envios já estão guardados; exclua alguns ou espere que expirem
503 files_unavailable envios não estão disponíveis agora
413 request_too_large a requisição excede o limite de tamanho (40 MB); envie imagens como links https:// e divida gravações longas
402 insufficient_credits seu saldo não pode cobrir o custo máximo do trabalho; a mensagem diz quanto é necessário
404 model_not_found / not_found nenhum modelo assim, ou nenhum trabalho assim na sua conta
404 artifact_expired o arquivo não está mais guardado (veja acima)
429 rate_limited espere os segundos em Retry-After (alguns segundos se estiver ausente), então tente novamente; X-RateLimit-Remaining mostra quantas requisições restam neste minuto
503 no_available_provider nenhum vendedor pode fazer isso agora; UberLLM já tentou os outros. A mensagem nomeia os modelos do mesmo tipo que funcionam agora