HTTPX: A Alternativa Moderna ao Requests em Python
Conheça o HTTPX, cliente HTTP moderno para Python com suporte a async e HTTP/2. Compare com requests e aiohttp com exemplos práticos de migração.
O requests é a biblioteca HTTP mais popular do Python — simples, elegante e presente em praticamente todo projeto. Mas ela tem limitações: não suporta async nativamente e não fala HTTP/2. O HTTPX resolve essas duas questões mantendo uma API quase idêntica ao requests, tornando a migração natural.
Neste artigo, vamos explorar o HTTPX em detalhes: instalação, uso básico e avançado, async, HTTP/2 e um guia prático de migração.
Por Que HTTPX?
O HTTPX foi criado como um “requests para a era moderna” do Python. Veja os principais diferenciais:
| Recurso | requests | httpx | aiohttp |
|---|---|---|---|
| API síncrona | ✅ | ✅ | ❌ |
| API assíncrona | ❌ | ✅ | ✅ |
| HTTP/2 | ❌ | ✅ | ❌ |
| Streaming | ✅ | ✅ | ✅ |
| Connection pooling | ✅ (via Session) | ✅ (via Client) | ✅ |
| Type hints | Parcial | ✅ Completo | Parcial |
| Compatível com requests | — | ✅ ~95% | ❌ |
| Timeout padrão | Nenhum (perigoso!) | 5s | Nenhum |
O ponto forte do HTTPX é ser duas bibliotecas em uma: funciona perfeitamente em código síncrono (substituição direta do requests) e em código assíncrono (substituição do aiohttp), tudo com a mesma API.
Instalação
Instale o HTTPX com suporte a HTTP/2:
pip install httpx[http2]
Ou se você usa uv para gerenciar pacotes:
uv add httpx[http2]
Uso Básico — Modo Síncrono
Se você já usa requests, vai se sentir em casa. A API é praticamente idêntica:
import httpx
# GET simples
response = httpx.get("https://httpbin.org/get")
print(response.status_code) # 200
print(response.json()) # Dicionário com dados da resposta
# POST com JSON
response = httpx.post(
"https://httpbin.org/post",
json={"nome": "Python", "versao": "3.13"},
)
print(response.json()["json"]) # {'nome': 'Python', 'versao': '3.13'}
# Headers customizados
response = httpx.get(
"https://api.github.com/user",
headers={"Authorization": "Bearer ghp_seutoken"},
)
A diferença mais importante em relação ao requests: o HTTPX tem timeout padrão de 5 segundos. Isso evita que seu programa trave indefinidamente esperando uma resposta — um problema clássico do requests que não define timeout por padrão.
Usando o Client para Connection Pooling
Para múltiplas requisições ao mesmo host, use httpx.Client() (equivalente ao requests.Session()):
import httpx
# Sem Client: cada requisição abre uma nova conexão TCP
for i in range(10):
httpx.get(f"https://httpbin.org/get?i={i}") # Lento!
# Com Client: reutiliza conexões (connection pooling)
with httpx.Client() as client:
for i in range(10):
response = client.get(f"https://httpbin.org/get?i={i}")
print(f"Requisição {i}: {response.status_code}")
O Client também permite configurar headers, autenticação e base URL globalmente:
import httpx
with httpx.Client(
base_url="https://api.exemplo.com/v1",
headers={"Authorization": "Bearer meu_token"},
timeout=10.0,
) as client:
# Todas as requisições herdam base_url e headers
usuarios = client.get("/usuarios").json()
pedidos = client.get("/pedidos").json()
detalhes = client.get(f"/usuarios/{usuarios[0]['id']}").json()
Modo Assíncrono com AsyncClient
Aqui está o maior diferencial do HTTPX. Com AsyncClient, você pode fazer requisições HTTP dentro de código async/await sem trocar de biblioteca:
import httpx
import asyncio
async def buscar_dados():
"""Faz múltiplas requisições HTTP de forma concorrente."""
urls = [
"https://httpbin.org/delay/1",
"https://httpbin.org/delay/1",
"https://httpbin.org/delay/1",
"https://httpbin.org/delay/1",
"https://httpbin.org/delay/1",
]
async with httpx.AsyncClient() as client:
# Criar todas as requisições de uma vez
tarefas = [client.get(url) for url in urls]
# Executar concorrentemente
respostas = await asyncio.gather(*tarefas)
for resp in respostas:
print(f"Status: {resp.status_code}, Tempo: {resp.elapsed}")
# 5 requisições de 1s cada → ~1s total (em vez de ~5s)
asyncio.run(buscar_dados())
Compare com a versão síncrona: as mesmas 5 requisições de 1 segundo levariam ~5 segundos no modo síncrono, mas apenas ~1 segundo com async. Para aplicações que fazem muitas chamadas a APIs externas — como a WhatsApp API — isso faz uma diferença enorme.
Suporte a HTTP/2
HTTP/2 traz melhorias significativas: multiplexação (múltiplas requisições na mesma conexão TCP), compressão de headers e server push. Com HTTPX, ativar é trivial:
import httpx
# Ativar HTTP/2 (precisa do extra http2 instalado)
with httpx.Client(http2=True) as client:
response = client.get("https://www.google.com")
print(f"Protocolo: {response.http_version}") # HTTP/2
# Funciona igual — a API não muda
response = client.get("https://httpbin.org/get")
print(response.json())
Na prática, HTTP/2 beneficia mais quando você faz muitas requisições ao mesmo servidor, como ao consumir APIs REST paginadas. Confira mais sobre consumo de APIs.
Streaming de Respostas
Para downloads grandes ou respostas que não cabem na memória, use streaming:
import httpx
# Download de arquivo grande com streaming
with httpx.stream("GET", "https://exemplo.com/arquivo-grande.zip") as response:
total = int(response.headers.get("content-length", 0))
baixado = 0
with open("arquivo.zip", "wb") as f:
for chunk in response.iter_bytes(chunk_size=8192):
f.write(chunk)
baixado += len(chunk)
if total:
progresso = (baixado / total) * 100
print(f"\rBaixando: {progresso:.1f}%", end="")
print("\nDownload completo!")
Versão assíncrona do streaming:
import httpx
import asyncio
async def download_async(url: str, destino: str):
"""Faz download de arquivo grande de forma assíncrona."""
async with httpx.AsyncClient() as client:
async with client.stream("GET", url) as response:
with open(destino, "wb") as f:
async for chunk in response.aiter_bytes(chunk_size=8192):
f.write(chunk)
print(f"Salvo em: {destino}")
asyncio.run(download_async(
"https://exemplo.com/dados.csv",
"dados.csv"
))
Upload de Arquivos
O envio de arquivos funciona de forma idêntica ao requests:
import httpx
# Upload simples
with open("relatorio.pdf", "rb") as f:
response = httpx.post(
"https://httpbin.org/post",
files={"arquivo": ("relatorio.pdf", f, "application/pdf")},
)
print(response.status_code)
# Upload com dados adicionais
with open("foto.jpg", "rb") as f:
response = httpx.post(
"https://api.exemplo.com/upload",
files={"imagem": f},
data={"descricao": "Foto do produto", "categoria": "eletronicos"},
)
Guia de Migração: requests → httpx
A migração é simples na maioria dos casos. Aqui está um mapa direto:
# ANTES (requests) # DEPOIS (httpx)
import requests import httpx
# Requisições simples — idêntico
requests.get(url) httpx.get(url)
requests.post(url, json=dados) httpx.post(url, json=dados)
# Session → Client
session = requests.Session() client = httpx.Client()
session.get(url) client.get(url)
session.close() client.close()
# Context manager
with requests.Session() as s: with httpx.Client() as c:
s.get(url) c.get(url)
# Autenticação básica — idêntico
requests.get(url, auth=("u","p")) httpx.get(url, auth=("u","p"))
As diferenças principais a observar:
# 1. Timeout: requests não tem padrão, httpx tem 5s
requests.get(url) # Pode travar para sempre!
httpx.get(url) # Timeout de 5s por padrão
httpx.get(url, timeout=30.0) # Customizar timeout
httpx.get(url, timeout=None) # Sem timeout (não recomendado)
# 2. response.text encoding: httpx usa charset do header
# requests "adivinha" com chardet — às vezes erra com conteúdo BR
# 3. Redirects: requests segue por padrão, httpx também
# Mas httpx permite controle granular:
httpx.get(url, follow_redirects=True) # Padrão
httpx.get(url, follow_redirects=False) # Não seguir redirects
Timeout Avançado
O HTTPX oferece controle granular de timeout — algo que o requests não tem:
import httpx
# Timeout simples (aplica a todas as fases)
httpx.get(url, timeout=10.0)
# Timeout detalhado por fase
timeout = httpx.Timeout(
connect=5.0, # Tempo para estabelecer conexão TCP
read=10.0, # Tempo para receber dados
write=5.0, # Tempo para enviar dados
pool=5.0, # Tempo esperando conexão disponível no pool
)
httpx.get(url, timeout=timeout)
# No Client, definir timeout global
with httpx.Client(timeout=timeout) as client:
client.get("/endpoint-rapido")
client.get("/endpoint-lento", timeout=30.0) # Override por requisição
Quando Usar Cada Biblioteca
- requests: projetos simples, scripts rápidos, quando não precisa de async nem HTTP/2
- httpx: projetos novos, APIs modernas, quando precisa de async ou HTTP/2, quando quer timeout seguro por padrão
- aiohttp: projetos puramente async que já usam aiohttp (não vale migrar só por migrar)
Para projetos com FastAPI, o HTTPX é a escolha natural — ambos são async-first e se complementam perfeitamente.
Exemplo Prático: Web Scraping com HTTPX
Combinando HTTPX com Beautiful Soup, você tem um scraper assíncrono poderoso:
import httpx
import asyncio
from selectolax.parser import HTMLParser
async def scrape_paginas(urls: list[str]) -> list[dict]:
"""Faz scraping de múltiplas páginas concorrentemente."""
resultados = []
async with httpx.AsyncClient(
headers={"User-Agent": "MeuBot/1.0"},
follow_redirects=True,
timeout=15.0,
) as client:
tarefas = [client.get(url) for url in urls]
respostas = await asyncio.gather(*tarefas, return_exceptions=True)
for url, resp in zip(urls, respostas):
if isinstance(resp, Exception):
print(f"Erro em {url}: {resp}")
continue
tree = HTMLParser(resp.text)
titulo = tree.css_first("title")
resultados.append({
"url": url,
"titulo": titulo.text() if titulo else "Sem título",
"status": resp.status_code,
})
return resultados
# Executar
urls = [
"https://python.org",
"https://pypi.org",
"https://docs.python.org/3/",
]
dados = asyncio.run(scrape_paginas(urls))
for d in dados:
print(f"{d['titulo']} ({d['status']})")
Conclusão
O HTTPX é a evolução natural do requests para o Python moderno. Com suporte a async nativo, HTTP/2, timeouts seguros e uma API familiar, ele se posiciona como a melhor escolha para projetos novos. A migração de projetos existentes é direta na maioria dos casos, tornando a transição suave.
Se você está construindo APIs com FastAPI ou Django, fazendo web scraping em escala, ou consumindo APIs externas como a WhatsApp API, o HTTPX vai simplificar seu código e melhorar a performance.
🚀 Para quem trabalha com APIs em outras linguagens, o golang.com.br tem conteúdo sobre o
net/httpdo Go — que já tem HTTP/2 nativo na biblioteca padrão. E o rustlang.com.br cobre oreqwest, o equivalente em Rust.
Equipe python.dev.br
Contribuidor do Python Brasil — Aprenda Python em Português