Erros
Cada código de negócio, o que ele significa e quais erros vale a pena tentar de novo.
Quando uma chamada dá errado, a resposta traz um número (code) que diz o tipo de problema e uma frase (msg) para uma pessoa ler. Decida pelo número; mostre ou registre a frase.
O envelope
{ "code": 40201, "msg": "Insufficient balance", "request_id": "req_…" }codeé um código de negócio.200significa sucesso; todo o resto está abaixo.- Em caso de falha não há campo
data, exceto em alguns erros da biblioteca de assets, que acrescentamdata.reason. - O status HTTP também vem certo (
40201chega com HTTP402). Decida por um dos dois, sem misturar — recomendamos ocode, que é mais específico. request_idtambém vai no cabeçalhoX-Request-Id. Informe-o ao falar com o suporte.
Se a resposta nem for JSON — uma página HTML ou de texto da rede no meio do caminho —, trate pelo status HTTP e registre o começo do corpo.
Idioma das mensagens de erro
msg e o errorMessage de uma tarefa com falha vêm em inglês por padrão. Envie Accept-Language com en, ja, ko, de, fr, es, pt-BR, ru, it ou pl, ou defina o idioma de erros da API no console, para recebê-los nesse idioma; os demais voltam ao inglês. Respostas de erro trazem Content-Language. Os códigos nunca mudam com o idioma — mais um motivo para nunca interpretar o msg.
Quando um modelo informa um motivo específico de falha (por exemplo, uma imagem de referência ilegível), o errorMessage repassa esse motivo em inglês, sem detalhes internos.
Códigos gerais
code | HTTP | Significado | Tentar de novo? |
|---|---|---|---|
200 | 200 / 202 | Sucesso | — |
400 | 400 | A requisição ou o input é inválido; o msg aponta o problema | Não — corrija |
40003 | 400 | Um upload não confere com o ticket (tamanho, tipo ou conteúdo) | Não — novo ticket, novo upload |
40004 | 400 | Requisição válida, mas o modelo não atende essa combinação de configurações agora | Não — mude a configuração citada no msg |
401 | 401 | Chave ausente, desconhecida ou revogada (detalhes) | Não |
403 | 403 | Não permitido (veja Permissões) | Não |
404 | 404 | Não existe ou pertence a outra chave | Não |
409 | 409 | Conflito com o estado atual (quando tentar de novo) | Às vezes |
40901 | 409 | Uma cotação expirou ou o preço mudou | Cote de novo |
413 | 413 | Corpo da requisição grande demais | Não — envie a mídia por upload em vez de embutir |
429 | 429 | Requisições demais | Sim — depois de Retry-After |
500 | 500 | Algo quebrou do nosso lado | Sim — com espera crescente |
503 | 503 | Um serviço de apoio ficou indisponível por um instante | Sim — depois de Retry-After |
Saldo e limites
code | HTTP | Significado | O que fazer |
|---|---|---|---|
40201 | 402 | Créditos insuficientes para a reserva desta tarefa | Compre créditos e envie de novo |
40202 | 402 | Um limite de gasto da chave foi atingido | Aumente no console da API ou espere ele zerar |
Permissões
code | HTTP | Significado | O que fazer |
|---|---|---|---|
40301 | 403 | Esta chave não pode chamar este modelo | Adicione o modelo à lista permitida da chave |
40302 | 403 | A requisição veio de um endereço fora da lista de IPs | Adicione o endereço ou tire a restrição |
40303 | 403 | O serviço não é oferecido nesta região | Não há como contornar |
40310 | 403 | O e-mail da conta ainda não foi verificado | Abra o link do e-mail de cadastro; a chave funciona assim que for verificado |
Um 403 simples com chave válida significa que a conta não pode usar a API no momento; fale com o suporte informando o request_id.
Disponibilidade de modelos
code | HTTP | Significado | Tentar de novo? |
|---|---|---|---|
50301 | 503 | O modelo não está aceitando tarefas agora | Sim, mais tarde — ou escolha outro modelo |
40004 | 400 | O modelo funciona, mas não com essas configurações | Não — mude a configuração |
Nenhum dos dois cria tarefa nem reserva créditos.
Códigos de falha de tarefas
Uma tarefa aceita que depois falha tem state: "failed" e um destes valores em errorCode. A lista é fechada: trate qualquer outro valor como upstream_failed.
errorCode | Significado | Tentar de novo? |
|---|---|---|
invalid_request | O modelo não aceitou os parâmetros | Não — corrija |
unsupported_combination | Essa combinação de configurações não é suportada agora | Não — mude uma configuração |
content_rejected | O modelo recusou esse conteúdo | Não — mude o prompt ou a referência |
invalid_asset | Um arquivo de referência não pôde ser lido ou não está mais disponível | Envie de novo e crie outra tarefa |
rate_limited | O serviço de geração estava ocupado | Sim, com espera crescente |
upstream_unavailable | A geração está indisponível por um tempo | Sim, daqui a pouco |
generation_failed | A geração rodou, mas não produziu nada | Talvez — a mesma entrada costuma falhar igual |
timeout | A tarefa ficou sem tempo | Talvez — tente uma saída mais curta ou com menos resolução |
upstream_failed | Todo o resto | Sim, com espera crescente |
Uma tarefa com falha libera a reserva e não custa nada (alguns poucos modelos cobram recusas de conteúdo e avisam na própria página). O errorMessage explica o motivo em palavras; decida só pelo errorCode.
Quando vale repetir um 409
| Caso | Tentar de novo? |
|---|---|
Duas requisições com o mesmo Idempotency-Key chegaram juntas | Sim, com a mesma chave, após uma pausa curta |
O Idempotency-Key já foi usado em outra requisição ou por outra chave | Não |
| Um upload ou um resultado ainda está em processamento | Sim, depois de alguns segundos |
jobs/retry numa tarefa que ainda não terminou | Sim, quando ela estiver final |
jobs/retry numa tarefa que deu certo | Não — crie uma nova tarefa |
| Um ticket de upload ou arquivo enviado expirou | Não — envie de novo |
Escrevendo a nova tentativa
Repita só o que pode dar certo na segunda vez, espere Retry-After quando ele vier e, se não vier, aumente a espera exponencialmente:
const RETRYABLE = new Set([429, 500, 503, 50301]);
async function call(path, init, attempts = 4) {
for (let i = 0; i < attempts; i++) {
const res = await fetch(`https://api.popnow.ai/api/v1${path}`, init);
if (!res.headers.get('content-type')?.includes('json')) {
throw new Error(`HTTP ${res.status}: ${(await res.text()).slice(0, 200)}`);
}
const body = await res.json();
if (body.code === 200) return body.data;
if (!RETRYABLE.has(body.code)) throw new Error(`${body.code} ${body.msg} (${body.request_id})`);
const wait = Number(res.headers.get('retry-after')) * 1000 || 2 ** i * 1000 + Math.random() * 500;
await new Promise((r) => setTimeout(r, wait));
}
throw new Error('out of retries');
}import random, time, requests
RETRYABLE = {429, 500, 503, 50301}
def call(method, path, attempts=4, **kwargs):
for i in range(attempts):
res = requests.request(method, f"https://api.popnow.ai/api/v1{path}", timeout=60, **kwargs)
try:
body = res.json()
except ValueError:
raise RuntimeError(f"HTTP {res.status_code}: {res.text[:200]}")
if body["code"] == 200:
return body["data"]
if body["code"] not in RETRYABLE:
raise RuntimeError(f'{body["code"]} {body["msg"]} ({body.get("request_id")})')
retry_after = res.headers.get("retry-after")
time.sleep(float(retry_after) if retry_after else 2**i + random.random() / 2)
raise RuntimeError("out of retries")Ao repetir createTask, envie sempre o mesmo Idempotency-Key, para que uma requisição que na verdade deu certo não seja executada nem cobrada duas vezes.
Depuração
- Anote o
request_ide ocode. - Procure a tarefa nos logs do console da API, pelo ID da tarefa ou pelo
request_id. - Ainda travado? Fale com o suporte com o
request_id, o horário (UTC) e ocode. Nunca envie sua chave de API.

