APIs REST são a principal forma de integrar sistemas e acessar dados externos — CRMs, ERPs, plataformas de e-commerce, dados governamentais. A biblioteca requests é o padrão de mercado em Python para consumir APIs HTTP: simples, poderosa e bem documentada.

Instalação

pip install requests pandas

GET básico

import requests
import pandas as pd

# Requisição simples:
resp = requests.get("https://api.exemplo.com/vendas")
print(resp.status_code)   # 200 = OK
print(resp.json())        # converte JSON para dict/list Python

# Com parâmetros de query (?ano=2026®iao=sul):
resp = requests.get(
    "https://api.exemplo.com/vendas",
    params={"ano": 2026, "regiao": "sul"}
)

Autenticação

import os

TOKEN = os.environ.get("API_TOKEN")

# Bearer token (o mais comum):
headers = {"Authorization": f"Bearer {TOKEN}"}
resp = requests.get("https://api.exemplo.com/dados", headers=headers)

# API Key no header:
headers = {"X-API-Key": TOKEN}

# Basic Auth:
resp = requests.get(url, auth=("usuario", "senha"))

Tratamento de erros HTTP

resp = requests.get("https://api.exemplo.com/dados", headers=headers)

# raise_for_status() lança HTTPError para 4xx/5xx:
try:
    resp.raise_for_status()
    dados = resp.json()
except requests.exceptions.HTTPError as e:
    print(f"Erro HTTP {resp.status_code}: {e}")
except requests.exceptions.ConnectionError:
    print("Falha de conexão — sem rede ou servidor indisponível")
except requests.exceptions.Timeout:
    print("A requisição excedeu o tempo limite")
except requests.exceptions.RequestException as e:
    print(f"Erro na requisição: {e}")

POST: enviando dados

payload = {
    "cliente": "Empresa ABC",
    "valor": 12500,
    "produto": "Licença Pro"
}

resp = requests.post(
    "https://api.exemplo.com/pedidos",
    json=payload,             # serializa dict para JSON e define Content-Type
    headers=headers
)
resp.raise_for_status()
pedido_criado = resp.json()
print(f"Pedido criado: ID {pedido_criado['id']}")

Paginação: coletando todos os registros

todos = []
pagina = 1

while True:
    resp = requests.get(
        "https://api.exemplo.com/vendas",
        params={"page": pagina, "per_page": 100},
        headers=headers
    )
    resp.raise_for_status()
    dados = resp.json()

    registros = dados.get("data", [])
    if not registros:
        break   # sem mais páginas

    todos.extend(registros)
    pagina += 1

    # Proteção contra loops infinitos:
    if pagina > dados.get("total_pages", 999):
        break

df = pd.DataFrame(todos)
print(f"Total de registros coletados: {len(df)}")

Session: reutilizando conexão e headers

# Session mantém headers e cookies automaticamente entre requisições:
with requests.Session() as s:
    s.headers.update({"Authorization": f"Bearer {TOKEN}"})
    s.headers.update({"Accept": "application/json"})

    vendas = s.get("https://api.exemplo.com/vendas").json()
    clientes = s.get("https://api.exemplo.com/clientes").json()

Perguntas frequentes

Como lidar com rate limiting (429 Too Many Requests)?

Use time.sleep() entre requisições ou implemente backoff exponencial com a biblioteca tenacity: @retry(wait=wait_exponential(min=1, max=60), stop=stop_after_attempt(5)).

requests vs httpx: quando usar cada um?

requests é síncrono — adequado para scripts e pipelines sequenciais. httpx com asyncio é para cenários onde você precisa fazer dezenas/centenas de chamadas em paralelo e cada milissegundo conta.