Tecnologia

Como consumir uma API com Python usando Requests: guia prático

Aprenda a acessar dados de qualidade do ar com Python e Requests. Neste guia prático, você fará uma requisição a uma API, enviará parâmetros e trabalhará com a resposta em JSON.

Ilustração de código Python usando a biblioteca Requests para acessar dados de qualidade do ar por meio de uma API.

Como consumir uma API com Python usando Requests: guia prático#

APIs permitem que programas acessem dados e funcionalidades disponibilizados por outros sistemas. Com elas, podemos consultar informações meteorológicas, indicadores econômicos, dados geográficos e diversas outras fontes sem copiar conteúdo manualmente de páginas da web.

Neste guia, você aprenderá a consumir uma API com Python usando a biblioteca requests. Como exemplo, consultaremos dados de qualidade do ar para a cidade de São Paulo por meio da Air Quality API da Open-Meteo.

O mesmo exemplo será executado de duas formas:

  • em um projeto local gerenciado com o uv;
  • em um notebook organizado no Google Colab.

Ao final do artigo, você saberá como:

  • enviar uma requisição HTTP;
  • passar parâmetros para uma API;
  • verificar o código de status;
  • interpretar uma resposta em JSON;
  • acessar valores específicos da resposta;
  • tratar erros de conexão e respostas inválidas;
  • compreender o que os dados ambientais consultados representam.

O que é uma API?#

API é a sigla para Application Programming Interface, ou Interface de Programação de Aplicações.

Uma API estabelece regras para que diferentes sistemas consigam trocar informações. Um programa envia uma requisição para determinado endereço, o servidor processa o pedido e devolve uma resposta.

Podemos representar esse fluxo da seguinte maneira:

TEXT
Programa em Python → requisição HTTP → API → resposta com dados

No nosso exemplo, o programa enviará uma localização geográfica e uma lista de variáveis ambientais. A API devolverá os dados correspondentes em formato JSON.

Alguns termos aparecerão ao longo do artigo:

  • endpoint: endereço de um recurso disponibilizado pela API;
  • requisição: pedido enviado ao servidor;
  • parâmetros: valores que especificam o conteúdo da consulta;
  • resposta: conteúdo devolvido pela API;
  • código de status: número que informa o resultado da requisição;
  • JSON: formato textual usado para representar dados estruturados.

O que é uma requisição HTTP?#

HTTP é um protocolo de comunicação utilizado na web. Ele define métodos que indicam qual operação desejamos realizar.

Entre os métodos mais comuns estão:

  • GET, para solicitar dados;
  • POST, para enviar novos dados;
  • PUT ou PATCH, para alterar dados;
  • DELETE, para excluir dados.

Neste artigo, usaremos apenas o método GET, pois nosso objetivo é consultar informações.

Com a biblioteca Requests, uma requisição GET pode ser feita assim:

PYTHON
import requests

resposta = requests.get("https://exemplo.com/api")

A função requests.get() envia a requisição e devolve um objeto Response. Esse objeto contém o código de status, o corpo da resposta, a URL final, os cabeçalhos e outras informações da comunicação HTTP. A documentação oficial também mostra como usar o argumento params para enviar parâmetros de consulta.

A API utilizada no exemplo#

Usaremos o endpoint de qualidade do ar da Open-Meteo:

TEXT
https://air-quality-api.open-meteo.com/v1/air-quality

O endpoint recebe coordenadas geográficas no sistema WGS84 e uma lista de variáveis ambientais. Em uma consulta bem-sucedida, os dados são devolvidos em JSON. Os parâmetros latitude e longitude são obrigatórios, enquanto current permite escolher quais condições atuais serão retornadas.

Para São Paulo, usaremos aproximadamente estas coordenadas:

TEXT
Latitude:  -23.5505
Longitude: -46.6333

Também informaremos:

TEXT
Timezone: America/Sao_Paulo

O parâmetro timezone faz com que os horários sejam apresentados no fuso solicitado. Sem ele, o padrão da API é GMT.

