Cover do episódio 111: Por que derrubei uma API de terceiro — e o que aprendi sobre rate limit
#11111 de novembro, 20192 min leituraInfra que Aprendi na PráticaS3 · 2018–2019

Por que derrubei uma API de terceiro — e o que aprendi sobre rate limit

Rodei um script de sincronização sem throttle. Em três minutos fiz 4.200 requests. A API bloqueou minha chave. O cliente ficou sem sistema por duas horas.

APIsRate LimitingProduçãoIntegração

Fiz 4.200 requests para a API do CRM em três minutos.

Não foi erro de lógica — foi minha primeira sincronização em lote, rodando sem nenhum controle de velocidade. O limite da API era 100 requests por minuto. Ultrapassei por 14x.

A API bloqueou minha chave de API. Minha aplicação começou a retornar 429 (Too Many Requests) em tudo — não só na sincronização, mas em qualquer request que dependia da mesma chave, incluindo funcionalidade normal do sistema.

O cliente ficou sem sistema por duas horas até eu conseguir uma nova chave e configurar.


Rate limiting existe porque API é recurso compartilhado.

Quando você chama uma API externa, está usando infraestrutura que serve milhares de outros clientes. Sem limite, um cliente pode consumir tudo e degradar o serviço para os outros. Rate limit é proteção — do provedor e, indiretamente, dos outros usuários.

O erro que cometi foi comum: ler a documentação suficientemente para entender como autenticar e como chamar os endpoints, mas não ler a parte sobre limites.


O que implementei depois:

import time
from ratelimit import limits, sleep_and_retry

CALLS_PER_MINUTE = 80  # 80% do limite real como margem

@sleep_and_retry
@limits(calls=CALLS_PER_MINUTE, period=60)
def call_crm_api(endpoint, payload):
    return requests.post(endpoint, json=payload, headers=auth_headers)

Ou, sem biblioteca, manualmente:

def sync_records_with_throttle(records, delay_seconds=0.8):
    for i, record in enumerate(records):
        call_api(record)
        if i > 0 and i % 10 == 0:
            time.sleep(1)  # pausa a cada 10 calls

Tem outro nível nisso: respeitar rate limit não é só não ultrapassar. É tratar o erro 429 quando acontecer.

Mesmo com throttle local, a API pode ter limites dinâmicos, ou outros clientes podem estar usando a mesma quota compartilhada, ou você pode ter um pico legítimo.

429 com Retry-After header te diz quando tentar de novo:

response = requests.post(endpoint, ...)
if response.status_code == 429:
    retry_after = int(response.headers.get("Retry-After", 60))
    time.sleep(retry_after)
    # tenta de novo

Tratar 429 graciosamente em vez de propagar erro para o usuário é a diferença entre sistema resiliente e sistema frágil.

Toda API que você integra tem limite. Parte do trabalho de integração é descobrir esse limite e planejar para ele — antes de rodar em produção.