W3docs

Python Trabalhando com APIs (requests)

Aprenda a usar a biblioteca requests do Python para fazer chamadas GET e POST, enviar cabeçalhos, parâmetros e tratar respostas JSON e erros.

A biblioteca requests é a forma padrão de fazer chamadas HTTP a partir do Python. Ela encapsula o urllib de nível mais baixo do Python em uma API limpa, de modo que buscar uma página web ou chamar uma API REST leva uma linha em vez de dez. Este capítulo cobre tudo o que você precisa: instalar o requests, fazer chamadas GET e POST, enviar cabeçalhos e parâmetros de consulta, trabalhar com respostas JSON, fazer upload de arquivos, usar sessões e tratar erros de forma robusta.

Instalação

requests não faz parte da biblioteca padrão, portanto você o instala com pip:

pip install requests

Se você estiver trabalhando dentro de um ambiente virtual (recomendado), ative-o primeiro para que o pacote fique restrito ao seu projeto. Após a instalação, verifique se funciona:

import requests
print(requests.__version__)   # e.g. 2.32.3

Fazendo uma requisição GET

requests.get() envia uma requisição HTTP GET e retorna um objeto Response. Esta é a operação mais comum — usada para buscar dados de APIs, páginas web e arquivos.

import requests

response = requests.get("https://jsonplaceholder.typicode.com/todos/1")

print(response.status_code)   # 200
print(response.url)           # https://jsonplaceholder.typicode.com/todos/1
print(response.text)          # raw response body as a string

Saída esperada:

200
https://jsonplaceholder.typicode.com/todos/1
{"userId": 1, "id": 1, "title": "delectus aut autem", "completed": false}

Lendo a resposta como JSON

A maioria das APIs modernas retorna JSON. Chame .json() na resposta em vez de analisar .text manualmente — ele chama json.loads() por você e retorna um dict ou lista Python.

import requests

response = requests.get("https://jsonplaceholder.typicode.com/todos/1")
data = response.json()

print(data["title"])      # delectus aut autem
print(data["completed"])  # False

Consulte o capítulo Python JSON para detalhes sobre como os objetos Python mapeiam para tipos JSON.

Enviando parâmetros de consulta

Parâmetros de consulta são os pares chave-valor após o ? em uma URL, como ?q=python&page=2. Passe-os como um dict para o argumento params — o requests faz a codificação URL e os acrescenta automaticamente.

import requests

params = {
    "q": "python requests",
    "page": 1,
    "per_page": 5,
}

response = requests.get("https://httpbin.org/get", params=params)

# requests builds the full URL for you
print(response.url)
# https://httpbin.org/get?q=python+requests&page=1&per_page=5

Sempre use params= em vez de construir a URL manualmente — isso trata corretamente caracteres especiais e codificação.

Enviando cabeçalhos de requisição

Os cabeçalhos carregam metadados: tokens de autenticação, preferências de tipo de conteúdo, chaves de API e muito mais. Passe-os como um dict para headers=:

import requests

headers = {
    "Accept": "application/json",
    "Authorization": "Bearer my-api-token",
    "User-Agent": "MyApp/1.0",
}

response = requests.get("https://httpbin.org/headers", headers=headers)
print(response.json())

Cabeçalhos comuns que você vai enviar:

CabeçalhoFinalidade
AuthorizationToken de autenticação (Bearer, Basic, etc.)
Content-TypeFormato do corpo da requisição (ex.: application/json)
AcceptFormato que você quer receber do servidor
User-AgentIdentifica seu cliente para o servidor
X-API-KeyChave de API em um cabeçalho personalizado (varia por serviço)

Fazendo uma requisição POST

requests.post() envia dados ao servidor — usado para criar recursos, enviar formulários ou chamar ações.

Enviando JSON

Passe um dict Python para json=. A biblioteca o serializa e define o cabeçalho Content-Type: application/json automaticamente:

import requests

payload = {
    "title": "Buy groceries",
    "completed": False,
    "userId": 1,
}

response = requests.post(
    "https://jsonplaceholder.typicode.com/todos",
    json=payload,
)

print(response.status_code)   # 201 Created
print(response.json())

Saída esperada:

201
{'title': 'Buy groceries', 'completed': False, 'userId': 1, 'id': 201}

Enviando dados de formulário

Algumas APIs ou formulários HTML esperam dados application/x-www-form-urlencoded. Use data= em vez de json=:

import requests

form_data = {
    "username": "alice",
    "password": "secret",
}

