0Pricing
AI Agents · Aula

Limitação de taxa e lógica de novas tentativas

Retardo exponencial, tratamento do código 429 e uso responsável de APIs.

Limitação de taxa e lógica de novas tentativas é uma aula grátis de AI Agents no CoddyKit. Esta é a aula 4 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de AI Agents, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de AI Agents inclui 4 aulas no total.

O que é limitação de taxa?

A limitação de taxa é a forma como as APIs se protegem contra sobrecarga. Quando seu agente envia solicitações demais em pouco tempo, a API retorna 429 Too Many Requests. Os limites comuns incluem solicitações por segundo, por minuto ou por dia.

Ignorar os limites de taxa pode resultar em agentes bloqueados, chaves de API revogadas e cobranças adicionais.

import requests

response = requests.get(
    'https://api.example.com/data',
    headers={'Authorization': 'Bearer YOUR_KEY'}
)

if response.status_code == 429:
    print('Rate limit exceeded!')
    # Check headers for limit details
    limit = response.headers.get('X-RateLimit-Limit')
    remaining = response.headers.get('X-RateLimit-Remaining')
    reset = response.headers.get('X-RateLimit-Reset')
    print(f'Limit: {limit}, Remaining: {remaining}, Reset: {reset}')

O cabeçalho Retry-After

Quando uma API retorna 429, ela geralmente inclui um cabeçalho Retry-After que informa exatamente quantos segundos você deve esperar antes de tentar novamente. Sempre respeite esse cabeçalho — ignorá-lo e tentar novamente imediatamente fará com que você receba outro 429.

import requests
import time

def request_with_retry_after(url, headers):
    response = requests.get(url, headers=headers)

    if response.status_code == 429:
        retry_after = int(response.headers.get('Retry-After', 60))
        print(f'Rate limited. Waiting {retry_after} seconds...')
        time.sleep(retry_after)

        # Retry once after waiting
        response = requests.get(url, headers=headers)

    response.raise_for_status()
    return response.json()

Espera exponencial

A espera exponencial é a estratégia padrão para novas tentativas: espere mais tempo após cada tentativa malsucedida. Se a tentativa 1 esperar 2 segundos, a tentativa 2 esperará 4, a tentativa 3 esperará 8 e assim por diante. Isso reduz progressivamente a carga no servidor e dá tempo para que ele se recupere.

Fórmula: wait = 2 ** attempt

import requests
import time

def get_with_exponential_backoff(url, headers, max_retries=5):
    for attempt in range(max_retries):
        response = requests.get(url, headers=headers, timeout=(5, 30))

        if response.status_code == 200:
            return response.json()

        if response.status_code in (429, 500, 502, 503):
            wait = 2 ** attempt  # 1, 2, 4, 8, 16 seconds
            print(f'Attempt {attempt+1} failed ({response.status_code}). '
                  f'Waiting {wait}s before retry...')
            time.sleep(wait)
        else:
            response.raise_for_status()  # non-retryable error

    raise Exception(f'Failed after {max_retries} retries')

Adicionando aleatoriedade à espera

Se muitos agentes tentarem novamente ao mesmo tempo (um cenário comum após uma breve interrupção), todos serão reativados simultaneamente, criando um efeito manada que fará com que a limitação de taxa ocorra novamente de imediato. Adicionar aleatoriedade (atraso aleatório) distribui as novas tentativas, reduzindo a carga no servidor.

import requests
import time
import random

def get_with_jittered_backoff(url, headers, max_retries=5):
    for attempt in range(max_retries):
        response = requests.get(url, headers=headers, timeout=(5, 30))

        if response.status_code == 200:
            return response.json()

        if response.status_code in (429, 500, 502, 503):
            base_wait = 2 ** attempt
            # Add random jitter: actual wait is 50%-100% of base
            jitter = random.uniform(0.5, 1.0)
            wait = base_wait * jitter
            print(f'Waiting {wait:.1f}s (attempt {attempt+1})')
            time.sleep(wait)
        else:
            response.raise_for_status()

    raise Exception(f'Failed after {max_retries} retries')

A biblioteca tenacity

tenacity é a biblioteca Python mais popular para lógica de novas tentativas. Ela lida com espera exponencial, aleatoriedade, número máximo de tentativas e condições de parada personalizadas com uma sintaxe limpa de decorador. É muito mais confiável do que desenvolver manualmente ciclos de novas tentativas.

from tenacity import (
    retry, stop_after_attempt, wait_exponential,
    retry_if_exception_type, before_sleep_log
)
import requests
import logging

logger = logging.getLogger(__name__)

