# Como Implementar Rate Limiting e Exponential Backoff ao Consumir APIs de CPF

> Aprenda a implementar rate limiting e exponential backoff ao consumir APIs de CPF. Guia com exemplos em Python e JavaScript para produção.

**Publicado:** 14/01/2025
**Autor:** Redação CPFHub.io
**URL:** https://cpfhub.io/blog/rate-limiting-exponential-backoff-apis-cpf

---


Rate limiting no cliente e exponential backoff são padrões complementares que tornam seu sistema resiliente ao consumir APIs de CPF: o rate limiter controla o ritmo das requisições para preservar sua cota mensal, enquanto o backoff exponencial com jitter garante recuperação elegante em falhas de rede ou indisponibilidade temporária do servidor.

## Introdução

Ao integrar a API de CPF do CPFHub.io, implementar **rate limiting** no lado do cliente e **exponential backoff** para retentativas são práticas essenciais que preservam sua cota de consultas, reduzem erros transitórios e tornam seu sistema mais resiliente. Essas técnicas são padrões de engenharia aplicáveis a qualquer API REST e ficam ainda mais relevantes quando o volume de consultas impacta diretamente o faturamento.

---

## Entendendo rate limiting do lado do servidor

Antes de implementar o controle do lado do cliente, é importante entender como APIs comunicam seus limites através de headers de resposta.

| Header | Significado | Exemplo |
|--------|-----------|---------|
| X-RateLimit-Limit | Requisições permitidas por janela | 100 |
| X-RateLimit-Remaining | Requisições restantes na janela | 47 |
| X-RateLimit-Reset | Timestamp de reset da janela | 1704110400 |
| Retry-After | Segundos para esperar após erro | 30 |

```python
# Python - Lendo headers de rate limit
import requests
import time

def consultar_com_awareness(cpf: str, api_key: str) -> dict:
 response = requests.get(
 f"https://api.cpfhub.io/cpf/{cpf}",
 headers={"x-api-key": api_key}
 )

 remaining = int(response.headers.get("X-RateLimit-Remaining", 100))
 reset_time = int(response.headers.get("X-RateLimit-Reset", 0))

 if remaining < 10:
 wait_time = max(0, reset_time - int(time.time()))
 print(f"Atenção: apenas {remaining} requisições restantes. "
 f"Reset em {wait_time}s")

 response.raise_for_status()
 return response.json()
```

- **X-RateLimit-Remaining** -- monitore este header para desacelerar proativamente antes de atingir o limite
- **Retry-After** -- em caso de erro de servidor, respeite este valor antes de tentar novamente
- **Reset timestamp** -- permite calcular exatamente quando a janela de rate limit reinicia

---

## Implementando rate limiter no cliente (Python)

Controlar a taxa de requisições do lado do cliente preserva sua cota mensal e melhora a previsibilidade do processamento.

```python
import time
import threading
from collections import deque

class RateLimiter:
 def __init__(self, max_requests: int, window_seconds: float):
 self.max_requests = max_requests
 self.window = window_seconds
 self.timestamps = deque()
 self.lock = threading.Lock()

 def acquire(self):
 with self.lock:
 now = time.monotonic()

 # Remover timestamps fora da janela
 while self.timestamps and self.timestamps[0] <= now - self.window:
 self.timestamps.popleft()

 if len(self.timestamps) >= self.max_requests:
 wait_time = self.timestamps[0] + self.window - now
 time.sleep(wait_time)
 return self.acquire()

 self.timestamps.append(now)

class CPFClientComLimite:
 def __init__(self, api_key: str, max_rps: int = 10):
 self.api_key = api_key
 self.limiter = RateLimiter(max_requests=max_rps, window_seconds=1.0)
 self.session = requests.Session()
 self.session.headers["x-api-key"] = api_key

 def consultar(self, cpf: str) -> dict:
 self.limiter.acquire()
 response = self.session.get(
 f"https://api.cpfhub.io/cpf/{cpf}",
 timeout=15
 )
 response.raise_for_status()
 return response.json()

 def consultar_lote(self, cpfs: list) -> list:
 resultados = []
 for i, cpf in enumerate(cpfs):
 try:
 resultado = self.consultar(cpf)
 resultados.append({"cpf": cpf, "dados": resultado, "erro": None})
 except Exception as e:
 resultados.append({"cpf": cpf, "dados": None, "erro": str(e)})

 if (i + 1) % 50 == 0:
 print(f"Processados {i + 1}/{len(cpfs)} CPFs")

 return resultados
```