response = requests.post("https://httpbin.org/post", data=form_data)
print(response.status_code)

Enviando arquivos (upload multipart)

Para fazer upload de um arquivo, abra-o em modo binário e passe-o através de files=:

import requests

with open("report.pdf", "rb") as f:
    response = requests.post(
        "https://httpbin.org/post",
        files={"file": f},
    )

print(response.status_code)

O requests codifica o upload como multipart/form-data, que é o que a maioria dos endpoints de upload de arquivos espera.

Outros métodos HTTP

As APIs REST usam diferentes verbos HTTP para diferentes operações. O requests fornece uma função por método:

import requests

base = "https://jsonplaceholder.typicode.com/todos/1"

# Update a resource (replace entirely)
response = requests.put(base, json={"title": "Updated", "completed": True, "userId": 1})
print(response.status_code)   # 200

# Partial update
response = requests.patch(base, json={"completed": True})
print(response.status_code)   # 200

# Delete a resource
response = requests.delete(base)
print(response.status_code)   # 200

Tratamento de erros

Verificando códigos de status

O código de status HTTP informa se a requisição foi bem-sucedida. Os grupos mais importantes:

IntervaloSignificado
2xxSucesso (200 OK, 201 Created, 204 No Content)
3xxRedirecionamento (tratado automaticamente pelo requests)
4xxErro do cliente (400 Bad Request, 401 Unauthorized, 404 Not Found)
5xxErro do servidor (500 Internal Server Error, 503 Service Unavailable)

raise_for_status()

Chamar .raise_for_status() em uma resposta lança uma exceção HTTPError automaticamente se o código de status for 4xx ou 5xx. Esta é a forma mais limpa de falhar rapidamente em respostas ruins:

import requests

response = requests.get("https://jsonplaceholder.typicode.com/todos/99999")

try:
    response.raise_for_status()
    data = response.json()
    print(data)
except requests.exceptions.HTTPError as err:
    print(f"HTTP error: {err}")

Sem raise_for_status(), uma resposta 404 parece um sucesso — seu código lê um corpo de erro e o processa silenciosamente.

Tratando erros de rede

Falhas no nível de rede (falha de DNS, conexão recusada, timeout) lançam requests.exceptions.ConnectionError ou requests.exceptions.Timeout. Capture ambos com a classe base requests.exceptions.RequestException:

import requests

try:
    response = requests.get("https://api.example.com/data", timeout=5)
    response.raise_for_status()
    data = response.json()
except requests.exceptions.Timeout:
    print("The request timed out — server took too long to respond.")
except requests.exceptions.ConnectionError:
    print("Could not connect — check your network or the URL.")
except requests.exceptions.HTTPError as err:
    print(f"HTTP error {response.status_code}: {err}")
except requests.exceptions.RequestException as err:
    print(f"Unexpected error: {err}")

Este padrão cobre toda a hierarquia de exceções: timeout, conexão, erro HTTP e a classe base catch-all. Consulte Python try/except para uma análise mais aprofundada do tratamento de exceções em Python.

Sempre defina um timeout

Por padrão, o requests esperará indefinidamente se o servidor nunca responder. Sempre passe timeout= para evitar que programas fiquem travados:

# timeout=(connect_timeout, read_timeout) in seconds
response = requests.get("https://api.example.com/data", timeout=(3, 10))

A forma de tupla define o timeout de conexão e o timeout de leitura separadamente. Um timeout de leitura de 10 segundos significa "aguardar até 10 segundos entre bytes após a conexão ser estabelecida."

Usando sessões

Um objeto requests.Session persiste configurações — cabeçalhos, cookies, autenticação — em múltiplas requisições para o mesmo host. Ele também reutiliza a conexão TCP subjacente (connection pooling), o que é mais rápido do que criar uma nova conexão para cada chamada.

import requests

with requests.Session() as session:
    # Set headers once — every request in this session will include them
    session.headers.update({
        "Authorization": "Bearer my-api-token",
        "Accept": "application/json",
    })

    # All requests reuse the connection and headers
    r1 = session.get("https://api.example.com/users")
    r2 = session.get("https://api.example.com/posts")
    r3 = session.post("https://api.example.com/todos", json={"title": "New"})

    print(r1.status_code, r2.status_code, r3.status_code)

Use uma sessão sempre que estiver fazendo mais de uma requisição ao mesmo servidor.

Inspecionando a resposta

O objeto Response expõe tudo o que o servidor enviou de volta:

import requests

response = requests.get("https://httpbin.org/get")