@retry(
    stop=stop_after_attempt(5),
    wait=wait_exponential(multiplier=1, min=2, max=60),
    retry=retry_if_exception_type(requests.exceptions.HTTPError),
    before_sleep=before_sleep_log(logger, logging.WARNING)
)
def fetch_data(url, headers):
    response = requests.get(url, headers=headers, timeout=(5, 30))
    if response.status_code == 429:
        response.raise_for_status()  # triggers retry
    response.raise_for_status()
    return response.json()

tenacity com condição personalizada de nova tentativa

Você pode configurar o tenacity para tentar novamente somente com códigos de status específicos (como 429 e 5xx) e parar imediatamente com erros do cliente (4xx), que não se beneficiariam de uma nova tentativa. Use retry_if_result ou uma função chamável personalizada para examinar a resposta.

from tenacity import (
    retry, stop_after_attempt, wait_exponential,
    retry_if_result
)
import requests

def is_retryable_response(response):
    return response.status_code in (429, 500, 502, 503, 504)

@retry(
    stop=stop_after_attempt(4),
    wait=wait_exponential(multiplier=2, min=2, max=30),
    retry=retry_if_result(is_retryable_response)
)
def resilient_get(url, headers):
    response = requests.get(url, headers=headers, timeout=(5, 30))
    return response  # retry logic inspects the response object

# Usage
response = resilient_get(
    'https://api.example.com/data',
    {'Authorization': 'Bearer YOUR_KEY'}
)
data = response.json()

Gerenciamento proativo da limitação de taxa

A melhor estratégia é evitar atingir os limites de taxa desde o início. Verifique os cabeçalhos de limitação de taxa em todas as respostas e diminua o ritmo quando estiver próximo do limite. Muitas APIs incluem os cabeçalhos X-RateLimit-Remaining e X-RateLimit-Reset.

import requests
import time

class RateLimitAwareClient:
    def __init__(self, base_url, api_key):
        self.base_url = base_url
        self.headers = {'Authorization': f'Bearer {api_key}'}
        self.remaining = 1000  # assume generous limit

    def get(self, path):
        # Proactively slow down if nearly exhausted
        if self.remaining < 10:
            print('Rate limit nearly exhausted, sleeping 5s...')
            time.sleep(5)

        response = requests.get(
            f'{self.base_url}{path}', headers=self.headers
        )

        # Update remaining from response headers
        remaining_str = response.headers.get('X-RateLimit-Remaining')
        if remaining_str:
            self.remaining = int(remaining_str)

        response.raise_for_status()
        return response.json()

Número máximo de novas tentativas e desistência

A lógica de novas tentativas sempre deve ter um limite. Tentar novamente indefinidamente pode causar falhas em cascata, nas quais todos os seus agentes ficam presos em ciclos de novas tentativas. Depois de max_retries, gere uma exceção final com contexto sobre o que falhou, para que o agente possa registrá-la e passar para outras tarefas.

import requests
import time

class MaxRetriesExceeded(Exception):
    def __init__(self, url, attempts, last_status):
        self.url = url
        self.attempts = attempts
        self.last_status = last_status
        super().__init__(
            f'Failed {url} after {attempts} attempts '
            f'(last status: {last_status})'
        )

def fetch_with_limit(url, headers, max_retries=3):
    last_response = None
    for attempt in range(max_retries):
        last_response = requests.get(url, headers=headers)
        if last_response.status_code == 200:
            return last_response.json()
        time.sleep(2 ** attempt)
    raise MaxRetriesExceeded(url, max_retries, last_response.status_code)

O padrão do disjuntor

O padrão do disjuntor impede que seu agente sobrecarregue um serviço com falhas. Depois de um limite de falhas, o circuito "abre" e todas as solicitações falham imediatamente, sem acessar a rede. Após um período de resfriamento, ele tenta uma solicitação — se ela for bem-sucedida, o circuito "fecha" e a operação normal é retomada.

import time

class CircuitBreaker:
    CLOSED, OPEN, HALF_OPEN = 'closed', 'open', 'half_open'

    def __init__(self, failure_threshold=5, recovery_timeout=60):
        self.state = self.CLOSED
        self.failures = 0
        self.failure_threshold = failure_threshold
        self.recovery_timeout = recovery_timeout
        self.opened_at = None

    def call(self, func, *args, **kwargs):
        if self.state == self.OPEN:
            if time.time() - self.opened_at > self.recovery_timeout:
                self.state = self.HALF_OPEN
            else:
                raise Exception('Circuit OPEN — service unavailable')
        try:
            result = func(*args, **kwargs)
            self.failures = 0
            self.state = self.CLOSED
            return result
        except Exception as e:
            self.failures += 1
            if self.failures >= self.failure_threshold:
                self.state = self.OPEN
                self.opened_at = time.time()
                print(f'Circuit OPENED after {self.failures} failures')
            raise

