Rate Limits
Cada request à API FourA passa por três verificações antes de alcançar uma engine: os limites do seu próprio plano, depois a cota compartilhada da plataforma para o endpoint chamado e, por fim, a cota compartilhada da plataforma para todo o tráfego. Cada verificação pode recusar uma request por si só, e cada uma responde com um body diferente.
As três verificações, em ordem
- Limites do plano. O que o seu próprio plano permite: quais endpoints e parâmetros ele inclui, quantas requests podem ser executadas simultaneamente por endpoint, quantas por minuto, quantas requests de browser por dia e os créditos e largura de banda disponíveis no período de faturamento.
- Limite global da plataforma. Tudo o que o host da API chamado está processando naquele momento, independentemente do endpoint de destino do tráfego. Uma recusa aqui retorna
"service": "api". - Limite da plataforma por endpoint. Tráfego no serviço single, proxy ou browser chamado.
O seu próprio plano é avaliado primeiro, e essa ordem é um contrato, não um detalhe de implementação. As cotas compartilhadas são um recurso comum; portanto, uma request que a plataforma já iria recusar não deve consumi-las no caminho até ser rejeitada. Uma conta enviando muito mais do que seu plano permite é interrompida antes de interferir nos recursos utilizados por outras pessoas.
As verificações 2 e 3 contabilizam o tráfego total da FourA, não o seu. Interprete uma recusa de qualquer uma delas como "A FourA está ocupada", e não como "você enviou requisições demais". A verificação 1 diz respeito apenas à sua conta, e nada mais na plataforma a altera.
Uma recusa de qualquer uma das verificações compartilhadas devolve à sua conta tudo o que a admissão contabilizou, tanto o bucket por minuto quanto o slot diário de browser, porque a request nunca alcançou um backend. Ela também não conta para a pausa de retry descrita em Requests por minuto: a capacidade da FourA a recusou, não o seu plano.
O POST /api/auto/ não ocupa um slot próprio. As subchamadas de Single, Proxy e Browser feitas por ele para você passam por todas as três verificações como qualquer outra request, portanto, um lote paralelo de chamadas auto consome o limite do seu plano por meio de suas subchamadas. (A contagem de requests e a taxa de sucesso contabilizam a chamada auto em si, uma vez; as subchamadas são exibidas como tentativas dela.)
Limites do plano
Um limite de plano responde com um header X-FourA-Limit indicando qual limite recusou a chamada. O mesmo código está no body sob reason, para que você possa criar condicionais sem ler os headers. Todo body de limite de plano inclui error, reason e documentation; o restante dos campos depende do limite.
X-FourA-Limit |
Status | O que se esgotou |
|---|---|---|
plan_limit_feature |
403 | O endpoint chamado ou o parâmetro exitCountries não está no seu plano |
plan_limit_premium |
403 | exitClass: premium não está no seu plano |
plan_limit_concurrency |
429 | Requests simultâneos nesse endpoint |
plan_limit_rate |
429 | Requests por minuto nesse endpoint |
plan_limit_browser_daily |
429 | Requests de browser no dia |
plan_limit_credits |
429 | Créditos cobrados no período de faturamento |
plan_limit_bandwidth |
429 | Largura de banda no período de faturamento |
Os números por trás de cada limite são do seu plano, e a aba Limits & Features de Usage & Limits lista todos eles ao lado do seu consumo em tempo real. Não defina esses valores no código: cada recusa traz o limite máximo que a originou.
Um request recusado não consome nada. O resultado é rate_limit, e apenas success é faturado.
Endpoint ou parâmetro não incluído no plano
Um 403 com plan_limit_feature indica que a chamada solicitou algo não incluído no plano. A verificação ocorre antes de qualquer contagem, portanto a chamada recusada não afeta seu rate limit nem seus contadores diários.
{
"error": "The browser endpoint is not included in your plan (Free). Upgrade to use it.",
"reason": "plan_limit_feature",
"documentation": "https://foura.ai/prices"
}
O mesmo código e status respondem a uma chamada POST /api/proxy/ que define exitCountries em um plano sem geo targeting. A string error nomeia o parâmetro:
{
"error": "Geo targeting (the exitCountries parameter) is not included in your plan (Free). Remove the parameter or upgrade.",
"reason": "plan_limit_feature",
"documentation": "https://foura.ai/prices"
}
plan_limit_premium tem o mesmo formato para exitClass: premium em um plano sem saídas premium. O FourA pode, em vez disso, atender a essa request usando o pool padrão e retornar exitClass: standard na response, portanto trate ambas as respostas. Nenhuma delas consome uma saída premium. Consulte exitClass.
Nenhum 403 define Retry-After. Aguardar não altera a resposta.
Simultaneous requests
A simultaneidade é contabilizada por endpoint: seu plano possui um limite para Single, um para Proxy e um para Browser. A request que ultrapassar o limite retorna como 429 com Retry-After: 1:
{
"error": "Concurrency limit reached: your plan allows 50 simultaneous proxy request(s). Retry when an in-flight request finishes.",
"reason": "plan_limit_concurrency",
"documentation": "https://foura.ai/prices",
"limit": 50,
"in_flight": 51,
"retry_after_seconds": 1
}
in_flight conta a request recusada também, portanto lê pelo menos um a mais do que limit.
A solução é limitar seu próprio paralelismo em vez de tentar novamente com mais intensidade. Responder a um 429 reenviando o mesmo lote imediatamente gera outro 429 para cada chamada nele. Consulte Executar Requests em Paralelo para ver um padrão prático.
Requests por minuto
Single e Proxy possuem um limite por minuto, medido ao longo de um minuto deslizante. Apenas requests aceitas contam para esse limite: uma request recusada é removida da contagem, portanto uma conta que solicita constantemente um pouco acima do seu limite recebe o equivalente ao seu limite, em vez de ter quase tudo recusado.
{
"error": "Rate limit reached: your plan allows 600 single requests per minute. Cool down and retry.",
"reason": "plan_limit_rate",
"documentation": "https://foura.ai/prices",
"limit_per_minute": 600,
"current_rate": 613,
"retry_after_seconds": 17
}
retry_after_seconds indica quanto tempo levará até que mais uma request seja aceita, caso você não envie mais nada nesse intervalo: no mínimo 1 segundo e no máximo 120. O header Retry-After contém o mesmo valor.
Tentar novamente requests recusadas mais rápido do que isso segue uma regra própria. Quando as requests recusadas por este limite no minuto móvel ultrapassarem o dobro da cota permitida, a chamada será recusada com uma pausa de 30 segundos:
{
"error": "Rate limit cooldown: 1250 requests over your plan's 600 single requests per minute were refused in the last minute and kept arriving. Pause for 30 seconds.",
"reason": "plan_limit_rate",
"documentation": "https://foura.ai/prices",
"limit_per_minute": 600,
"current_rate": 540,
"refused_last_minute": 1250,
"cooldown": true,
"retry_after_seconds": 30
}
Recusas durante a pausa não são contabilizadas, portanto a pausa termina por conta própria com o avançar do minuto, mesmo para um cliente que continua tentando novamente. Para diferenciar a pausa do limite normal, leia cooldown em vez do texto de error.
Requests de browser por dia
O Browser não tem um limite por minuto. O limite do seu plano é um número de requests de browser por dia, contados a partir da meia-noite UTC, e o medidor contabiliza todos os requests de browser admitidos, não apenas os bem-sucedidos.
{
"error": "Daily browser limit reached: your plan allows 300 browser requests per day (resets at midnight UTC).",
"reason": "plan_limit_browser_daily",
"documentation": "https://foura.ai/prices",
"limit_per_day": 300,
"used_today": 301
}
Esta recusa não contém retry_after_seconds e nenhum header Retry-After, pois a espera é de horas em vez de segundos. Trate isso como uma interrupção e agende a próxima execução para a meia-noite UTC.
Créditos para o período de faturamento
Apenas créditos faturados contam, o que significa apenas requests bem-sucedidas. Quando o total faturado atinge os créditos disponíveis para você neste período, novas requests são recusadas até que o período seja redefinido ou você compre mais.
{
"error": "Credit limit reached: 75,000 of 75,000 credits used this billing period. Upgrade your plan or wait for the reset.",
"reason": "plan_limit_credits",
"documentation": "https://foura.ai/prices",
"used": 75000,
"hard_stop": 75000,
"retry_after_seconds": 86400,
"resets_at": "2026-10-01T00:00:00.000Z"
}
hard_stop é a contagem de créditos faturados na qual as requests são interrompidas neste período. Leia este valor a partir do body em vez de calculá-lo: ele já inclui os créditos que você comprou além do plano.
Largura de banda para o período de faturamento
Planos que possuem um limite de largura de banda recusam requests assim que o tráfego padrão atinge esse limite no período. O tráfego premium tem sua própria franquia e não conta para este limite. A largura de banda comprada conta da mesma forma que a largura de banda incluída, e a string error informa o que está disponível para você, não apenas o que o plano inclui.
{
"error": "Bandwidth limit reached: 50 GB is available to you this billing period.",
"reason": "plan_limit_bandwidth",
"documentation": "https://foura.ai/prices",
"used_bytes": 53687091200,
"limit_bytes": 53687091200,
"retry_after_seconds": 86400,
"resets_at": "2026-10-01T00:00:00.000Z"
}
Em ambos os limites de período, retry_after_seconds é limitado a 24 horas; resets_at é o instante exato em que o período é renovado.
Plan limit fields
| Field | Type | Present on | Description |
|---|---|---|---|
error |
string | all | Mensagem legível para humanos, incluindo o limite aplicado a você |
reason |
string | all | plan_limit_ mais o nome do limite. Mesmo valor do header X-FourA-Limit. |
documentation |
string | all | Link para a página de planos |
retry_after_seconds |
number | concurrency, rate, credits, bandwidth | Quanto tempo esperar. Mesmo valor do header Retry-After. |
limit |
number | concurrency | Requests simultâneos que o plano permite nesse endpoint |
in_flight |
number | concurrency | Requests em execução nesse endpoint para a sua conta, incluindo o que foi recusado |
limit_per_minute |
number | rate | Requests por minuto que o plano permite nesse endpoint |
current_rate |
number | rate | Requests contabilizados no minuto deslizante, incluindo o que foi recusado |
refused_last_minute |
number | rate pause | Requests recusados pelo limite por minuto no minuto deslizante. Apenas na pausa de 30 segundos. |
cooldown |
boolean | rate pause | true na pausa de 30 segundos por tentar novamente rápido demais. Ausente em uma recusa comum por minuto. |
limit_per_day |
number | browser daily | Requests de navegador que o plano permite por dia |
used_today |
number | browser daily | Requests de navegador contabilizados hoje, incluindo o que foi recusado |
used |
number | credits | Créditos faturados até o momento neste período |
hard_stop |
number | credits | Créditos faturados em que os requests são interrompidos neste período |
used_bytes |
number | bandwidth | Tráfego padrão até o momento neste período, em bytes. O tráfego premium não está incluído. |
limit_bytes |
number | bandwidth | Bytes disponíveis neste período |
resets_at |
string | credits, bandwidth | Timestamp ISO 8601 do fim do período |
Os limites do plano usam retry_after_seconds. Os limites da plataforma abaixo usam retryAfter. Um helper de retry precisa ler ambos, ou ler o header Retry-After, que apenas os limites do plano definem.
Platform Limits
As verificações da plataforma monitoram dois itens por serviço e mais um em todos eles:
- Concurrency: quantos requests o FourA está executando ao mesmo tempo.
- RPM: quantos requests o FourA recebeu nos últimos 60 segundos.
Ambos os contadores são compartilhados por todos os usuários desse serviço. current e limits nas respostas abaixo descrevem a plataforma, não a sua conta. Se você quiser o seu próprio número, leia in_flight a partir de uma resposta de limite do plano, ou abra Usage & Limits no dashboard.
429: RPM Exceeded
{
"error": "Rate limit exceeded",
"status": 429,
"service": "single",
"retryAfter": 5,
"current": {
"concurrency": 12,
"rpm": 3000
},
"limits": {
"maxConcurrency": 500,
"maxRpm": 3000
}
}
O serviço atingiu o limite de requests permitidas no último minuto. Aguarde retryAfter segundos.
503: Concorrência Excedida
{
"error": "Service at capacity",
"status": 503,
"service": "proxy",
"retryAfter": 2,
"current": {
"concurrency": 500,
"rpm": 1200
},
"limits": {
"maxConcurrency": 500,
"maxRpm": 3000
}
}
O serviço está executando o número máximo permitido de requests simultâneos. Isso se normaliza em segundos.
Serviço Desativado
Quando um serviço é temporariamente colocado offline para manutenção, a API retorna 503 com uma mensagem de erro diferente:
{
"error": "Service disabled",
"status": 503,
"service": "single",
"retryAfter": 60,
"current": { "concurrency": 0, "rpm": 0 },
"limits": { "maxConcurrency": 500, "maxRpm": 3000 }
}
Isso não é um rate limit. O serviço está temporariamente indisponível. Verifique o valor de retryAfter e tente novamente após esse número de segundos. Isso normalmente se resolve em minutos.
Ambos os formatos de 503 trazem as mesmas chaves, portanto faça a ramificação com base na string error e nunca em quais campos estão presentes. Service disabled é manutenção, Service at capacity é concorrência.
No formato de manutenção, current.concurrency e current.rpm são sempre 0: a request foi recusada antes de qualquer medição.
Campos de Limite da Plataforma
| Campo | Tipo | Descrição |
|---|---|---|
error |
string | Mensagem de erro legível para humanos |
status |
number | Código de status HTTP (429 ou 503) |
service |
string | Qual serviço recusou a chamada: single, proxy, browser ou api |
retryAfter |
number | Tempo de espera recomendado em segundos antes de tentar novamente |
current.concurrency |
number | Requests que o serviço estava executando em toda a plataforma quando recusou |
current.rpm |
number | Requests que o serviço recebeu em toda a plataforma nos últimos 60 segundos |
limits.maxConcurrency |
number | Limite de concorrência do serviço em toda a plataforma |
limits.maxRpm |
number | Limite por minuto do serviço em toda a plataforma |
Tratando Todas as Recusas com um Único Helper
Retry-After é definido nos limites de plano pelos quais vale a pena esperar, retry_after_seconds está em seus corpos e retryAfter está nos corpos da plataforma. Leia todos os três nessa ordem e pare nos limites de plano que nenhuma espera resolverá:
import time
import requests
# Plan limits that a short wait never clears.
STOP_ON = {
"plan_limit_feature",
"plan_limit_premium",
"plan_limit_browser_daily",
"plan_limit_credits",
"plan_limit_bandwidth",
}
def wait_seconds(resp, attempt):
header = resp.headers.get("Retry-After")
if header and header.isdigit():
return int(header)
try:
body = resp.json()
except ValueError:
return 2 ** attempt
return body.get("retry_after_seconds") or body.get("retryAfter") or 2 ** attempt
def fetch(url, api_key, max_retries=5):
for attempt in range(max_retries):
resp = requests.post(
"https://eu.api.foura.ai/api/single/",
headers={"X-API-Key": api_key, "Content-Type": "application/json"},
json={"method": "GET", "url": url},
)
limit = resp.headers.get("X-FourA-Limit")
if limit in STOP_ON:
raise RuntimeError(f"stopped by plan limit {limit}: {resp.json().get('error')}")
if resp.status_code in (429, 503):
time.sleep(wait_seconds(resp, attempt))
continue
return resp
raise RuntimeError("Max retries exceeded")
Uma franquia diária não retorna por horas, e uma franquia de período não retorna por dias, portanto, trate-as como uma parada e não como uma espera. Leia resets_at do body se você quiser agendar a próxima execução.
Dicas
- Limite o número de requests em andamento em vez de tentar novamente um lote recusado. Uma tempestade de retentativas transforma um 429 em muitos.
- Leia
X-FourA-Limitprimeiro. Ele informa em uma única string se o limite é seu ou da plataforma, e nenhuma recusa da plataforma o define. - Não defina números fixos no código. Cada response de limite do plano informa o teto que o recusou, e Usage & Limits mostra todos eles.
retryAfternos limites da plataforma é fixo por tipo: 2 segundos para simultaneidade, 5 para RPM, 60 para manutenção.- Faça a correspondência em
errorpara diferenciar os dois 503s. Ambos os formatos contêmcurrentelimits, portanto, uma verificação de "esses campos estão presentes?" interpreta manutenção como um problema de simultaneidade. - Um 403 com
X-FourA-Limitrefere-se ao seu plano, não ao site de destino. O destino nunca respondeu.
A Porta do Proxy Tem Seus Próprios Números
Tudo acima refere-se à API JSON. O tráfego enviado através de proxy.foura.ai segue um conjunto separado de números do plano, em uma unidade diferente: túneis abertos simultaneamente, aberturas de túnel por minuto e o tráfego padrão para o período de faturamento. Essas recusas chegam como um status HTTP com um header X-Foura-Error em vez de um body JSON, porque um CONNECT não tem body para incluir um. Consulte Proxy Port para a tabela de status e How Your Plan Is Metered para saber de qual pool os gigabytes da porta são consumidos.
Relacionado
- Run Requests in Parallel: Um padrão prático de simultaneidade limitada
- Usage & Limits: Todos os limites do plano ao lado do seu uso em tempo real
- API Endpoints: Referência completa de parâmetros
- Error Handling: Todos os tipos de erro e responses
- Response Headers:
X-FourA-Limit,Retry-Aftere o restante - Troubleshooting: Problemas comuns e correções