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

Atualizado em: 18 de junho de 2026