Proxies Residenciais

Limitação de Taxa e Controle de Requisições com Proxies Residenciais

Um grande pool de proxies não isenta você dos limites de taxa, apenas muda onde eles se aplicam. Veja como ritmar requisições por destino, por IP e em toda uma frota.

Matt Brown

Matt Brown

26 de agosto de 2026 · 9 min de leitura

Existe uma lógica tentadora que pega muitas equipes de surpresa: limites de taxa são aplicados por IP, um pool residencial oferece muitos IPs, portanto os limites de taxa deixam de valer. As duas primeiras premissas são verdadeiras e a conclusão é errada, e é nessa lacuna entre elas que vive um número surpreendente de scrapers bloqueados.

Um pool muda onde o limite atua, não se ele existe. Você ainda precisa controlar o ritmo, só que em eixos diferentes dos de uma configuração de IP único, e o eixo que as pessoas esquecem é justamente o que as faz serem pegas. Veja como o throttling funciona de fato quando suas requisições estão distribuídas por um pool rotativo.

Três limites, não um

Quando você faz scraping através de um pool, pelo menos três tetos separados se aplicam ao mesmo tempo, e sua taxa de transferência é definida por qual deles for atingido primeiro.

Por IP, por alvo. Este é o clássico. Qualquer endereço único só pode enviar uma certa quantidade de requisições a um determinado site antes que esse site o limite ou bloqueie. A rotação é o que mantém você abaixo desse limite, e esse é justamente o propósito de distribuir a carga por um pool, como descrito em balanceamento de carga.

Por alvo, de forma agregada. Este é o que surpreende as pessoas. Um site não conta apenas por endereço; ele também vê sua própria carga total de entrada e pode reconhecer um padrão coordenado entre muitos endereços, particularmente se as requisições compartilham timing, caminhos ou comportamento. Seus cinquenta IPs enviando duas requisições cada por segundo somam cem requisições por segundo chegando a uma única origem, e nenhuma quantidade de rotação de endereços faz isso parecer tráfego orgânico comum. Os sistemas anti-bot cada vez mais raciocinam nesse nível.

Sua própria capacidade. Concorrência que você consegue realmente sustentar: conexões abertas, threads de trabalho, memória, e o fato de que a latência residencial é maior do que uma conexão direta, de modo que um número fixo de threads produz menos requisições por segundo do que você poderia esperar.

A consequência prática é que seu throttle precisa ser expresso por alvo, e não de forma global. Um único limite de taxa global ou sufoca você nos cinquenta sites que poderiam aguentar mais, ou martela aquele que não consegue.

Comece pelo alvo, não pelas suas ambições

Antes de escrever qualquer código de throttling, descubra o que o alvo realmente tolera, porque chutar produz um trabalho desnecessariamente lento ou um trabalho bloqueado.

Leia os sinais que o site fornece. Um 429 é uma declaração explícita de que você está indo rápido demais. Um cabeçalho Retry-After é o site dizendo exatamente quanto tempo esperar, e respeitá-lo é ao mesmo tempo correto e muito mais barato do que descobrir a resposta por tentativa e erro. Algumas APIs publicam limites em documentação ou retornam cabeçalhos de cota restante. E se o site tiver um robots.txt com uma diretiva crawl-delay, essa é uma preferência declarada que vale a pena respeitar.

Onde nada é publicado, calibre empiricamente: comece de forma conservadora, aumente gradualmente e observe a taxa de sucesso validada em vez dos códigos de status, já que um site sob pressão frequentemente degrada antes de recusar, e uma página de desafio com status 200 parece um sucesso para um contador ingênuo, como abordado em detectando conteúdo bloqueado ou falso. O ponto em que a taxa de sucesso começa a cair é seu teto real, e você quer operar abaixo dele, não exatamente nele.

Implementando o throttle

Um token bucket por alvo é a ferramenta padrão e é simples o bastante para ser escrita do zero. Os tokens são reabastecidos na taxa escolhida por você, cada requisição consome um, e uma requisição espera quando o bucket está vazio. Isso proporciona uma média estável com uma margem de burst controlada, o que se ajusta melhor ao comportamento do tráfego real do que um atraso rígido de uma requisição por N segundos.

import time, threading

class TargetLimiter:
    """Token bucket, one instance per target host."""
    def __init__(self, rate_per_sec, burst=5):
        self.rate, self.capacity = rate_per_sec, burst
        self.tokens, self.updated = burst, time.monotonic()
        self.lock = threading.Lock()

    def acquire(self):
        while True:
            with self.lock:
                now = time.monotonic()
                self.tokens = min(self.capacity,
                                  self.tokens + (now - self.updated) * self.rate)
                self.updated = now
                if self.tokens >= 1:
                    self.tokens -= 1
                    return
                wait = (1 - self.tokens) / self.rate
            time.sleep(wait)               # sleep outside the lock

LIMITS = {                                  # per target, tuned per target
    "shop.example.com":  TargetLimiter(2.0),
    "search.example.com": TargetLimiter(0.5),
}

def fetch(url, host, proxies):
    LIMITS[host].acquire()
    return requests.get(url, proxies=proxies, timeout=20)