# --- demo ---
def flaky():
    raise ValueError('upstream 500')

def works():
    return 'ok'

cb = CircuitBreaker(failure_threshold=3, recovery_timeout=60)
for i in range(3):
    try:
        cb.call(flaky)
    except Exception as e:
        print(f'call {i+1} failed: {e}')
print(f'Breaker state after 3 failures: {cb.state}')
try:
    cb.call(flaky)
except Exception as e:
    print(f'Rejected without calling flaky(): {e}')

Enfileirando solicitações para permanecer dentro dos limites

Para agentes que fazem muitas chamadas em lote, use um balde de tokens ou uma limitação simples baseada em espera para permanecer dentro dos limites. Calcule o intervalo seguro entre as chamadas com base no limite de taxa da API (por exemplo, 60 chamadas por minuto = 1 chamada por segundo).

import requests
import time

def batch_requests(urls, headers, calls_per_minute=60):
    interval = 60.0 / calls_per_minute  # seconds between calls
    results = []

    for i, url in enumerate(urls):
        start = time.time()

        response = requests.get(url, headers=headers, timeout=(5, 30))
        response.raise_for_status()
        results.append(response.json())

        print(f'Processed {i+1}/{len(urls)}')

        # Sleep for remaining time in the interval
        elapsed = time.time() - start
        sleep_time = interval - elapsed
        if sleep_time > 0:
            time.sleep(sleep_time)

    return results

Combinando a lógica de novas tentativas com cabeçalhos de espera

O padrão mais robusto combina os tempos de espera especificados pelo servidor (Retry-After) com a espera exponencial como alternativa. Sempre prefira as orientações do servidor quando estiverem disponíveis — ele sabe exatamente quando você poderá tentar novamente.

import requests
import time
import random

def smart_retry(url, headers, max_retries=5):
    for attempt in range(max_retries):
        response = requests.get(url, headers=headers, timeout=(5, 30))

        if response.status_code == 200:
            return response.json()

        if response.status_code == 429:
            # Use Retry-After if provided, else exponential backoff
            retry_after = response.headers.get('Retry-After')
            if retry_after:
                wait = int(retry_after)
            else:
                wait = (2 ** attempt) + random.uniform(0, 1)
            print(f'429 rate limit. Waiting {wait:.1f}s...')
            time.sleep(wait)

        elif response.status_code >= 500:
            wait = (2 ** attempt) + random.uniform(0, 1)
            print(f'Server error {response.status_code}. Waiting {wait:.1f}s...')
            time.sleep(wait)

        else:
            response.raise_for_status()  # non-retryable

    raise Exception(f'Gave up after {max_retries} attempts')

Verificação rápida: espera exponencial

Teste sua compreensão sobre estratégias de novas tentativas.

Recapitulação da limitação de taxa e das novas tentativas

Agora seus agentes conseguem lidar adequadamente com os limites de taxa:

  • 429 Muitas solicitações — respeite o cabeçalho Retry-After; espere antes de tentar novamente
  • Espera exponencial — wait = 2^attempt dobra o tempo de espera a cada nova tentativa
  • Aleatoriedade — adiciona variação para distribuir as novas tentativas entre várias instâncias do agente
  • tenacity — gerencia toda a lógica de novas tentativas com decoradores e uma configuração simples
  • Disjuntor — interrompe a sobrecarga de um serviço com falhas após um determinado limite
  • Limitação proativa — verifique X-RateLimit-Remaining e diminua o ritmo antes de atingir o limite

Perguntas Frequentes

A aula “Limitação de taxa e lógica de novas tentativas” é grátis?

Sim — o texto completo de “Limitação de taxa e lógica de novas tentativas” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de AI Agents, atualize para CoddyKit PRO. O curso de AI Agents inclui 4 aulas no total.

O que vou aprender em “Limitação de taxa e lógica de novas tentativas”?

Retardo exponencial, tratamento do código 429 e uso responsável de APIs. Você pratica AI Agents com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.

Preciso ter experiência prévia para começar AI Agents?

Nenhuma experiência prévia é necessária. AI Agents no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 4 de 4.

Quanto tempo leva a aula “Limitação de taxa e lógica de novas tentativas”?

A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.

Posso escrever e executar código nesta aula de AI Agents?

Sim. Cada aula de AI Agents inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.

Todas as aulas deste curso

  1. Fundamentos da API REST para desenvolvedores de agentes
  2. Autenticação: chaves de API e OAuth
  3. Lidando com respostas e erros de APIs
  4. Limitação de taxa e lógica de novas tentativas
← Voltar para AI Agents