Erros e limites
A forma única de erro, o que fazer em cada status e como tratar o 429
Todo erro da API, em qualquer rota e em qualquer status, tem o mesmo corpo. Aprendeu a ler um, leu todos.
{
"code": "INSUFFICIENT_CREDITS",
"extra": { "credits_needed": 121, "current_balance": 40 }
}codeé um identificador estável, em maiúsculas, de um conjunto fechado. É nele que o seuswitchdecide.extrasó aparece quando existe algo para agir: o que faltou, qual campo reprovou, quais ids foram recusados.- Não existe mensagem pronta. O servidor não sabe o idioma de quem usa o seu produto, e texto corrido não dá para tratar em código. Quem escreve a frase é você.
Estado é minúsculo, erro é maiúsculo. status descreve onde algo está (queued, processing, completed, failed); code e error_code descrevem o que deu errado (VIDEO_TOO_LONG, DOWNLOAD_NO_AUDIO).
O que fazer em cada status
| Status | O que significa | O que fazer |
|---|---|---|
400 | O corpo da requisição está errado | Corrija o payload. Repetir igual não adianta |
401 | Chave ausente, revogada ou expirada | Confira o header X-Api-Key |
402 | Créditos insuficientes | Recarregue, ou leia extra.credits_needed |
403 | O vídeo ou a conta não está acessível | Não há o que repetir |
404 | Não existe nesse workspace | Confira o id e o workspace |
409 | Existe, mas o estado não aceita a operação | Espere e tente de novo |
410 | Existiu e expirou | Submeta ou renderize de novo |
422 | Link válido, vídeo que não serve | Tente outra fonte |
429 | Limite de requisições atingido | Espere o Retry-After |
5xx | Erro nosso | Tente de novo com backoff |
A lista completa de code de cada rota está na referência, resposta por resposta.
Tratando erros no seu código
O padrão que cobre tudo: leia o code, decida pelo status se vale repetir.
const RETRIABLE = new Set([429, 500, 502, 503, 504]);
async function call(path, init = {}, attempt = 1) {
const response = await fetch(`https://api.cut.pro/api/v1${path}`, {
...init,
headers: { "X-Api-Key": process.env.CUTPRO_API_KEY, "Content-Type": "application/json" },
});
if (response.ok) return response.json();
const body = await response.json();
if (RETRIABLE.has(response.status) && attempt <= 5) {
// O servidor diz quanto esperar no 429. Nos 5xx, backoff exponencial.
const retryAfter = Number(response.headers.get("retry-after"));
const wait = retryAfter > 0 ? retryAfter : 2 ** attempt;
await new Promise((resolve) => setTimeout(resolve, wait * 1000));
return call(path, init, attempt + 1);
}
throw Object.assign(new Error(body.code), { code: body.code, extra: body.extra, status: response.status });
}E aí o seu produto traduz o code para a língua de quem está olhando:
switch (error.code) {
case "INSUFFICIENT_CREDITS":
show(`Faltam ${error.extra.credits_needed - error.extra.current_balance} créditos.`);
break;
case "VIDEO_TOO_LONG":
show(`Esse vídeo tem ${error.extra.duration}s e o seu plano aceita até ${error.extra.max_duration}s.`);
break;
case "LIVE_STREAM":
show("A transmissão ainda está no ar. Tente quando ela virar vídeo.");
break;
default:
show("Não conseguimos processar agora. Tente de novo em instantes.");
}Limites de requisição
600 requisições por minuto, por chave de API, numa janela fixa de 60 segundos. Toda resposta diz onde você está:
| Header | O que traz |
|---|---|
X-RateLimit-Limit | O teto (600) |
X-RateLimit-Remaining | Quantas chamadas ainda cabem nesta janela |
X-RateLimit-Reset | Unix, em segundos, de quando a janela zera |
Passou do teto, a resposta é 429 com { "code": "RATE_LIMIT_EXCEEDED" } e o header Retry-After em segundos. Respeite-o: tentar na hora só queima a janela seguinte.
POST /clips/info tem um limite próprio e mais apertado, de 10 por minuto, porque cada chamada vai até a plataforma de origem buscar os metadados. Analise uma vez e guarde o id.
O limite é por chave, não por IP: vários processos seus com a mesma chave dividem a mesma cota, e duas chaves atrás do mesmo IP não atrapalham uma à outra.
Isso cabe no polling?
Cabe, com folga. Acompanhar uma submissão a cada 10 segundos gasta 6 requisições por minuto: dá para ter dezenas de jobs em voo ao mesmo tempo. O que estoura o limite é loop apertado sem espera.