Antes do código: o que esses dados representam?#

Antes de consultar os valores, é importante compreender sua origem.

Os dados fornecidos por essa API são provenientes de modelos atmosféricos do Copernicus Atmosphere Monitoring Service, o CAMS. Eles não representam necessariamente uma medição direta realizada por um sensor instalado exatamente nas coordenadas informadas.

A Open-Meteo utiliza duas fontes principais:

  • CAMS European Air Quality Forecast, com grade aproximada de 11 quilômetros para a Europa;
  • CAMS Global Atmospheric Composition Forecast, com grade aproximada de 45 quilômetros para cobertura global.

Como São Paulo está fora do domínio europeu, a consulta depende do modelo global. A fonte global possui resolução temporal nativa de três horas e é atualizada a cada 12 horas, segundo a documentação da API.

A documentação também informa que as condições retornadas pelo parâmetro current são baseadas em dados do modelo disponibilizados em intervalos de 15 minutos. Isso deve ser interpretado como uma representação modelada do estado atual, e não como uma leitura bruta de um instrumento localizado naquele ponto.

A coordenada devolvida pode ser diferente#

A API seleciona uma célula da grade do modelo para responder à consulta. Por esse motivo, a latitude e a longitude presentes na resposta podem ser ligeiramente diferentes das coordenadas solicitadas.

A documentação explica que as coordenadas devolvidas representam o centro da célula utilizada e podem estar a alguns quilômetros do ponto informado.

Variáveis que consultaremos#

Solicitaremos cinco variáveis:

VariávelChave na APIUnidade
Índice europeu de qualidade do areuropean_aqiEAQI
Material particulado PM10pm10µg/m³
Material particulado PM2.5pm2_5µg/m³
Dióxido de nitrogênionitrogen_dioxideµg/m³
Ozônioozoneµg/m³

PM10 representa partículas com diâmetro inferior a 10 micrômetros. PM2.5 representa partículas com diâmetro inferior a 2,5 micrômetros. A documentação descreve essas concentrações e os gases atmosféricos como valores próximos à superfície, aproximadamente 10 metros acima do solo.

O índice europeu de qualidade do ar#

O campo european_aqi é um índice consolidado. A Open-Meteo calcula índices individuais para diferentes poluentes e devolve, no índice geral, o maior deles.

A escala apresentada pela documentação é:

IntervaloClassificação
0 a 20Boa
Acima de 20 até 40Razoável
Acima de 40 até 60Moderada
Acima de 60 até 80Ruim
Acima de 80 até 100Muito ruim
Acima de 100Extremamente ruim

Essa é a escala europeia. Ela não deve ser confundida com classificações ou padrões regulatórios brasileiros.

Opção 1: projeto local com uv#

O uv é um gerenciador de projetos e dependências para Python. Em um projeto, ele trabalha com arquivos como pyproject.toml, .python-version e uv.lock, além de gerenciar automaticamente um ambiente virtual em .venv.

Instalando o uv#

No macOS ou Linux:

BASH
curl -LsSf https://astral.sh/uv/install.sh | sh

No Windows PowerShell:

POWERSHELL
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"

Os comandos acima são os instaladores disponibilizados na documentação oficial do projeto.

Verifique a instalação:

BASH
uv --version

Criando o projeto#

No terminal, execute:

BASH
uv init api-qualidade-ar
cd api-qualidade-ar

O uv init cria um novo projeto Python. Entre os arquivos iniciais estão main.py, pyproject.toml, .python-version e README.md.

Adicionando Requests#

Adicione a biblioteca ao projeto:

BASH
uv add requests

O comando registra a dependência no pyproject.toml, resolve uma versão compatível e atualiza o ambiente do projeto.

Código completo do projeto local#

Substitua o conteúdo de main.py pelo código abaixo.

PYTHON
import requests


API_URL = "https://air-quality-api.open-meteo.com/v1/air-quality"

VARIAVEIS = (
    "european_aqi",
    "pm10",
    "pm2_5",
    "nitrogen_dioxide",
    "ozone",
)

