Menu

Requisições HTTP em Python: usando a biblioteca requests (GET, POST, JSON)

Como fazer requisições HTTP em Python com a biblioteca requests — GET, POST, parâmetros de query, headers, corpos JSON e tratamento de erros.

Buscando dados da internet em poucas linhas

A maioria dos programas Python reais eventualmente precisa conversar com algo pela rede — uma API REST, um serviço de meteorologia, um endpoint do GitHub, um download. A biblioteca padrão consegue fazer isso (via urllib), mas a ferramenta de facto no mundo Python é uma biblioteca de terceiros chamada requests. A API é tão mais amigável que vale o único pip install.

pip install requests

Se você ainda não está num ambiente virtual, monte um primeiro — mantém essa instalação escopada ao seu projeto.

Primeiro GET

main.py
Output
Click Run to see the output here.

Três linhas: chame get, confira o status code, olhe o corpo. response.text é o corpo da resposta como string. Truncado aqui porque o JSON completo é longo.

Status codes que vale reconhecer no nível de folha de cola:

  • 200 — OK, tudo deu certo.
  • 201 — Criado (resposta comum de POST).
  • 301 / 302 — redirecionamentos; requests segue automaticamente por padrão.
  • 400 — requisição ruim; algo no que você enviou estava errado.
  • 401 / 403 — não autenticado / não autorizado.
  • 404 — o recurso não existe.
  • 429 — rate limited; desacelere.
  • 500 — erro do servidor.

Parseando uma resposta JSON

Quando o endpoint retorna JSON, chame .json() na resposta — parseia o corpo e te entrega um dict (ou list):

main.py
Output
Click Run to see the output here.

Por baixo dos panos, .json() é o mesmo que json.loads(response.text) — só um atalho para o caso comum.

Enviando parâmetros de query

Não grude ?chave=valor&... na URL na mão. Passe um dict para params=:

main.py
Output
Click Run to see the output here.

O requests lida com URL encoding para você — espaços, caracteres especiais, Unicode tudo funciona com segurança.

A URL real que saiu está disponível em response.url, útil para debug.

Requisições POST com corpos JSON

Para enviar dados — criar recursos, submeter formulários, chamar endpoints que modificam — use requests.post:

import requests

payload = {
    "title": "Docs update",
    "body": "Added HTTP requests page.",
    "labels": ["docs"],
}

response = requests.post(
    "https://api.example.com/issues",
    json=payload,
    headers={"Authorization": "Bearer YOUR_TOKEN"},
)

print(response.status_code)
print(response.json())

O argumento json= faz duas coisas: serializa payload para JSON e define Content-Type: application/json. Você poderia fazer na mão com data=json.dumps(payload) e um header explícito, mas json= é o atalho idiomático.

Para dados form-encoded (o tipo que um formulário HTML clássico envia), use data=:

requests.post("https://example.com/login", data={"user": "rosa", "password": "..."})

Headers

Passe qualquer header customizado como dict:

import requests

response = requests.get(
    "https://api.example.com/profile",
    headers={
        "Authorization": "Bearer abc123",
        "User-Agent": "my-tool/1.0",
    },
)

A maioria das APIs quer um header Authorization para autenticação. O esquema exato (Bearer, Basic, Token) está na documentação delas.

Timeouts não são opcionais

Por padrão, requests espera para sempre por uma resposta. Num programa real, isso transforma um servidor instável num script travado. Sempre passe um timeout:

import requests

try:
    response = requests.get("https://api.example.com/slow", timeout=5)
except requests.Timeout:
    print("Server took too long.")

O número é em segundos. timeout=5 significa "desista se não tivermos resposta em 5 segundos". Você pode passar uma tupla (connect_timeout, read_timeout) para mais controle.

Tratamento de erros

Dois tipos de problema podem acontecer:

  1. Erros de nível HTTP (4xx, 5xx) — o servidor respondeu, mas a resposta é de erro. response.status_code te conta.
  2. Problemas de nível de rede — timeouts, falhas de DNS, hosts inalcançáveis. Esses disparam exceções.

O padrão idiomático combina os dois:

main.py
Output
Click Run to see the output here.

raise_for_status() é no-op para respostas 2xx e dispara exceção nos outros casos. RequestException é a classe base de todo erro que requests dispara — um único catch para "qualquer coisa que deu errado conversando com esse endpoint".

Baixando um arquivo

