Erros da API
Como tratar erros da API FourA.
Formato de Resposta de Erro
A API retorna objetos JSON planos para todos os erros. Não há um objeto error aninhado ou códigos de erro.
{
"error": "Invalid API key"
}
Alguns erros incluem campos adicionais como status, service, retryAfter, current ou limits no nível superior:
{
"error": "Rate limit exceeded",
"status": 429,
"service": "single",
"retryAfter": 5,
"current": { "concurrency": 12, "rpm": 3000 },
"limits": { "maxConcurrency": 500, "maxRpm": 3000 }
}
Rastreando um Request
Cada response da API (sucesso ou erro) inclui um header X-Foura-Request-Id com um UUID para essa chamada. Registre-o do seu lado. Se precisar perguntar ao suporte o que aconteceu com um request específico, esse ID nos permite encontrá-lo.
curl -i -X POST https://eu.api.foura.ai/api/single/ \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"method": "GET", "url": "https://example.com"}'
# HTTP/1.1 200 OK
# X-Foura-Request-Id: 9f1c4e6c-7b2a-4d3e-8a1f-2c9d8e4a3b15
# Content-Type: application/json
# ...
Tipos de Erro
400: Bad Request
O corpo do request não contém os campos obrigatórios, contém valores inválidos ou aponta para um destino que a API se recusa a buscar.
{
"error": "Invalid request body format"
}
O mesmo erro 400 também cobre a proteção contra SSRF. Se a sua url resolver para uma faixa de IP privada, de loopback ou reservada (RFC 5735, RFC 6598, blocos reservados IPv6), o request será rejeitado antes de sair da rede da FourA:
{
"error": "Target <ip> resolves to a private/reserved IP"
}
Correção: Verifique se o seu request inclui todos os campos obrigatórios, se as URLs usam http:// ou https:// e se o host resolve para um endereço público.
401: Unauthorized
Sua API key está ausente ou é inválida.
Chave ausente:
{
"error": "Missing API key. Include X-API-Key header."
}
Chave inválida:
{
"error": "Invalid API key"
}
Correção: Verifique se o seu header X-API-Key contém uma chave válida. Gere uma nova chave no Dashboard, se necessário.
429: Rate Limited
Você enviou requests demais em um curto período.
{
"error": "Rate limit exceeded",
"status": 429,
"service": "single",
"retryAfter": 5,
"current": { "concurrency": 12, "rpm": 3000 },
"limits": { "maxConcurrency": 500, "maxRpm": 3000 }
}
Correção: Aguarde a quantidade de segundos indicada em retryAfter antes de enviar mais requests. Consulte Rate Limits para obter detalhes.
500: Server Error
Ocorreu um erro do nosso lado.
Correção: Tente novamente o request após um curto intervalo. Se o erro persistir, verifique a página de status ou entre em contato com o suporte informando o X-Foura-Request-Id da response que falhou.
503: Service Disabled or At Capacity
Um erro 503 significa que o serviço está temporariamente indisponível para manutenção ou que você atingiu o limite de concorrência. Ambas as responses incluem um campo retryAfter. O formato de concorrência também inclui current e limits.
{
"error": "Service disabled",
"status": 503,
"retryAfter": 60
}
Correção: Aguarde os segundos de retryAfter e tente novamente. A página de status lista as janelas de manutenção ativas.
Falhas no Lado do Destino Dentro de 200 OK
Nem toda falha aparece como um status HTTP não-2xx. Quando o site de destino retorna HTTP 200 com um payload de erro, a FourA ainda entrega o corpo para você, mas classifica o request como application_error. Quando o destino retorna um não-2xx que suas regras de validate não aceitam, o resultado é application_fail e o corpo é entregue sem alterações.
Ambos os casos são faturáveis como se o request tivesse funcionado no nível da rede. A referência de Outcomes cobre a taxonomia completa.
Encoding de Response
A FourA decodifica automaticamente os corpos das responses para UTF-8. Se o destino servir windows-1251, gbk, shift_jis, iso-8859-* ou qualquer outro charset declarado no header Content-Type ou em uma tag HTML <meta charset>, você receberá uma string UTF-8 limpa no campo data (single, proxy) ou body (browser).
Para payloads binários (imagens, protobuf, áudio bruto), defina returnBuffer: true no request. O corpo retorna como um buffer base64 sem nenhuma conversão de charset aplicada.
Estratégia de Retry
Uma política prática de retry:
import time
import requests
def make_request(url, payload, api_key, max_retries=3):
for attempt in range(max_retries):
resp = requests.post(
url,
headers={"X-API-Key": api_key, "Content-Type": "application/json"},
json=payload,
)
if resp.status_code == 200:
return resp.json()
body = resp.json() if resp.headers.get("content-type", "").startswith("application/json") else {}
retry_after = body.get("retryAfter", 2 ** attempt)
request_id = resp.headers.get("X-Foura-Request-Id", "?")
if resp.status_code in (429, 503):
time.sleep(retry_after)
continue
if resp.status_code >= 500:
time.sleep(2 ** attempt)
continue
# 400/401/404 won't fix themselves
raise RuntimeError(f"{resp.status_code} (request {request_id}): {body.get('error')}")
raise RuntimeError(f"Exhausted {max_retries} retries")
Relacionado
- Rate Limits: Detalhes de concorrência e RPM
- Request Outcomes: Explicação dos sete valores de outcome
- Common Issues: Sintomas, causas e correções