ROTULOS = {
    "european_aqi": "Índice europeu de qualidade do ar",
    "pm10": "PM10",
    "pm2_5": "PM2.5",
    "nitrogen_dioxide": "Dióxido de nitrogênio",
    "ozone": "Ozônio",
}


def consultar_qualidade_ar(
    latitude: float,
    longitude: float,
) -> tuple[dict, str]:
    """Consulta as condições atuais de qualidade do ar."""

    parametros = {
        "latitude": latitude,
        "longitude": longitude,
        "current": ",".join(VARIAVEIS),
        "timezone": "America/Sao_Paulo",
    }

    resposta = requests.get(
        API_URL,
        params=parametros,
        timeout=30,
    )

    resposta.raise_for_status()

    dados = resposta.json()

    return dados, resposta.url


def exibir_resultados(dados: dict) -> None:
    """Exibe os dados atuais e suas respectivas unidades."""

    dados_atuais = dados["current"]
    unidades = dados["current_units"]

    print("Qualidade do ar em São Paulo")
    print("=" * 40)

    print(f"Horário: {dados_atuais['time']}")
    print(
        "Célula do modelo: "
        f"{dados['latitude']}, {dados['longitude']}"
    )

    print()

    for variavel in VARIAVEIS:
        valor = dados_atuais[variavel]
        unidade = unidades[variavel]
        rotulo = ROTULOS[variavel]

        print(f"{rotulo}: {valor} {unidade}")


def main() -> None:
    latitude = -23.5505
    longitude = -46.6333

    try:
        dados, url_consulta = consultar_qualidade_ar(
            latitude=latitude,
            longitude=longitude,
        )

    except requests.exceptions.Timeout:
        print("A API demorou muito para responder.")

    except requests.exceptions.HTTPError as erro:
        print("A API devolveu um erro HTTP.")
        print(erro)

    except requests.exceptions.JSONDecodeError:
        print("A resposta não contém um JSON válido.")

    except requests.exceptions.RequestException as erro:
        print("Não foi possível realizar a requisição.")
        print(erro)

    else:
        print("URL enviada:")
        print(url_consulta)
        print()

        exibir_resultados(dados)


if __name__ == "__main__":
    main()

Execute o arquivo com:

BASH
uv run main.py

Antes de executar o comando, o uv run verifica se o arquivo de bloqueio e o ambiente estão sincronizados com o pyproject.toml. O programa é executado com as dependências gerenciadas pelo projeto, sem exigir a ativação manual do ambiente virtual.

Entendendo o código#

Endpoint e variáveis#

O endpoint é armazenado em uma constante:

PYTHON
API_URL = "https://air-quality-api.open-meteo.com/v1/air-quality"

As variáveis solicitadas são organizadas em uma tupla:

PYTHON
VARIAVEIS = (
    "european_aqi",
    "pm10",
    "pm2_5",
    "nitrogen_dioxide",
    "ozone",
)

Usar uma coleção evita repetir a mesma lista em diferentes partes do programa.

Parâmetros da requisição#

Dentro da função consultar_qualidade_ar, criamos um dicionário:

PYTHON
parametros = {
    "latitude": latitude,
    "longitude": longitude,
    "current": ",".join(VARIAVEIS),
    "timezone": "America/Sao_Paulo",
}

A expressão:

PYTHON
",".join(VARIAVEIS)

transforma a tupla em uma única string separada por vírgulas:

TEXT
european_aqi,pm10,pm2_5,nitrogen_dioxide,ozone

Esse é o formato aceito pelo parâmetro current.

Envio da requisição#

A requisição é feita com:

PYTHON
resposta = requests.get(
    API_URL,
    params=parametros,
    timeout=30,
)

O argumento params solicita que o Requests transforme o dicionário em parâmetros da URL. Assim, não precisamos montar manualmente os caracteres ?, & e =.

Podemos visualizar a URL final por meio de:

PYTHON
resposta.url

