# Executar Requests em Paralelo

> URL: https://foura.ai/pt/docs/how-to/run-requests-in-parallel
> Updated: 2026-10-15
> Index: https://foura.ai/llms.txt

## O Que Você Vai Aprender

Como executar um grande lote de requests FourA simultaneamente sem receber erros 429, limitando quantas chamadas você mantém abertas e reagindo a uma recusa em vez de repeti-la.

## Pré-requisitos

- Uma chave de API da FourA ([obtenha uma aqui](https://foura.ai/docs/getting-started/authentication))
- Python 3.9+ com `requests`, ou Node.js 18+

## Os Limites com os Quais Você Está Lidando

Seu plano possui dois limites máximos por endpoint: quantas requests podem ser executadas ao mesmo tempo e quantas podem iniciar por minuto (o Browser tem um limite diário em vez de um limite por minuto). Single, Proxy e Browser têm seus próprios números, e a aba **Limits & Features** em [Usage & Limits](https://foura.ai/docs/dashboard/quota) lista esses valores.

A request que ultrapassa o teto de simultaneidade retorna como HTTP 429 com `X-FourA-Limit: plan_limit_concurrency` e o limite no corpo:

```json
{
  "error": "Concurrency limit reached: your plan allows 50 simultaneous single 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
}
```

A requisição que ultrapassa o teto por minuto retorna com `X-FourA-Limit: plan_limit_rate`, e `retry_after_seconds` informa quanto tempo falta para que mais uma requisição seja aceita (de 1 a 120 segundos).

Ambas as recusas são imediatas. A FourA não coloca a chamada em fila para entregá-la mais tarde, portanto nada é gasto e nada é faturado. Uma chamada recusada não conta para o minuto deslizante, de modo que uma conta que solicita um pouco acima do seu teto ainda obtém o teto total. Uma tempestade de retentativas tem uma resposta própria: quando chamadas recusadas continuam chegando a mais que o dobro do seu teto por minuto, todas as chamadas nesse endpoint são recusadas com `cooldown: true` e `retry_after_seconds: 30`; portanto, pause durante esses 30 segundos. Referência completa dos campos: [Rate Limits](https://foura.ai/docs/api/rate-limits).

Dois pontos importantes que frequentemente passam despercebidos:

- Uma chamada `POST /api/auto/` não ocupa nenhum slot por si só, mas cada subchamada Single, Proxy e Browser que ela faz para você ocupa. Uma chamada auto pode ocupar mais de um slot enquanto sua sequência é executada. (No seu dashboard, a chamada auto é a requisição; as subchamadas são as tentativas.)
- Uma requisição Browser ocupa seu slot pelo tempo que a página leva para renderizar, o que é muito mais longo do que uma requisição Single. Um lote de chamadas browser atinge o teto com menos requisições do que um lote de chamadas single.

## Passo 1: Limite Sua Própria Concorrência

Escolha um número abaixo do teto para o endpoint que você está chamando e mantenha-o. Um pool de workers faz isso em uma única linha:

```python
import os
import concurrent.futures
import requests

API = "https://eu.api.foura.ai/api/single/"
KEY = os.environ["FOURA_API_KEY"]
HEADERS = {"X-API-Key": KEY, "Content-Type": "application/json"}

# Below your plan's Single concurrency, so a slow request never pushes the batch over it.
MAX_IN_FLIGHT = 30

def fetch_one(url):
    resp = requests.post(API, headers=HEADERS, json={"method": "GET", "url": url}, timeout=60)
    return url, resp.status_code, resp.json()

def fetch_all(urls):
    results = []
    with concurrent.futures.ThreadPoolExecutor(max_workers=MAX_IN_FLIGHT) as pool:
        for outcome in concurrent.futures.as_completed(pool.submit(fetch_one, u) for u in urls):
            results.append(outcome.result())
    return results
```

`max_workers` é todo o mecanismo. O pool nunca tem mais chamadas abertas do que isso, portanto o lote pode ter um milhão de URLs e ainda assim permanecer abaixo do limite.

Deixe uma margem de segurança. Se dois processos do seu lado usam a mesma conta, mesmo com chaves de API diferentes, eles compartilham o mesmo teto, então dê a cada um metade. Se um destino rápido permitir que o pool conclua requests mais rápido do que um minuto permite, controle o ritmo dos workers para ficar abaixo do teto por minuto também.

## Passo 2: Use Backoff em Vez de Reenviar

Se uma recusa chegar, a resposta errada é reenviar o lote imediatamente. Cada chamada nele será recusada novamente, e as novas tentativas vão se acumular sobre as requests que já estavam em execução.

Aguarde primeiro. O tempo de espera está no header `Retry-After` e em `retry_after_seconds` no corpo:

```python
import time

# Plan limits that no short wait will clear.
STOP_ON = {"plan_limit_browser_daily", "plan_limit_credits", "plan_limit_bandwidth", "plan_limit_feature", "plan_limit_premium"}

def fetch_one(url, attempts=4):
    for attempt in range(attempts):
        resp = requests.post(API, headers=HEADERS, json={"method": "GET", "url": url}, timeout=60)

        limit = resp.headers.get("X-FourA-Limit")
        if limit in STOP_ON:
            raise RuntimeError(f"stopped by {limit}")

        if resp.status_code not in (429, 503):
            return url, resp.status_code, resp.json()

        header = resp.headers.get("Retry-After")
        if header and header.isdigit():
            wait = int(header)
        else:
            body = resp.json()
            wait = body.get("retry_after_seconds") or body.get("retryAfter") or 2 ** attempt
        time.sleep(wait)

    return url, 429, {"error": "still refused after retries"}
```

Adicione jitter quando você estiver executando muitos workers. Sem isso, todo worker recusado no mesmo segundo acorda no mesmo segundo e é recusado novamente em conjunto.

```python
import random
time.sleep(wait + random.uniform(0, 0.5))
```

## Etapa 3: A mesma coisa em Node

```javascript
const API = 'https://eu.api.foura.ai/api/single/';
const HEADERS = {
  'X-API-Key': process.env.FOURA_API_KEY,
  'Content-Type': 'application/json',
};
const MAX_IN_FLIGHT = 30;

async function fetchOne(url) {
  const resp = await fetch(API, {
    method: 'POST',
    headers: HEADERS,
    body: JSON.stringify({ method: 'GET', url }),
  });
  return { url, status: resp.status, body: await resp.json() };
}

async function fetchAll(urls) {
  const queue = [...urls];
  const results = [];

  async function worker() {
    while (queue.length) {
      results.push(await fetchOne(queue.pop()));
    }
  }

  await Promise.all(
    Array.from({ length: Math.min(MAX_IN_FLIGHT, urls.length) }, worker)
  );
  return results;
}
```

Um número fixo de workers consumindo de uma única fila mantém exatamente essa quantidade de chamadas abertas, qualquer que seja o tamanho do lote.

## Passo 4: Monitore o que você realmente está usando

Abra **Uso e limites** no [dashboard](https://foura.ai/dashboard). Os contadores em tempo real ao lado dos seus limites mostram a concorrência, a taxa por minuto e as requisições de navegador por dia conforme mudam, permitindo dimensionar `MAX_IN_FLIGHT` com base em uma execução real em vez de uma estimativa.

O [Log de atividades](https://foura.ai/docs/dashboard/activity-log) também registra recusas. Uma execução concluída com o número correto de linhas, mas com vários resultados `rate_limit` dispersos, estava limitando a si mesma via rate limit.

## Erros comuns

- **Disparar a lista inteira de uma vez.** `asyncio.gather` em 5.000 URLs, ou `Promise.all` em um array ilimitado, abre 5.000 chamadas. Limite o pool, não a lista.
- **Tentar novamente em paralelo.** Reenviar cada chamada recusada no exato momento da recusa reproduz o pico que causou a falha. Aguarde o `Retry-After` e adicione jitter.
- **Tratar uma franquia diária ou do período como uma simples espera.** `plan_limit_browser_daily` é zerado à meia-noite UTC, `plan_limit_credits` e `plan_limit_bandwidth` no fim do período de faturamento. Interrompa a execução e leia `resets_at` no corpo da resposta quando presente.
- **Contar chamadas auto como apenas um slot cada.** O escalonamento interno do `/api/auto/` faz subchamadas reais, e são elas que contam para os limites máximos.
- **Compartilhar uma chave entre processos sem dividir o orçamento.** Os limites máximos são por conta, não por processo.
- **Dimensionar um único pool para todos os endpoints.** Single, Proxy e Browser têm, cada um, seu próprio número de concorrência. Um pool dimensionado para Single excede o limite menor de Browser.

## Conteúdo relacionado

- [Rate Limits](https://foura.ai/docs/api/rate-limits): Cada formato de recusa e o significado de cada campo
- [Response Headers](https://foura.ai/docs/api/response-headers): `X-FourA-Limit` e `Retry-After`
- [Resultados de requisição](https://foura.ai/docs/api/outcomes): Por que um resultado `rate_limit` nunca é faturado
- [Uso e limites](https://foura.ai/docs/dashboard/quota): Onde encontrar os contadores em tempo real e os números do seu plano
- [Smart Fetch (Auto)](https://foura.ai/docs/guides/smart-fetch): Como uma única chamada auto se divide em várias subchamadas