| Configuração | Valor | Efeito |
|-------------|-------|--------|
| max_rps=10 | 10 req/s | Conservador, nunca atinge rate limit |
| max_rps=50 | 50 req/s | Moderado, monitora remaining |
| max_rps=100 | 100 req/s | Agressivo, precisa de backoff |

- **Sliding window** -- controle baseado em janela deslizante é mais preciso que contadores simples
- **Thread-safe** -- o lock garante que o rate limiter funcione corretamente em ambientes multi-thread
- **Processamento em lote** -- o rate limiter gerencia automaticamente o ritmo das requisições

---

## Implementando rate limiter no cliente (JavaScript)

Em JavaScript, o rate limiter precisa funcionar com a event loop assíncrona.

```javascript
class RateLimiter {
 constructor(maxRequests, windowMs) {
 this.maxRequests = maxRequests;
 this.windowMs = windowMs;
 this.timestamps = [];
 this.queue = [];
 }

 async acquire() {
 return new Promise((resolve) => {
 this.queue.push(resolve);
 this.#process();
 });
 }

 #process() {
 const now = Date.now();
 this.timestamps = this.timestamps.filter(
 (t) => t > now - this.windowMs
 );

 while (this.queue.length > 0 && this.timestamps.length < this.maxRequests) {
 this.timestamps.push(now);
 const resolve = this.queue.shift();
 resolve();
 }

 if (this.queue.length > 0) {
 const oldestTimestamp = this.timestamps[0];
 const waitTime = oldestTimestamp + this.windowMs - now;
 setTimeout(() => this.#process(), Math.max(waitTime, 10));
 }
 }
}

class CPFClientComLimite {
 constructor(apiKey, maxRPS = 10) {
 this.apiKey = apiKey;
 this.limiter = new RateLimiter(maxRPS, 1000);
 this.baseUrl = "https://api.cpfhub.io/cpf";
 }

 async consultar(cpf) {
 await this.limiter.acquire();

 const response = await fetch(`${this.baseUrl}/${cpf.replace(/\D/g, "")}`, {
 headers: { "x-api-key": this.apiKey },
 });

 if (!response.ok) {
 throw new Error(`HTTP ${response.status}`);
 }

 return response.json();
 }

 async consultarLote(cpfs) {
 const resultados = [];
 for (const cpf of cpfs) {
 try {
 const dados = await this.consultar(cpf);
 resultados.push({ cpf, dados, erro: null });
 } catch (error) {
 resultados.push({ cpf, dados: null, erro: error.message });
 }
 }
 return resultados;
 }
}
```

- **Promise queue** -- requisições aguardam sua vez em uma fila quando o limite é atingido
- **setTimeout** -- agenda a próxima verificação para quando a janela reiniciar
- **Processamento sequencial** -- em lote, cada requisição respeita o rate limit automaticamente

---

## Exponential backoff com jitter