A documentação oficial do Requests apresenta esse mesmo mecanismo para envio de parâmetros em requisições GET.

Timeout#

O argumento:

PYTHON
timeout=30

impede que o programa aguarde indefinidamente por uma resposta.

No Requests, esse valor não representa necessariamente um limite absoluto para toda a execução. Ele controla o período de espera da comunicação quando nenhum dado é recebido pela conexão. A documentação recomenda definir um timeout nas requisições feitas por aplicações.

Código de status e erros HTTP#

O método:

PYTHON
resposta.raise_for_status()

gera uma exceção quando o servidor devolve um código HTTP de erro.

Alguns códigos comuns são:

CódigoSignificado geral
200Requisição processada com sucesso
400Parâmetros ou formato da requisição inválidos
404Recurso não encontrado
500Erro interno do servidor

No caso específico da Open-Meteo, parâmetros inválidos produzem uma resposta JSON acompanhada de status HTTP 400.

Conversão do JSON#

Depois da verificação do status, usamos:

PYTHON
dados = resposta.json()

O método .json() converte a resposta para estruturas do Python, geralmente dicionários e listas.

É importante executar raise_for_status() antes de considerar a consulta bem-sucedida. Uma API pode devolver uma mensagem de erro em JSON, portanto o sucesso de .json() não significa que o servidor aceitou a requisição.

Se o conteúdo não for um JSON válido, o Requests gera requests.exceptions.JSONDecodeError.

Estrutura dos dados#

Uma resposta com condições atuais apresenta uma estrutura semelhante a esta:

PYTHON
{
    "latitude": -23.5,
    "longitude": -46.5,
    "timezone": "America/Sao_Paulo",
    "current_units": {
        "time": "iso8601",
        "european_aqi": "EAQI",
        "pm10": "μg/m³",
        "pm2_5": "μg/m³",
        "nitrogen_dioxide": "μg/m³",
        "ozone": "μg/m³",
    },
    "current": {
        "time": "2026-07-15T10:15",
        "european_aqi": 35,
        "pm10": 18.2,
        "pm2_5": 9.4,
        "nitrogen_dioxide": 12.1,
        "ozone": 64.8,
    },
}

Os números acima são apenas uma representação da estrutura. Os valores reais mudam conforme a data, o horário, a localização e a atualização do modelo.

Para acessar o PM2.5:

PYTHON
dados["current"]["pm2_5"]

Para acessar sua unidade:

PYTHON
dados["current_units"]["pm2_5"]

Separar valores e unidades evita assumir que todas as APIs usam o mesmo sistema de medidas.

Opção 2: Google Colab#

O Google Colab é um serviço hospedado de notebooks Jupyter. Ele permite executar Python no navegador sem configurar previamente um ambiente local. Os recursos das sessões são temporários e os limites disponíveis podem variar.

Em um notebook bem organizado, cada célula deve representar uma etapa coerente do trabalho. Não colocaremos instalação, funções, consulta, inspeção e apresentação dos resultados em uma única célula.

A estrutura será:

  1. apresentação do notebook;
  2. instalação da dependência;
  3. importações e configurações;
  4. função de consulta;
  5. execução da requisição;
  6. inspeção da resposta;
  7. apresentação dos resultados.

Célula de texto: título e objetivo#

Crie uma célula de texto no início do notebook:

MARKDOWN
# Consulta de qualidade do ar com Python

Este notebook utiliza a biblioteca Requests e a Air Quality API
da Open-Meteo para consultar as condições atuais de qualidade do ar
na cidade de São Paulo.

Essa célula documenta o objetivo do notebook sem misturar explicação e execução.

Célula 1: instalação da dependência#

Crie uma célula de código apenas para as dependências:

PYTHON
%pip install -q requests

O comando %pip instala o pacote no ambiente associado ao kernel do notebook.

Depois de executar essa célula, não é necessário repeti-la durante a mesma sessão.

Célula 2: importações e configurações#

PYTHON
import requests


API_URL = "https://air-quality-api.open-meteo.com/v1/air-quality"

