Requests vs HTTPX vs aiohttp: Qual Cliente HTTP Usar?
Compare Requests, HTTPX e aiohttp em Python: async, HTTP/2, timeouts, streaming, testes e migração para escolher o cliente HTTP certo.
Use Requests para scripts síncronos simples, HTTPX como escolha padrão versátil para projetos novos e aiohttp quando a aplicação é inteiramente baseada em asyncio e precisa do ecossistema cliente-servidor da biblioteca. Se você ainda não sabe se precisará de async, o HTTPX costuma ser a decisão mais flexível: sua API síncrona é familiar para quem conhece Requests e o mesmo pacote oferece AsyncClient, HTTP/2 opcional e recursos de teste.
A escolha não deve ser feita apenas por popularidade ou por um benchmark isolado. O que muda o resultado é o tipo de aplicação: quantas requisições serão feitas, se elas podem ocorrer concorrentemente, como timeouts e conexões serão controlados, qual biblioteca a equipe já domina e quanto custa migrar o código existente.
Comparação rápida
| Critério | Requests | HTTPX | aiohttp |
|---|---|---|---|
| API síncrona | Sim | Sim | Não é o foco |
| API assíncrona nativa | Não | Sim | Sim |
| HTTP/2 no cliente | Não | Sim, opcional | Não é o foco do cliente |
| Pool de conexões | Session | Client e AsyncClient | ClientSession |
| Timeout padrão | Requer configuração explícita | Possui timeout padrão | Deve ser configurado conforme a operação |
| Streaming | Sim | Sim, síncrono e assíncrono | Sim, assíncrono |
| WebSockets | Não | Não como recurso principal do cliente | Sim |
| Servidor HTTP no mesmo pacote | Não | Não | Sim |
| Curva para quem conhece Requests | Muito baixa | Baixa | Moderada |
| Melhor encaixe | Scripts e código síncrono existente | Projetos novos, APIs e uso híbrido | Sistemas async e ecossistema aiohttp |
Não existe vencedor absoluto. Requests reduz a complexidade de um script pequeno. HTTPX evita trocar de biblioteca quando o projeto cresce de síncrono para assíncrono. aiohttp entrega mais do que um cliente: ele também inclui servidor web e WebSockets, o que pode ser decisivo em uma arquitetura já baseada nele.
Requests: simplicidade e um ecossistema consolidado
Requests tornou o consumo de HTTP em Python muito mais legível. Para consultar uma API, enviar JSON ou trabalhar com autenticação básica, seu modelo mental é direto:
import requests
response = requests.get(
"https://api.github.com/repos/python/cpython",
timeout=10,
)
response.raise_for_status()
dados = response.json()
print(dados["full_name"])
O detalhe mais importante é o timeout=10. Requests não deve ser usado em produção sem um limite explícito: uma conexão ou leitura lenta pode manter a execução presa por tempo indesejado.
Quando escolher Requests
Requests continua adequado quando:
- o programa faz poucas chamadas síncronas;
- o código existente já está estável e testado;
- a equipe não precisa de HTTP/2 nem de
asyncio; - uma dependência ou SDK interno já expõe uma
Session; - simplicidade é mais importante do que unificar APIs síncronas e assíncronas.
Uma automação que baixa um relatório uma vez ao dia não fica automaticamente melhor por ser assíncrona. Se há apenas uma requisição por vez, Requests pode ser a solução com menor custo de manutenção.
Use Session para várias chamadas
Chamadas soltas são convenientes, mas uma Session reaproveita conexões e centraliza configurações:
import requests
with requests.Session() as session:
session.headers.update({"User-Agent": "relatorio-python/1.0"})
for pagina in range(1, 4):
response = session.get(
"https://api.example.com/vendas",
params={"pagina": pagina},
timeout=(3.05, 15),
)
response.raise_for_status()
print(response.json())
A tupla de timeout separa o limite de conexão do limite de leitura. Ela não representa um prazo total para toda a operação. Essa distinção é importante ao projetar integrações resilientes.
Limitações do Requests
Requests não oferece uma API assíncrona nativa. Colocar uma chamada bloqueante dentro de um endpoint async def pode bloquear o event loop. Também não é a opção indicada quando HTTP/2 é um requisito.
Isso não torna a biblioteca obsoleta. Significa apenas que seu escopo é deliberadamente síncrono. Para entender o consumo básico de APIs antes de comparar bibliotecas, leia Python e APIs: consumindo dados.
HTTPX: a opção mais versátil para projetos novos
HTTPX oferece APIs síncrona e assíncrona no mesmo pacote. O modo síncrono é familiar:
import httpx
with httpx.Client(
base_url="https://api.github.com",
timeout=10.0,
headers={"User-Agent": "portfolio-python/1.0"},
) as client:
response = client.get("/repos/python/cpython")
response.raise_for_status()
print(response.json()["full_name"])
Quando a aplicação precisa fazer muitas chamadas concorrentes, a estrutura permanece parecida:
import asyncio
import httpx
async def buscar_repositorios(nomes: list[str]) -> list[dict]:
timeout = httpx.Timeout(connect=3.0, read=10.0, write=10.0, pool=3.0)
async with httpx.AsyncClient(
base_url="https://api.github.com",
timeout=timeout,
headers={"User-Agent": "portfolio-python/1.0"},
) as client:
respostas = await asyncio.gather(
*(client.get(f"/repos/{nome}") for nome in nomes)
)
resultados = []
for response in respostas:
response.raise_for_status()
resultados.append(response.json())
return resultados
repositorios = asyncio.run(
buscar_repositorios(["python/cpython", "pallets/flask", "fastapi/fastapi"])
)
print([repo["full_name"] for repo in repositorios])
Concorrência ajuda quando o programa passa a maior parte do tempo esperando I/O. Ela não acelera automaticamente processamento de CPU e também não autoriza disparar requisições sem limite. Em uma API real, use semáforos, limites de conexão e a política de rate limit do serviço.
Quando escolher HTTPX
HTTPX é uma boa escolha quando:
- o projeto novo pode ter partes síncronas e assíncronas;
- a aplicação usa FastAPI, Starlette ou ASGI;
- HTTP/2 é necessário no cliente;
- a equipe quer timeout granular e limites de pool explícitos;
- testes precisam simular o transporte sem acessar a internet;
- há intenção de migrar gradualmente de Requests.
O guia de HTTPX como alternativa moderna ao Requests aprofunda instalação, streaming e migração. Para produção, veja também timeouts e retries com HTTPX.
HTTP/2 com HTTPX
O suporte a HTTP/2 é opcional. Instale o extra apropriado e ative o recurso no cliente:
python -m pip install "httpx[http2]"
import httpx
with httpx.Client(http2=True, timeout=10.0) as client:
response = client.get("https://www.google.com")
print(response.http_version)
O servidor ainda precisa oferecer HTTP/2. Além disso, HTTP/2 não garante que toda carga ficará mais rápida: o benefício depende de reutilização de conexão, latência, concorrência e comportamento do servidor.
aiohttp: cliente e servidor no ecossistema asyncio
aiohttp é uma biblioteca assíncrona que oferece cliente HTTP, servidor web e suporte a WebSockets. Ela exige familiaridade com asyncio, context managers assíncronos e ciclo de vida de sessões.
import asyncio
import aiohttp
async def buscar_repositorio() -> dict:
timeout = aiohttp.ClientTimeout(total=10)
async with aiohttp.ClientSession(
base_url="https://api.github.com",
timeout=timeout,
headers={"User-Agent": "portfolio-python/1.0"},
) as session:
async with session.get("/repos/python/cpython") as response:
response.raise_for_status()
return await response.json()
dados = asyncio.run(buscar_repositorio())
print(dados["full_name"])
Observe duas diferenças: a resposta também é usada como context manager assíncrono, e a leitura do JSON exige await. Esses detalhes são naturais em um projeto async, mas adicionam complexidade desnecessária a um script estritamente sequencial.
Quando escolher aiohttp
Escolha aiohttp quando:
- a aplicação inteira já segue
asyncio; - a equipe tem código e utilitários consolidados com
ClientSession; - cliente HTTP, servidor e WebSockets serão mantidos no mesmo ecossistema;
- controle assíncrono de streaming é central para o projeto;
- migrar uma base madura para outra biblioteca não traria benefício suficiente.
Se o único requisito é consumir APIs a partir de FastAPI, HTTPX geralmente oferece uma integração mental mais simples. Se a aplicação já é um serviço aiohttp ou depende de seus WebSockets e middlewares, permanecer no mesmo ecossistema pode reduzir o número de abstrações.
Concorrência: async não significa “mais rápido” em todo caso
Uma comparação justa precisa separar três cenários.
Uma única requisição
Em uma chamada isolada, DNS, TLS, latência de rede e tempo do servidor costumam dominar. A diferença entre bibliotecas raramente justifica uma migração por si só.
Muitas requisições independentes
HTTPX assíncrono e aiohttp podem esperar várias respostas concorrentemente sem criar uma thread por chamada. O ganho aparece quando o serviço permite concorrência e cada tarefa passa bastante tempo esperando I/O.
Processamento pesado após o download
Se cada resposta exige compactação, visão computacional ou cálculo intenso, asyncio não resolve o gargalo de CPU. Considere processos, filas ou trabalho em lote. O tutorial de async e await em Python explica essa separação.
Não publique benchmarks universais como “biblioteca X é dez vezes mais rápida”. Teste o seu fluxo com conexões reutilizadas, payload realista, mesmo servidor, mesma concorrência e limites iguais.
Timeouts: compare a política, não apenas o valor
Um cliente de produção precisa limitar conexão, leitura, escrita e espera pelo pool. Cada biblioteca representa isso de maneira diferente.
Requests
response = requests.get(url, timeout=(3.05, 15))
HTTPX
timeout = httpx.Timeout(connect=3.0, read=15.0, write=10.0, pool=3.0)
response = httpx.get(url, timeout=timeout)
aiohttp
timeout = aiohttp.ClientTimeout(
total=20,
connect=3,
sock_connect=3,
sock_read=15,
)
Os nomes e a semântica não são idênticos. Antes de migrar, confira se o novo cliente preserva o comportamento esperado. Um timeout total de 20 segundos, por exemplo, não é necessariamente equivalente a um timeout de leitura de 20 segundos.
Streaming e arquivos grandes
As três bibliotecas permitem trabalhar com respostas sem carregar tudo na memória, mas a API muda.
Com HTTPX síncrono:
import httpx
with httpx.stream("GET", "https://example.com/arquivo.zip", timeout=30.0) as response:
response.raise_for_status()
with open("arquivo.zip", "wb") as arquivo:
for bloco in response.iter_bytes(chunk_size=64 * 1024):
arquivo.write(bloco)
Com aiohttp:
import aiohttp
async def baixar(session: aiohttp.ClientSession, url: str) -> None:
async with session.get(url) as response:
response.raise_for_status()
with open("arquivo.zip", "wb") as arquivo:
async for bloco in response.content.iter_chunked(64 * 1024):
arquivo.write(bloco)
A escrita tradicional em arquivo ainda é bloqueante. Para downloads muito grandes ou alta concorrência, avalie uma estratégia de I/O de disco adequada, limites simultâneos e espaço disponível. Não transforme um exemplo async em centenas de downloads paralelos sem backpressure.
Testes sem chamar a API real
Testes não devem depender de um serviço externo em toda execução. Isso cria lentidão, flutuação e risco de consumir cota.
HTTPX inclui MockTransport:
import httpx
def responder(request: httpx.Request) -> httpx.Response:
if request.url.path == "/clientes/42":
return httpx.Response(200, json={"id": 42, "nome": "Ana"})
return httpx.Response(404, json={"erro": "não encontrado"})
def test_buscar_cliente() -> None:
transport = httpx.MockTransport(responder)
with httpx.Client(base_url="https://crm.test", transport=transport) as client:
response = client.get("/clientes/42")
assert response.json() == {"id": 42, "nome": "Ana"}
No ecossistema Requests, ferramentas como responses e requests-mock são escolhas comuns. No aiohttp, plugins e utilitários de teste do próprio ecossistema ajudam a simular servidor e cliente. Independentemente da biblioteca, separe testes rápidos e determinísticos de poucos testes de contrato contra o ambiente real.
Como migrar de Requests para HTTPX
A semelhança ajuda, mas não faça apenas uma substituição global do import.
# Antes
import requests
with requests.Session() as session:
response = session.get(url, timeout=10)
response.raise_for_status()
# Depois
import httpx
with httpx.Client(timeout=10.0) as client:
response = client.get(url)
response.raise_for_status()
Revise estes pontos:
- comportamento de redirects;
- configuração e semântica dos timeouts;
- tipos das exceções capturadas;
- cookies, proxies e autenticação;
- streaming e ciclo de vida da resposta;
- encoding do texto recebido;
- mocks e fixtures dos testes;
- fechamento de
ClientouAsyncClient; - limites de conexão e concorrência;
- compatibilidade de bibliotecas que recebem uma
requests.Session.
Migre por cliente ou integração, execute a suíte e observe erros e latência. Uma base síncrona não precisa virar assíncrona ao mesmo tempo: você pode adotar primeiro httpx.Client e introduzir AsyncClient somente onde a concorrência traz benefício claro.
Qual usar com FastAPI, Django e scripts?
FastAPI
Prefira httpx.AsyncClient para chamadas externas dentro de fluxos assíncronos. Crie e feche o cliente no ciclo de vida da aplicação, em vez de abrir uma conexão nova a cada requisição. O guia de APIs com FastAPI mostra a base do framework.
Django
Requests ou HTTPX síncrono continuam adequados em views e jobs síncronos. Se a aplicação usa views async, verifique todo o caminho: ORM, middlewares e bibliotecas bloqueantes. Trocar apenas o cliente HTTP não transforma o restante da stack em assíncrono.
Scripts e automações
Use Requests quando a tarefa é curta, sequencial e já funciona. Escolha HTTPX quando quer timeout padrão, uma API moderna ou a possibilidade de crescer para async. Use aiohttp quando o script já coordena várias tarefas com asyncio e a equipe conhece a biblioteca.
WebSockets e servidor assíncrono
Entre as três opções, aiohttp se destaca por incluir cliente, servidor e WebSockets no mesmo projeto. Se você só precisa de um cliente HTTP, essa amplitude pode não ser necessária.
Rubrica de decisão
Escolha Requests se:
- o código é síncrono e pequeno;
- há poucas requisições por execução;
- a base existente está madura;
- não existe requisito de HTTP/2 ou async;
- o menor custo cognitivo é a prioridade.
Escolha HTTPX se:
- o projeto é novo e consome APIs;
- você quer uma biblioteca para sync e async;
- usa FastAPI ou ASGI;
- precisa de HTTP/2 no cliente;
- quer transporte simulado para testes;
- planeja migrar gradualmente de Requests.
Escolha aiohttp se:
- o sistema é totalmente assíncrono;
- a equipe já usa
ClientSession; - servidor HTTP ou WebSockets também fazem parte da solução;
- há uma base aiohttp madura que não precisa ser reescrita;
- a flexibilidade do ecossistema compensa uma API menos parecida com Requests.
Erros comuns
Criar uma sessão por requisição
Session, Client, AsyncClient e ClientSession existem para reaproveitar conexões. Instanciar e fechar um cliente para cada item de um lote perde esse benefício.
Usar Requests dentro de async def
Uma chamada bloqueante pode parar o event loop. Use um cliente assíncrono ou mova a operação síncrona para uma estratégia apropriada, entendendo o custo de threads.
Fazer concorrência ilimitada
asyncio.gather com milhares de URLs pode esgotar conexões, memória e a cota da API. Limite concorrência e respeite 429 e Retry-After.
Repetir qualquer POST
Retries em operações com efeito colateral podem duplicar cobrança, pedido ou mensagem. Use chave de idempotência quando o provedor oferece esse recurso.
Colocar token no código
Credenciais devem vir de variável de ambiente ou gerenciador de segredos, nunca do repositório. Veja python-dotenv e variáveis de ambiente para desenvolvimento local.
Conclusão
HTTPX é a recomendação padrão para a maioria dos projetos Python novos que consomem APIs, porque atende código síncrono e assíncrono sem exigir duas bibliotecas e oferece uma transição acessível para quem conhece Requests. Requests continua excelente para scripts simples e bases síncronas estáveis. aiohttp é a escolha especializada para aplicações profundamente integradas ao asyncio, principalmente quando cliente, servidor ou WebSockets convivem no mesmo ecossistema.
Não migre apenas para seguir tendência. Primeiro defina timeout, reutilização de conexões, concorrência, retries, testes e observabilidade. Depois escolha a biblioteca que expressa essas decisões com menos complexidade para a equipe. Em clientes HTTP, confiabilidade operacional importa mais do que vencer um benchmark de laboratório.