---
title: "Comparativo entre HTTPX, Requests e AIOHTTP"
description: "Requests é simples e síncrono, o AIOHTTP é assíncrono, e o HTTPX oferece os dois. Uso de proxy, desempenho e qual escolher em cada projeto, com exemplos."
url: https://proxynet.io/pt-br/blog/httpx-vs-requests-vs-aiohttp
date: 2026-09-13
author: "Acar Diveroli"
category: "Comparativos, Web scraping"
lang: pt-BR
---

# Comparativo entre HTTPX, Requests e AIOHTTP

Em Python, três bibliotecas se destacam para enviar requisições HTTP: **Requests**, **HTTPX** e **AIOHTTP**. As três fazem o mesmo trabalho básico, mas a diferença entre elas não aparece ao escrever um script pequeno, e sim quando você precisa gerenciar milhares de requisições ao mesmo tempo. Neste artigo comparamos as três bibliotecas em facilidade de uso, concorrência, suporte a proxy, tratamento de erros e ecossistema, damos exemplos funcionais de cada uma e mostramos o caminho para migrar do Requests para o assíncrono.

Os códigos dos exemplos foram testados com Python 3.13, Requests 2.32, HTTPX 0.28 e AIOHTTP 3.13.

> **Nota: Resposta rápida**
>
> Para scripts pequenos e integrações de API, Requests; para projetos que começam síncronos hoje e podem migrar para assíncrono amanhã, e para HTTP/2, HTTPX; para trabalhos de volume muito alto, desenhados desde o início como assíncronos, AIOHTTP. As três suportam proxy HTTP de forma nativa.

## As três bibliotecas em poucas palavras

- **Requests:** a biblioteca HTTP mais usada em Python. Funciona de forma síncrona, é muito fácil de aprender e tem documentação e exemplos por toda parte. Lembre-se de que cada requisição faz o programa esperar naquela linha até terminar.
- **HTTPX:** oferece uma interface muito parecida com a do Requests, tanto de forma síncrona quanto assíncrona. Também tem suporte a HTTP/2. Migrar um código Requests existente para assíncrono geralmente é menos trabalhoso com o HTTPX.
- **AIOHTTP:** uma biblioteca desenhada como assíncrona desde o início. Além do cliente, também traz um servidor web embutido. É uma escolha madura e comum para trabalhos que exigem concorrência muito alta.

## O que significa síncrono e assíncrono?

Como esse é o conceito central da comparação, vamos explicar rapidamente. Um cliente **síncrono** envia a requisição e espera até a resposta chegar; nesse meio-tempo, o programa não faz mais nada. Já um cliente **assíncrono**, depois de enviar a requisição, devolve o controle ao loop de eventos; enquanto espera a resposta, outras requisições podem sair.

A maior parte do tempo de uma requisição web se passa na rede, ou seja, esperando o servidor de destino responder. Em código síncrono, essa espera é desperdiçada; em código assíncrono, dezenas de outras requisições podem ser aguardadas nesse mesmo intervalo. A diferença não se sente ao buscar cem páginas; ao buscar dez mil páginas, ela vira a diferença entre horas e minutos.

O preço dessa vantagem é a complexidade: a sintaxe `async`/`await`, o loop de eventos e a incompatibilidade com bibliotecas não assíncronas. Por isso, dizer que "assíncrono é sempre melhor" está errado; sem necessidade real, código síncrono é mais legível e mais fácil de manter.

## As diferenças principais em uma tabela