LATITUDE = -23.5505
LONGITUDE = -46.6333

VARIAVEIS = (
    "european_aqi",
    "pm10",
    "pm2_5",
    "nitrogen_dioxide",
    "ozone",
)

Essa célula concentra importações, constantes e configurações que serão usadas no restante do notebook.

Célula 3: função de consulta#

PYTHON
def consultar_qualidade_ar(
    latitude: float,
    longitude: float,
) -> tuple[dict, str]:
    """Consulta as condições atuais de qualidade do ar."""

    parametros = {
        "latitude": latitude,
        "longitude": longitude,
        "current": ",".join(VARIAVEIS),
        "timezone": "America/Sao_Paulo",
    }

    resposta = requests.get(
        API_URL,
        params=parametros,
        timeout=30,
    )

    resposta.raise_for_status()

    return resposta.json(), resposta.url

A célula apenas define a função. Nenhuma requisição é feita até que a função seja chamada.

Célula 4: execução da requisição#

PYTHON
try:
    dados, url_consulta = consultar_qualidade_ar(
        latitude=LATITUDE,
        longitude=LONGITUDE,
    )

except requests.exceptions.Timeout:
    raise RuntimeError(
        "A API demorou muito para responder."
    )

except requests.exceptions.HTTPError as erro:
    raise RuntimeError(
        f"A API devolveu um erro HTTP: {erro}"
    ) from erro

except requests.exceptions.JSONDecodeError as erro:
    raise RuntimeError(
        "A resposta não contém um JSON válido."
    ) from erro

except requests.exceptions.RequestException as erro:
    raise RuntimeError(
        f"Não foi possível realizar a requisição: {erro}"
    ) from erro

Em um notebook, gerar uma exceção com uma mensagem clara é útil porque interrompe a sequência de execução. Assim, as células seguintes não tentam trabalhar com uma variável dados inexistente ou incompleta.

Célula 5: inspeção da requisição#

Primeiro, visualize a URL construída:

PYTHON
print(url_consulta)

Depois, veja as chaves principais da resposta:

PYTHON
dados.keys()

O resultado será semelhante a:

TEXT
dict_keys([
    'latitude',
    'longitude',
    'generationtime_ms',
    'utc_offset_seconds',
    'timezone',
    'timezone_abbreviation',
    'elevation',
    'current_units',
    'current'
])

Também podemos inspecionar apenas os dados atuais:

PYTHON
dados["current"]

E suas unidades:

PYTHON
dados["current_units"]

Essa etapa é importante em análise de dados: antes de escrever transformações ou visualizações, examine a estrutura recebida.

Célula 6: seleção dos dados#

PYTHON
dados_atuais = dados["current"]
unidades = dados["current_units"]

Agora, consulte valores individuais:

PYTHON
dados_atuais["pm2_5"]
PYTHON
unidades["pm2_5"]

As células pequenas permitem examinar resultados intermediários e entender melhor o formato da resposta.

Célula 7: apresentação organizada#

PYTHON
ROTULOS = {
    "european_aqi": "Índice europeu de qualidade do ar",
    "pm10": "PM10",
    "pm2_5": "PM2.5",
    "nitrogen_dioxide": "Dióxido de nitrogênio",
    "ozone": "Ozônio",
}


print("Qualidade do ar em São Paulo")
print("=" * 40)

print(f"Horário: {dados_atuais['time']}")
print(
    "Célula do modelo: "
    f"{dados['latitude']}, {dados['longitude']}"
)

print()

for variavel in VARIAVEIS:
    rotulo = ROTULOS[variavel]
    valor = dados_atuais[variavel]
    unidade = unidades[variavel]

    print(f"{rotulo}: {valor} {unidade}")

O notebook fica dividido por responsabilidades:

  • uma célula prepara o ambiente;
  • outra define as configurações;
  • outra contém a lógica de acesso;
  • outra executa a requisição;
  • outras inspecionam e apresentam os dados.

Essa separação facilita testes, correções e reutilização.