Quando um retry é necessário por falha de rede ou erro de servidor, o backoff exponencial com jitter distribui as retentativas de forma inteligente, segundo as [boas práticas de retry recomendadas pela AWS](https://aws.amazon.com/builders-library/timeouts-retries-and-backoff-with-jitter/).

```python
import random
import time

def exponential_backoff_retry(
 func,
 max_tentativas: int = 5,
 base_delay: float = 1.0,
 max_delay: float = 60.0,
 jitter_strategy: str = "full"
):
 for tentativa in range(max_tentativas):
 try:
 return func()
 except Exception as e:
 if tentativa == max_tentativas - 1:
 raise

 # Calcular delay com backoff exponencial
 delay = min(base_delay * (2 ** tentativa), max_delay)

 # Aplicar jitter
 if jitter_strategy == "full":
 delay = random.uniform(0, delay)
 elif jitter_strategy == "equal":
 delay = delay / 2 + random.uniform(0, delay / 2)
 elif jitter_strategy == "decorrelated":
 delay = random.uniform(base_delay, delay * 3)

 print(f"Tentativa {tentativa + 1} falhou: {e}. "
 f"Aguardando {delay:.1f}s")
 time.sleep(delay)

# Uso com consulta de CPF
client = CPFClientComLimite(api_key="sua-chave", max_rps=10)

resultado = exponential_backoff_retry(
 lambda: client.consultar("12345678909"),
 max_tentativas=3,
 base_delay=1.0
)
```

| Estratégia de jitter | Fórmula | Quando usar |
|---------------------|---------|-------------|
| **Full jitter** | random(0, delay) | Padrão recomendado pela AWS |
| **Equal jitter** | delay/2 + random(0, delay/2) | Quando precisa de delay mínimo |
| **Decorrelated** | random(base, delay*3) | Cenários de alta contenção |
| **Sem jitter** | delay fixo | Nunca em produção |

- **Full jitter** -- a estratégia mais recomendada, distribui retentativas uniformemente
- **max_delay** -- cap no delay máximo para evitar esperas absurdas após muitas falhas
- **Decorrelated jitter** -- cada retry é independente do anterior, reduzindo sincronização

---

## Perguntas frequentes

### A API da CPFHub.io retorna HTTP 429 quando o limite de consultas é atingido?

Não. A API da CPFHub.io **nunca retorna HTTP 429 nem bloqueia requisições**. Ao ultrapassar as 50 consultas mensais do plano gratuito (ou as 1.000 do plano Pro), cada consulta adicional é cobrada a R$ 0,15 automaticamente — o serviço continua disponível sem interrupção. O rate limiting descrito neste artigo é um padrão *client-side* para você controlar o ritmo das suas requisições e gerenciar sua cota, não uma resposta a bloqueios do servidor.

### Por que implementar rate limiting no cliente se a API não bloqueia?

Porque o controle do lado do cliente serve a dois propósitos práticos: (1) distribuir o consumo ao longo do mês para evitar cobranças inesperadas de excedente, e (2) proteger sua infraestrutura de picos acidentais em processamentos em lote que poderiam gerar centenas de requisições em segundos. Trata-se de higiene de engenharia, não de uma obrigação imposta pelo servidor.

### Quando o exponential backoff deve ser usado ao chamar a API de CPF?

O backoff exponencial é indicado para lidar com falhas de rede, timeouts e erros de servidor (5xx) — cenários transitórios onde a requisição pode ter sucesso em uma nova tentativa após breve espera. Para erros de CPF não encontrado (`success: false`) ou autenticação inválida (401), o retry não resolve: é necessário corrigir o dado ou a chave de API.

### Qual é a latência esperada da API da CPFHub.io em produção?

A API da CPFHub.io responde em aproximadamente 900ms em condições normais. Ao configurar timeouts, use valores entre 10 e 15 segundos para absorver variações de rede sem tratar respostas legítimas como falhas. Com um rate limiter a 10 req/s, um lote de 1.000 CPFs processa em cerca de 100 segundos mais a latência de rede.

### Leia também

- [SLA de API de CPF: níveis de disponibilidade e o que exigir do seu provedor](https://cpfhub.io/blog/sla-api-cpf-niveis-disponibilidade)
- [Como validar CPF no frontend com React e API REST](https://cpfhub.io/blog/como-validar-cpf-no-frontend-com-react-e-api-rest)
- [10 erros mais comuns ao integrar uma API de CPF e como evitá-los](https://cpfhub.io/blog/10-erros-mais-comuns-ao-integrar-uma-api-de-cpf)
- [API de CPF grátis para desenvolvedores: como começar em 5 minutos](https://cpfhub.io/blog/api-cpf-gratis-desenvolvedores-comecar-5-minutos)

---

## Conclusão

Rate limiting no cliente e exponential backoff são técnicas complementares que tornam seu sistema resiliente ao consumir APIs de CPF. O rate limiter preserva sua cota mensal e evita cobranças de excedente desnecessárias, enquanto o backoff exponencial com jitter garante recuperação elegante quando falhas de rede ou indisponibilidade temporária acontecem. Implemente ambas as técnicas desde o início do projeto e ajuste os parâmetros conforme os limites da sua conta.

Cadastre-se em [cpfhub.io](https://www.cpfhub.io/) — 50 consultas mensais gratuitas, sem cartão de crédito — e integre a API de CPF com os padrões de resiliência que seu sistema merece.