Dois refinamentos importam em produção. Adicione jitter para que as requisições não caiam em intervalos exatos, já que um espaçamento perfeitamente regular é, em si, uma assinatura de máquina, e também sincroniza seus workers em ondas. E faça o limiter ser compartilhado entre workers em vez de por processo, caso contrário dez processos fazendo educadamente duas requisições por segundo cada um estarão, coletivamente, fazendo vinte. Em uma configuração distribuída, isso significa um contador compartilhado no Redis ou algo semelhante, o mesmo problema que escalar scraping em Kubernetes precisa resolver.

Concorrência é a outra metade

Taxa e concorrência são discos diferentes e ambos precisam de limites. A taxa controla com que frequência você inicia requisições; a concorrência controla quantas estão em andamento simultaneamente. Um job com um limite de taxa modesto mas concorrência ilimitada ainda vai abrir centenas de conexões simultâneas a uma única origem no momento em que essa origem ficar lenta, porque respostas lentas fazem as requisições se acumularem.

Limite a concorrência por alvo com um semáforo, e dimensione-o de acordo com o que o alvo tolera, não com o que sua máquina consegue abrir. Do lado do proxy, conexões concorrentes geralmente não são sua restrição, o que torna fácil esquecer que a tolerância do alvo é. A questão de distribuição de quantos endereços isso implica é tratada em quantos IPs de proxy você realmente precisa.

Throttling adaptativo supera um número fixo

Uma taxa estática é um chute que se torna errado. Sites mudam sua tolerância, ficam mais lentos sob carga ou apertam limites em horários de pico. O padrão mais robusto é ajustar com base no que se observa, no estilo de aumento aditivo e diminuição multiplicativa: aumente a taxa devagar enquanto as coisas estão saudáveis, e corte bruscamente ao primeiro sinal de tensão.

Acione a diminuição com base em um sinal composto em vez de um único código de status: um 429, uma proporção crescente de páginas de desafio, latência em alta, ou uma queda na taxa de sucesso validada. Depois recupere gradualmente em vez de voltar de imediato à taxa antiga, já que um retorno imediato à velocidade máxima após um bloqueio é, em si, um padrão reconhecível.

class AdaptiveRate:
    def __init__(self, start=2.0, floor=0.2, ceiling=10.0):
        self.rate, self.floor, self.ceiling = start, floor, ceiling

    def ok(self):                       # healthy response
        self.rate = min(self.ceiling, self.rate * 1.02)   # creep up

    def strained(self):                 # 429, challenge, timeout, latency spike
        self.rate = max(self.floor, self.rate * 0.5)      # back off hard

Onde o throttling encontra a rotação e as retries

Três interações merecem ser explicitadas, porque errar nelas desfaz o throttle.

Não responda a um sinal de taxa girando o IP. Um 429 significa que você deve desacelerar. Trocar para um endereço novo para manter o mesmo ritmo é o comportamento que transforma um único endereço limitado em um pool queimado, e é precisamente o que faz o tráfego parecer evasivo em vez de simplesmente entusiasmado. Espere quando o sinal for sobre taxa; gire quando o sinal for sobre o endereço.

As retries devem respeitar o limiter. Uma retry é outra requisição e precisa adquirir um token como qualquer outra, ou seu caminho de erro estará silenciosamente contornando o ritmo que você construiu. Tempestades de retry são a forma mais comum de um job limitado se tornar um job bloqueado, tema tratado em retry e backoff.

Sessões persistentes concentram carga. Manter um único endereço para um fluxo de múltiplas etapas significa que esse endereço carrega toda a sequência, então o ritmo por IP importa mais dentro de uma sessão persistente do que em um trabalho rotativo, onde a carga naturalmente se espalha.

Educação é interesse próprio

É fácil ler tudo isso como uma tarefa de conformidade, mas os incentivos apontam na mesma direção. O controle de ritmo preserva a reputação dos endereços dos quais você depende, mantém sua taxa de sucesso alta para que você não pague banda por páginas de desafio, e evita o ciclo de escalada em que uma coleta agressiva provoca defesas mais rígidas que tornam o alvo mais difícil para todos, você inclusive, no próximo trimestre. Respeitar os limites declarados de um site, suas diretivas de robots e seus termos é ao mesmo tempo a coisa certa e a coisa mais barata a fazer.

Conclusão

Um pool não remove os limites de taxa, ele os desloca. Faça o throttle por alvo, não de forma global, porque um único número está errado para todo site que você acessa. Descubra a tolerância real de cada alvo a partir de seus próprios sinais, respeite Retry-After e 429 como instruções, não como obstáculos, e calibre pela taxa de sucesso validada em vez dos códigos de status. Implemente com um token bucket compartilhado mais jitter, limite a concorrência separadamente da taxa, e torne o limite adaptativo para que ele degrade rápido e se recupere devagar. Depois mantenha as interações em ordem: nunca responda a um sinal de ritmo girando endereços, sempre passe as retries pelo limiter, e controle o ritmo com mais rigor dentro de sessões persistentes. Bem feito, o throttling é o que permite que um pool funcione em sua capacidade real, em vez de queimá-la.

O próprio pool é o que dá espaço para trabalhar: proxies residenciais distribuem um job com ritmo adequado por muitos endereços reais de nível doméstico, com segmentação por país e cidade, e o preço por GB faz com que um job que mantém o ritmo e busca apenas o que precisa custe menos do que um que martela e faz retries sem parar.

Pronto para começar?

Experimente os proxies residenciais da Shifter, mais de 205M IPs, mais de 195 países, a partir de $0,75/GB.

Começar