| Recurso | Requests | HTTPX | AIOHTTP |
|---|---|---|---|
| Uso síncrono | Sim | Sim | Não |
| Uso assíncrono | Não | Sim | Sim |
| HTTP/2 | Não | Sim (com pacote extra) | Não |
| Proxy (HTTP/HTTPS) | Nativo | Nativo | Nativo |
| Proxy SOCKS | Com `requests[socks]` | Com `httpx[socks]` | Com pacote externo |
| Tempo limite padrão | Nenhum (espera indefinidamente) | 5 segundos | 5 minutos (total) |
| Seguir redirecionamentos | Ativado por padrão | Desativado por padrão | Ativado por padrão |
| Pool de conexões | Com `Session` | Com `Client` | Com `ClientSession` |
| Curva de aprendizado | Muito fácil | Fácil | Média |
| Trabalho mais adequado | Script simples, volume pequeno | Projetos mistos, síncrono + assíncrono | Concorrência alta |

A linha "tempo limite padrão" da tabela é a diferença mais frequentemente esquecida entre as três bibliotecas. No Requests, se você não passar um tempo limite, a requisição pode esperar para sempre; por isso, adicionar `timeout=` a cada chamada do Requests deveria ser um hábito. A diferença entre `ConnectTimeout` e `ReadTimeout`, e como cada um aparece na mensagem de erro, mostramos em [Max retries exceeded with url](/pt-br/blog/max-retries-exceeded-with-url).

## Requests: simples e síncrono

Instalação:

```bash
pip install requests
```

Uma única requisição via proxy:

```python
import requests

PROXY = "http://kullanici:parola@pr.proxynet.io:8000"
proxies = {"http": PROXY, "https": PROXY}

response = requests.get("https://httpbin.org/ip", proxies=proxies, timeout=20)
print(response.status_code, response.json())
```

A chave `https` no dicionário `proxies` é o proxy usado ao acessar endereços HTTPS. É correto o valor começar com `http://`: o tráfego HTTPS é tunelado dentro da conexão estabelecida com o proxy.

Se você vai fazer mais de uma requisição, use `Session`; as conexões são reaproveitadas, os cookies são preservados e a configuração de proxy é feita uma única vez:

```python
import requests

PROXY = "http://kullanici:parola@pr.proxynet.io:8000"

with requests.Session() as session:
    session.proxies.update({"http": PROXY, "https": PROXY})
    session.headers.update({"User-Agent": "bot-de-coleta/1.0"})
    for path in ["ip", "headers", "user-agent"]:
        r = session.get(f"https://httpbin.org/{path}", timeout=20)
        print(path, r.status_code)
```

O limite do Requests é a concorrência. Se você buscar 1.000 páginas em sequência, o tempo total se aproxima da soma da duração de cada requisição. É possível amenizar isso com `concurrent.futures.ThreadPoolExecutor`, mas em volumes muito altos uma biblioteca assíncrona funciona de forma mais eficiente. Os exemplos desta seção só enviam requisições GET; no artigo [POST com JSON no Python Requests: equivalentes do cURL](/pt-br/blog/python-requests-post-json), mostramos como escrever no Requests requisições POST com corpo JSON, parâmetros de consulta com `params=` e cabeçalhos.

## HTTPX: o meio-termo entre os dois mundos

Instalação:

```bash
pip install httpx
```

O uso síncrono se parece quase ponto a ponto com o Requests:

```python
import httpx

PROXY = "http://kullanici:parola@pr.proxynet.io:8000"

with httpx.Client(proxy=PROXY, timeout=20) as client:
    response = client.get("https://httpbin.org/ip")
    print(response.json())
```

> **Nota**
>
> Na versão 0.28 do HTTPX, o antigo parâmetro `proxies=` foi removido. Para um único proxy, use `proxy=`; se precisar de proxies diferentes para endereços diferentes, defina transportes com `mounts=`. Exemplos antigos disponíveis na internet podem gerar erro por causa dessa mudança.

A versão assíncrona do mesmo código envia várias requisições ao mesmo tempo:

```python
import asyncio
import httpx

PROXY = "http://kullanici:parola@pr.proxynet.io:8000"
URLS = ["https://httpbin.org/ip"] * 10

async def main():
    async with httpx.AsyncClient(proxy=PROXY, timeout=20) as client:
        responses = await asyncio.gather(*(client.get(url) for url in URLS))
        print([r.status_code for r in responses])

asyncio.run(main())
```