Testando outras localidades#

Para consultar outra cidade, altere apenas as coordenadas.

Exemplo aproximado para o Rio de Janeiro:

PYTHON
LATITUDE = -22.9068
LONGITUDE = -43.1729

Depois, execute novamente as células de consulta, seleção e apresentação.

Uma melhoria futura seria usar uma API de geocodificação para converter nomes de cidades em coordenadas automaticamente. Entretanto, isso adicionaria outra API ao fluxo e desviaria o foco deste primeiro artigo.

Solicitando outras variáveis#

A documentação da Open-Meteo disponibiliza outras variáveis, como:

  • monóxido de carbono;
  • dióxido de enxofre;
  • profundidade óptica de aerossóis;
  • poeira;
  • índice UV;
  • pólen, em regiões onde esses dados estão disponíveis.

Para acrescentar monóxido de carbono, inclua sua chave:

PYTHON
VARIAVEIS = (
    "european_aqi",
    "pm10",
    "pm2_5",
    "carbon_monoxide",
    "nitrogen_dioxide",
    "ozone",
)

Também será necessário acrescentar um rótulo:

PYTHON
ROTULOS["carbon_monoxide"] = "Monóxido de carbono"

As variáveis disponíveis, suas unidades e limitações geográficas estão descritas na documentação oficial da Air Quality API.

Erros comuns#

Esquecer o timeout#

Evite:

PYTHON
requests.get(API_URL, params=parametros)

Prefira:

PYTHON
requests.get(
    API_URL,
    params=parametros,
    timeout=30,
)

Sem um timeout, uma falha de rede pode fazer o programa aguardar indefinidamente.

Montar manualmente toda a URL#

É possível concatenar strings, mas isso aumenta o risco de erros de codificação e separação de parâmetros.

Evite:

PYTHON
url = (
    API_URL
    + "?latitude="
    + str(latitude)
    + "&longitude="
    + str(longitude)
)

Prefira:

PYTHON
parametros = {
    "latitude": latitude,
    "longitude": longitude,
}

resposta = requests.get(
    API_URL,
    params=parametros,
    timeout=30,
)

Tratar .json() como confirmação de sucesso#

Este código é insuficiente:

PYTHON
dados = resposta.json()

Uma API pode devolver detalhes de erro em JSON. Verifique primeiro o status:

PYTHON
resposta.raise_for_status()
dados = resposta.json()

A documentação do Requests alerta explicitamente que a decodificação bem-sucedida do JSON não comprova que a requisição HTTP foi aceita.

Interpretar o resultado como leitura de um sensor local#

Os dados da consulta são estimativas de um modelo atmosférico em uma célula de grade. Eles podem ser úteis para estudos exploratórios, aplicações educacionais, visualizações e acompanhamento geral, mas não substituem automaticamente dados de estações locais ou fontes oficiais voltadas a decisões regulatórias.

O que aprendemos#

Neste artigo, utilizamos Python e Requests para consumir uma API ambiental.

O fluxo completo foi:

  1. identificar o endpoint;
  2. definir as coordenadas e variáveis;
  3. organizar os parâmetros em um dicionário;
  4. enviar a requisição com requests.get();
  5. limitar o tempo de espera com timeout;
  6. verificar erros HTTP com raise_for_status();
  7. converter a resposta com .json();
  8. examinar a estrutura devolvida;
  9. acessar valores e unidades;
  10. tratar erros de rede e decodificação.

Também executamos o exemplo em dois ambientes.

No projeto local, o uv gerenciou a dependência, o ambiente virtual e o arquivo de bloqueio. No Google Colab, dividimos o notebook em células pequenas e especializadas, preservando uma sequência clara de preparação, consulta, inspeção e apresentação.

Por fim, analisamos a origem dos dados. Os valores da Open-Meteo são derivados de modelos CAMS e representam uma célula de grade atmosférica. Portanto, devem ser interpretados como estimativas modeladas, e não necessariamente como medições realizadas por uma estação no ponto consultado.

Referências oficiais#