print(response.status_code)      # 200
print(response.reason)           # OK
print(response.headers)          # dict of response headers
print(response.headers["Content-Type"])  # application/json
print(response.encoding)         # utf-8
print(response.elapsed)          # how long the request took
print(response.url)              # final URL (after redirects)

Para conteúdo binário (imagens, PDFs), use response.content (retorna bytes) em vez de response.text:

import requests

response = requests.get("https://httpbin.org/image/png")
with open("image.png", "wb") as f:
    f.write(response.content)

Autenticação

HTTP Basic Auth

Passe uma tupla (username, password) para auth=:

import requests

response = requests.get(
    "https://httpbin.org/basic-auth/alice/secret",
    auth=("alice", "secret"),
)
print(response.status_code)   # 200

Bearer token (chaves de API)

A maioria das APIs modernas usa um bearer token no cabeçalho Authorization:

import requests

headers = {"Authorization": "Bearer eyJhbGciOiJIUzI1NiIs..."}
response = requests.get("https://api.example.com/me", headers=headers)

Nunca codifique tokens diretamente nos arquivos de código-fonte. Carregue-os a partir de variáveis de ambiente ou de um gerenciador de segredos:

import os
import requests

token = os.environ["API_TOKEN"]
headers = {"Authorization": f"Bearer {token}"}
response = requests.get("https://api.example.com/me", headers=headers)

Exemplo do mundo real — GitHub API

Este exemplo demonstra um padrão completo: sessão, autenticação bearer, paginação, tratamento de erros e análise de JSON:

import os
import requests

GITHUB_TOKEN = os.environ.get("GITHUB_TOKEN", "")
BASE_URL = "https://api.github.com"

with requests.Session() as session:
    session.headers.update({
        "Authorization": f"Bearer {GITHUB_TOKEN}",
        "Accept": "application/vnd.github+json",
        "X-GitHub-Api-Version": "2022-11-28",
    })

    try:
        # Fetch the first page of public repos for a user
        response = session.get(
            f"{BASE_URL}/users/torvalds/repos",
            params={"per_page": 5, "sort": "updated"},
            timeout=10,
        )
        response.raise_for_status()
        repos = response.json()

        for repo in repos:
            print(f"{repo['name']:40s}{repo['stargazers_count']}")

    except requests.exceptions.HTTPError as err:
        print(f"GitHub API error: {err}")
    except requests.exceptions.RequestException as err:
        print(f"Network error: {err}")

Este padrão — sessão com cabeçalhos compartilhados, raise_for_status(), try/except com escopo definido — é a abordagem pronta para produção para qualquer cliente de API que você escreva.

Requisições concorrentes com asyncio

Para programas que chamam muitos endpoints ao mesmo tempo, a biblioteca requests síncrona bloqueia a cada chamada. Mude para aiohttp (o equivalente assíncrono) e combine-o com o módulo asyncio do Python:

import asyncio
import aiohttp

async def fetch(session, url):
    async with session.get(url) as response:
        return await response.json()

async def main():
    urls = [
        "https://jsonplaceholder.typicode.com/todos/1",
        "https://jsonplaceholder.typicode.com/todos/2",
        "https://jsonplaceholder.typicode.com/todos/3",
    ]
    async with aiohttp.ClientSession() as session:
        results = await asyncio.gather(*[fetch(session, u) for u in urls])
    for r in results:
        print(r["title"])

asyncio.run(main())

Consulte o capítulo Python asyncio para entender como async/await funciona antes de adotar este padrão.

Referência rápida

TarefaCódigo
Requisição GETrequests.get(url)
GET com paramsrequests.get(url, params={"key": "val"})
GET com cabeçalhosrequests.get(url, headers={"Authorization": "Bearer token"})
POST com corpo JSONrequests.post(url, json={"key": "val"})
POST com dados de formuláriorequests.post(url, data={"key": "val"})
Upload de arquivorequests.post(url, files={"file": open("f.pdf", "rb")})
PUT / PATCH / DELETErequests.put/patch/delete(url, json=...)
Verificar statusresponse.status_code
Lançar exceção em 4xx/5xxresponse.raise_for_status()
Analisar corpo JSONresponse.json()
Corpo como textoresponse.text
Corpo como bytesresponse.content
Definir timeoutrequests.get(url, timeout=5)
Reutilizar conexãowith requests.Session() as s: ...

Capítulos relacionados

Prática

Prática
Which argument sends a Python dict as a JSON body in a POST request?
Which argument sends a Python dict as a JSON body in a POST request?
Was this page helpful?