Aqui, as 10 requisições saem não em sequência, mas ao mesmo tempo. O tempo total se aproxima da duração da requisição mais lenta.

Para usar HTTP/2, basta instalar com `pip install "httpx[http2]"` e passar `http2=True` ao cliente. Como a multiplexação acontece em uma única conexão ao enviar muitas requisições para o mesmo servidor, o custo de estabelecer conexão cai. Ao usar proxy, o HTTP/2 funciona dentro do túnel `CONNECT`, entre você e o site de destino; o proxy não precisa suportar HTTP/2.

## AIOHTTP: para concorrência alta

Instalação:

```bash
pip install aiohttp
```

```python
import asyncio
import aiohttp

PROXY = "http://pr.proxynet.io:8000"
AUTH = aiohttp.BasicAuth("kullanici", "parola")
URLS = ["https://httpbin.org/ip"] * 10

async def fetch(session, url):
    async with session.get(url, proxy=PROXY, proxy_auth=AUTH) as response:
        return response.status

async def main():
    async with aiohttp.ClientSession() as session:
        statuses = await asyncio.gather(*(fetch(session, url) for url in URLS))
        print(statuses)

asyncio.run(main())
```

No AIOHTTP, o proxy é passado pelo parâmetro `proxy=` em cada requisição, não na sessão. Você pode passar as credenciais separadamente com `proxy_auth`, ou embuti-las no próprio endereço (`http://kullanici:parola@...`). Esse desenho torna natural passar um proxy diferente em cada requisição; se você quer fazer a rotação da sua própria lista de IPs, o AIOHTTP facilita isso.

Outra diferença do AIOHTTP é que o corpo da resposta precisa ser lido dentro do gerenciador de contexto. Chamar `await response.text()` depois de sair do bloco `async with` gera erro; leia o corpo dentro do bloco e guarde em uma variável.

## Limitando a concorrência

Ao usar assíncrono, não deixe a concorrência sem limite. Iniciar dez mil URLs de uma vez com `asyncio.gather` sobrecarrega tanto o limite de descritores de arquivo do seu próprio sistema quanto empurra o site de destino imediatamente para o limite de taxa. Usar `asyncio.Semaphore` para limitar quantas requisições ficam abertas ao mesmo tempo protege os dois lados:

```python
import asyncio
import httpx

PROXY = "http://kullanici:parola@pr.proxynet.io:8000"
URLS = [f"https://httpbin.org/get?i={i}" for i in range(100)]
LIMIT = asyncio.Semaphore(10)

async def fetch(client, url):
    async with LIMIT:
        r = await client.get(url)
        return r.status_code

async def main():
    async with httpx.AsyncClient(proxy=PROXY, timeout=20) as client:
        results = await asyncio.gather(*(fetch(client, u) for u in URLS))
        print(sum(1 for s in results if s == 200), "com sucesso")

asyncio.run(main())
```

Nesse exemplo, cem requisições entram na fila, mas no máximo dez ficam abertas ao mesmo tempo. Ajuste o limite de acordo com a tolerância do site de destino e a cota de conexões simultâneas do seu pacote de proxy.

## Tratamento de erros e nova tentativa

Nas três bibliotecas, erros de rede chegam como exceção; mas códigos de erro HTTP (404, 429, 500) **não** são exceção por padrão. Verificar a resposta é trabalho seu:

- No Requests e no HTTPX, a chamada `response.raise_for_status()` lança exceção em respostas 4xx e 5xx.
- No AIOHTTP, o mesmo é feito com `response.raise_for_status()` ou definindo `raise_for_status=True` ao abrir a sessão.

Ao trabalhar com proxy, os códigos mais comuns são:

| Código | Significado | O que fazer? |
|---|---|---|
| 407 | Falha na autenticação do proxy | Verifique usuário, senha e a codificação de caracteres especiais |
| 429 | O site de destino está aplicando limite de taxa | Reduza a concorrência, espere e tente de novo, aumente a distribuição de IP |
| 403 | O site de destino está recusando a requisição | Revise o User-Agent e o tipo de IP |
| 502 / 504 | O proxy não conseguiu alcançar o destino | Tente de novo com um IP de saída diferente |

Escreva a lógica de nova tentativa com espera exponencial: um segundo na primeira tentativa, depois dois, depois quatro. Insistir em intervalos fixos pode terminar com o bloqueio total de um IP que já recebeu 429.

## Desempenho: qual é mais rápida?

Ao escolher a biblioteca, a pergunta "qual é mais rápida" geralmente olha para o lugar errado. A maior parte do tempo de uma requisição web se passa na rede; o tempo de processamento da própria biblioteca é pequeno perto disso.

O que é determinante é **quantas requisições você consegue esperar ao mesmo tempo**:

- Um cliente síncrono espera cada requisição em sequência.
- Um cliente assíncrono pode esperar centenas de requisições ao mesmo tempo.

Ou seja, para um trabalho de 10 páginas, o Requests é suficiente. Para um trabalho de 10.000 páginas, o cliente assíncrono do HTTPX ou o AIOHTTP reduzem o tempo de forma considerável. Dentro da mesma classe (entre o HTTPX assíncrono e o AIOHTTP), existem diferenças mensuráveis, e o AIOHTTP costuma estar à frente em desempenho bruto; mas essa diferença, na maioria dos projetos, fica ofuscada pelo tempo de resposta do site de destino e pela latência do proxy.

## Como pensar proxy e concorrência juntos?

Bibliotecas assíncronas conseguem enviar muitas requisições ao mesmo tempo; mas se todas essas requisições saem de um único IP, o site de destino aplica limite de taxa rapidamente. Ao aumentar a concorrência, você também precisa pensar na distribuição de IP:

- **IP diferente a cada requisição:** com o [Proxies rotativos](https://proxynet.io/pt-br/rotating-proxy), você usa um único endereço de entrada e recebe um IP de saída diferente em cada requisição. Nenhuma alteração de código é necessária; todos os exemplos acima funcionam exatamente como estão.
- **Mesmo IP durante toda a sessão:** em fluxos com login ou que mantêm estado, como carrinho de compras, o [Proxies de sessão fixa](https://proxynet.io/pt-br/sticky-proxy) mantém o mesmo IP por um determinado período.
- **Fazer você mesmo a rotação da sua lista:** se você tem uma lista fixa de IPs, pode fazer a rotação no código; veja um exemplo passo a passo no artigo [Como fazer a rotação de proxies em Python?](/pt-br/blog/how-to-rotate-proxies-in-python).
- **Tipo de IP:** em alvos protegidos, o tipo de IP é mais determinante do que a concorrência; explicamos o motivo no artigo [Diferença entre proxy residencial e datacenter](/pt-br/blog/residential-vs-datacenter-proxy).

## Migrando do Requests para o HTTPX

Se você quer migrar um projeto Requests existente para assíncrono, o HTTPX é o caminho mais curto. Diferenças a observar na migração:

| Requests | HTTPX | Observação |
|---|---|---|
| `requests.get(url)` | `httpx.get(url)` | Igual |
| `proxies={"http": p, "https": p}` | `proxy=p` | Um único parâmetro |
| `timeout=None` por padrão | `timeout=5` por padrão | No HTTPX o tempo limite vem ativado |
| Redirecionamento seguido por padrão | Requer `follow_redirects=True` | O HTTPX não segue por padrão |
| `Session()` | `Client()` / `AsyncClient()` | Mesma lógica |
| `response.json()` | `response.json()` | Igual |

A maior parte do código funciona sem mudanças; as diferenças se concentram nas cinco linhas acima. Migrar primeiro para o `Client` síncrono e rodar os testes, depois passar para o `AsyncClient`, reduz o risco.

## Qual você deve escolher?

- **Escolha Requests** para scripts pequenos, tarefas de automação, integrações de API e trabalhos em que a concorrência não é importante.
- **Escolha HTTPX** se você pode migrar para assíncrono no futuro depois de começar síncrono hoje, se precisa de HTTP/2, ou se quer os dois usos juntos no mesmo projeto.
- **Escolha AIOHTTP** para trabalhos de coleta de dados de volume muito alto, desenhados como assíncronos desde o início, e quando você precisa de cliente e servidor na mesma aplicação.

Se buscar as páginas apenas via HTTP não for suficiente, ou seja, se o conteúdo é carregado com JavaScript, essas bibliotecas sozinhas não bastam e você vai precisar de uma automação de navegador. Tratamos essa distinção no artigo [Extração de dados: JavaScript ou Python?](/pt-br/blog/web-scraping-javascript-vs-python). Para automação de navegador do lado Python, veja nossos guias de [Selenium](/pt-br/blog/selenium) e [Proxy com o SeleniumBase](/pt-br/blog/how-to-use-proxy-with-seleniumbase).

## Perguntas frequentes

### É difícil migrar do Requests para o HTTPX?

Geralmente não. Trocar `requests.get` por `httpx.get` funciona diretamente na maioria dos códigos simples. As diferenças estão em detalhes como o tempo limite vir ativado por padrão, os redirecionamentos não serem seguidos por padrão e o nome do parâmetro de proxy. A tabela de migração acima lista essas diferenças.

### Como uso proxy SOCKS5?

Instale `pip install "requests[socks]"` para o Requests e `pip install "httpx[socks]"` para o HTTPX, e use o esquema `socks5://` no endereço do proxy. Se também quiser que a resolução de DNS seja feita do lado do proxy, use `socks5h://` no Requests. As diferenças entre os protocolos estão no artigo [Diferença entre SOCKS e HTTP proxy](/pt-br/blog/socks-vs-http-proxy).

### Código assíncrono é sempre melhor?

Não. Código assíncrono é mais complexo e mais difícil de depurar. Para um trabalho que não vai realmente se beneficiar da concorrência, código síncrono é mais legível e mais fácil de manter.

### Usar threads com o Requests é uma alternativa ao assíncrono?

Para volumes médios, sim. É possível rodar dezenas de requisições em paralelo com `ThreadPoolExecutor` e manter o código síncrono. Em centenas de requisições simultâneas, o custo de memória das threads aumenta; nesse ponto, um cliente assíncrono é mais eficiente.

### Sou obrigado a escrever as credenciais de proxy no código?

Não. As três bibliotecas leem as variáveis de ambiente `HTTP_PROXY` e `HTTPS_PROXY` (no HTTPX, `trust_env` vem ativado por padrão). Manter as credenciais em variável de ambiente reduz o risco de vazamento ao enviar o código para o repositório.

### Quantas requisições devo abrir ao mesmo tempo?

Não existe um único número correto. Isso é determinado pela tolerância do site de destino, pela cota de conexões simultâneas do seu pacote de proxy e pelos limites da sua própria máquina. Começar com dez e aumentar sem ver resposta 429 é um método seguro.

## Em resumo

As três bibliotecas fazem bem o seu trabalho; a escolha depende da escala do projeto. Para trabalhos simples, o Requests se destaca; para flexibilidade e preparo para o futuro, o HTTPX; para concorrência alta, o AIOHTTP. Seja qual for a biblioteca escolhida, defina tempo limite, limite a concorrência e escreva a nova tentativa com espera exponencial. Conforme o volume cresce, a estratégia de IP ganha tanta importância quanto a biblioteca. Para uma infraestrutura de coleta de dados em grande escala, veja nossas [soluções de extração de dados](/pt-br/data-scraping).