Para downloads binários grandes, faça streaming da resposta para não ficar na memória:

import requests

url = "https://example.com/large.zip"

with requests.get(url, stream=True, timeout=30) as r:
    r.raise_for_status()
    with open("large.zip", "wb") as f:
        for chunk in r.iter_content(chunk_size=8192):
            f.write(chunk)

stream=True diz ao requests para não pré-carregar o corpo. iter_content(chunk_size=...) produz o corpo pedaço por pedaço, que você escreve direto no disco.

Sessions: reuso de conexão e defaults

Se você vai fazer várias requisições ao mesmo serviço, use uma Session. Ela reutiliza a conexão TCP por baixo (mais rápida) e permite definir defaults de uma vez:

import requests

session = requests.Session()
session.headers.update({"Authorization": "Bearer abc123"})

# Every request through this session carries the header.
a = session.get("https://api.example.com/users/1")
b = session.get("https://api.example.com/users/2")
c = session.post("https://api.example.com/users", json={"name": "Rosa"})

Para scripts que batem na mesma API dezenas de vezes, uma session é um ganho notável de velocidade.

Um exemplo realista: pequeno cliente do GitHub

Buscando o último release de um repo:

import requests

def latest_release(owner, repo):
    url = f"https://api.github.com/repos/{owner}/{repo}/releases/latest"
    response = requests.get(url, timeout=10)
    response.raise_for_status()
    data = response.json()
    return {
        "tag": data["tag_name"],
        "name": data["name"],
        "published": data["published_at"],
        "url": data["html_url"],
    }

release = latest_release("python", "cpython")
print(release)

Menos de quinze linhas: monte a URL, requisite, confira erros, extraia os campos que te importam. Essa é a cara da maioria dos clientes de API que você vai escrever.

E o urllib?

O urllib.request da biblioteca padrão consegue fazer tudo que requests faz — em mais linhas e com ergonomia pior. Se você absolutamente não pode adicionar dependência, está lá:

import json
import urllib.request

with urllib.request.urlopen("https://api.github.com/repos/python/cpython") as r:
    data = json.loads(r.read().decode("utf-8"))

print(data["name"])

Para qualquer coisa além de script rápido, requests (ou httpx se você precisa de async) vale a instalação.

Alguns hábitos

  • Sempre defina timeout. Sem exceção.
  • Use raise_for_status() em scripts que esperam sucesso — transforma uma resposta ruim numa exceção barulhenta.
  • Passe dicts para params= e json=, não strings montadas na mão.
  • Envolva chamadas relacionadas numa Session quando você estiver batendo na mesma API muitas vezes.
  • Logue response.status_code e response.text ao debugar — o corpo geralmente te diz exatamente o que o servidor não gostou.

Próxima: datas e horas

Com requests no seu kit, você pode conversar com qualquer API web moderna, baixar arquivos e construir pequenas integrações entre serviços. Combinado com as páginas de JSON e CSV antes desta, você agora tem o loop completo "busque dados, leia, faça algo, escreva de volta" — o formato de um número enorme de scripts Python reais. A maior parte desses dados tem um timestamp, porém, e a próxima página cobre como o Python representa datas, horas e as armadilhas de fuso horário que vale evitar.

Perguntas frequentes

Como faço uma requisição HTTP em Python?

Instale a biblioteca requests com pip install requests, depois chame requests.get(url) para um GET ou requests.post(url, json=...) para um POST. O objeto response tem .status_code, .text, .json() e .headers. Exemplo: r = requests.get('https://api.example.com/users/1').

Devo usar requests ou urllib?

requests para qualquer coisa que você escreveria na mão — a API é muito mais amigável. urllib é embutido e serve quando adicionar uma dependência é impossível, mas exige mais código para coisas como corpos JSON e sessões. Para produção, a maioria dos times também usa httpx (uma biblioteca compatível com requests com suporte assíncrono).

Como envio JSON numa requisição POST em Python?

Passe json={'key': 'value'} para requests.post(...). O requests serializa o dict para JSON e define Content-Type: application/json para você. Não passe data= e json= juntos — escolha um.

Como trato erros com a biblioteca requests?

Confira response.status_code (200 significa sucesso), ou chame response.raise_for_status() para disparar uma exceção em 4xx/5xx. Problemas de nível de rede (timeouts, falhas de DNS) disparam subclasses de requests.RequestException — capture isso para cobrir os dois.

Aprenda a programar com o Coddy

COMEÇAR