Códigos de erro da API de vídeo com IA: o que significam e quando tentar de novo
Jobs de geração de vídeo falham mais do que chamadas de API comuns: os modelos ficam ocupados, recusam prompts ou estouram o tempo limite. Este guia lista todos os erros que você pode receber, quais vale a pena tentar de novo e como fazer isso com segurança.
Dois momentos em que um erro pode aparecer
Um job de vídeo ou imagem pode dar errado em dois momentos, e seu código precisa tratar os dois:
- Ao enviar.
POST /v1/generationsresponde com um status HTTP de erro e um corpo como{"error": {"code": "...", "message": "..."}}. Nada foi iniciado e nada é cobrado. - Durante a execução. O envio funcionou com
202, mas o job termina depois comstatus: "failed". Oerrortraz umcode, umamessageque você pode mostrar a quem fez o pedido eretryable.
Um job que falha nunca é cobrado, em nenhum modelo. O valor é reservado do seu saldo no envio e liberado se o job falhar.
Erros HTTP no envio
| Status | Código | Significado | Tentar de novo? |
|---|---|---|---|
| 400 | invalid_request | O corpo não é JSON, não tem model ou input, ou contém um campo que a API não conhece. | Não. Corrija o corpo. |
| 401 | unauthorized | A chave de API está ausente ou incorreta. | Não. Verifique a chave. |
| 402 | insufficient_credits | O saldo não cobre o valor reservado para o job. | Depois de recarregar. |
| 403 | account_disabled, generation_not_enabled | A conta está pausada, ou a chave consegue consultar preços, mas não gerar. | Não. Fale com o suporte. |
| 404 | not_found | Não existe geração com esse id na sua conta. | Não. |
| 409 | idempotency_conflict | O Idempotency-Key já foi usado com um corpo diferente. | Com uma nova chave. |
| 409 | not_ready | Você pediu o arquivo antes de o job ser concluído com sucesso. | Sim, depois que ele for concluído com sucesso. |
| 413 | body_too_large | O corpo JSON tem mais de 1 MB. Faça upload dos arquivos e envie links. | Não. Reduza o corpo. |
| 422 | invalid_input | Um valor que o modelo não aceita, como uma duração ou um tamanho que ele não lista. A mensagem indica qual. | Não. Altere o valor. |
| 422 | max_cost_exceeded | Todas as rotas custam mais do que o seu max_cost_usd. | Não. Aumente o limite ou reduza as configurações. |
| 422 | model_not_available | Não existe modelo com esse id. | Não. Verifique o id. |
| 429 | rate_limited | Muitas requisições no mesmo segundo vindas de uma conta. | Sim, depois de Retry-After. |
| 429 | concurrency_limit | A conta já tem o número máximo de jobs em execução. | Sim, quando um terminar. |
| 500 | server_error | Algo falhou do nosso lado. | Sim, com o mesmo Idempotency-Key. |
| 503 | busy | Um momento de carga alta. | Sim, depois de Retry-After, com a mesma chave. |
| 503 | model_unavailable | Nenhuma rota deste modelo está disponível agora. | Sim, em instantes. |
A regra geral: 429, 500 e 503 valem uma nova tentativa com backoff; qualquer erro da faixa 4xx, exceto 429, significa que a própria requisição precisa mudar.
Por que um job falha depois de iniciado
| error.code | retryable | O que aconteceu |
|---|---|---|
content_policy | no | O modelo recusou o prompt ou a mídia, ou reteve o resultado. reason informa o motivo: sexual, minors, copyright, real_person ou other. |
invalid_input | no | Não foi possível ler uma referência, ou o modelo rejeitou um valor. A mensagem indica qual. |
model_declined | no | O modelo respondeu com palavras em vez de criar a imagem ou o vídeo. Descreva o próprio resultado no prompt. |
reference_changed | no | Um arquivo de referência mudou depois de ser verificado. Envie-o de novo. |
max_cost_exceeded | no | A única rota capaz de executar custa mais do que o seu limite. |
model_busy | yes | Todas as rotas estavam no limite de capacidade. Tente de novo em um minuto. |
model_unavailable | yes | Nenhuma rota pôde assumir o job agora. |
timeout | yes | Nenhuma rota devolveu um resultado a tempo, ou o seu max_wait_s passou. |
all_routes_failed | yes | Todas as rotas falharam por motivos do lado delas. |
Nos bastidores, um job que cai em uma rota ocupada ou com defeito passa sozinho para a próxima rota. Portanto, quando você vê model_busy ou all_routes_failed, todas as rotas já foram tentadas. O campo attempts informa quantas. Esperar um ou dois minutos antes de tentar de novo costuma bastar.
Recusas de conteúdo
content_policy é a falha mais comum. Quem decide é o modelo, não a API, e o reason indica qual regra foi acionada. As causas mais frequentes são o nome ou o rosto de uma pessoa famosa no prompt ou em uma imagem de referência (real_person), marcas e personagens conhecidos (copyright) e qualquer coisa que pareça envolver um menor. Tentar a mesma requisição de novo não ajuda; reescreva o prompt ou troque a imagem. Os modelos variam em rigor, então o mesmo prompt pode passar em um e ser recusado em outro.
Um loop de retry que faz a coisa certa
Este helper em Python tenta de novo o que vale a pena e para no que não vale. Dois detalhes importam:
- Reenviar uma submissão após um
503ou uma conexão interrompida usa o mesmoIdempotency-Key, então nunca inicia um segundo job cobrado. - Tentar de novo depois que um job falhou usa uma chave nova. A mesma chave só devolveria o job que falhou.
import os
import time
import uuid
import requests
API = "https://api.revana.si/v1"
AUTH = {"Authorization": f"Bearer {os.environ['REVANA_API_KEY']}"}
RETRY_HTTP = {429, 500, 503}
class GenerationError(Exception):
pass
def submit(body):
"""POST a job. Retries 429, 500 and 503 with the same Idempotency-Key, so a retry never starts a second job."""
headers = {**AUTH, "Idempotency-Key": str(uuid.uuid4())}
for attempt in range(6):
r = requests.post(f"{API}/generations", headers=headers, json=body, timeout=60)
if r.ok:
return r.json()
if r.status_code in RETRY_HTTP:
time.sleep(float(r.headers.get("Retry-After", 2 ** attempt)))
continue
err = r.json()["error"] # 400, 401, 402, 403, 409, 413, 422: fix the request, do not retry
raise GenerationError(f"{r.status_code} {err['code']}: {err['message']}")
raise GenerationError("still busy after 6 tries")
def wait(job):
while job["status"] in ("queued", "running"):
time.sleep(5)
job = requests.get(f"{API}/generations/{job['id']}", headers=AUTH).json()
return job
def generate(body, tries=3):
for n in range(tries):
job = wait(submit(body)) # each call uses a new key: a failed job is final
if job["status"] == "succeeded":
return job
err = job["error"]
if not err["retryable"]:
raise GenerationError(f"{err['code']}: {err['message']}")
time.sleep(60 * (n + 1)) # model_busy, timeout and the like: wait, then try again
raise GenerationError(f"gave up after {tries} tries: {err['code']}")
job = generate({
"model": "seedance-2.0-fast",
"input": {"prompt": "Waves roll onto black sand at sunset.", "resolution": "480p", "duration": 5},
"max_cost_usd": 0.3,
})
print(job["output"]["urls"][0], job["cost_usd"])O job de exemplo é um clipe de 5 segundos em 480p com Seedance 2.0 Fast, que custa $0.25 se der certo e nada se falhar. O limite max_cost_usd impede que uma configuração errada custe mais do que o esperado.
Como evitar erros desde o início
- Leia as opções antes de enviar.
GET /v1/modelse a página de modelos listam os tamanhos, as durações e os limites de referência de cada modelo. Um valor fora deles é recusado na hora com422 invalid_input. - Faça upload dos arquivos em vez de embuti-los.
POST /v1/uploadsaceita imagens de até 30 MB, clipes de até 50 MB e áudio de até 15 MB e devolve um link válido por 14 dias, o que mantém o corpo da requisição pequeno. - Verifique seu saldo no código.
GET /v1/balanceantes de um lote grande evita uma sequência de erros402. Os pacotes de créditos estão na página de preços.
Para formas de reduzir o custo dos jobs bem-sucedidos, veja como reduzir o custo da API de vídeo com IA. A referência completa de erros está na documentação; obtenha uma chave de API para testar.
Perguntas frequentes
Sou cobrado quando uma geração de vídeo com IA falha?
Não. O valor fica reservado quando você envia e é liberado se o job falhar, seja qual for o motivo.
Quais erros devo tentar de novo?
No envio, tente de novo 429, 500 e 503 depois do atraso indicado em Retry-After. Em um job que falhou, tente de novo quando error.retryable for true: model_busy, model_unavailable, timeout e all_routes_failed.
O que significa content_policy?
O modelo recusou o prompt ou a mídia, ou reteve o que gerou. O campo reason indica a regra: sexual, minors, copyright, real_person ou other. Altere a requisição em vez de tentar de novo.
Como tentar de novo sem pagar duas vezes?
Envie o header Idempotency-Key e reutilize-o ao reenviar após um timeout ou um 503. Você recebe a mesma geração de volta, não uma segunda.
O que significa 422 invalid_input?
O modelo não aceita um valor que você enviou, como duração, tamanho ou número de imagens de referência. A mensagem indica o campo e o que ele aceita.
Crie sobre uma API que nunca cobra uma falha
Uma chave de API para todos os modelos de vídeo e imagem, com fallback automático entre rotas. Crédito pré-pago a partir de $10, cobrado apenas pelos jobs que dão certo.