O que é uma API e em que consiste extrair dados dela
API (Application Programming Interface) — é a interface pela qual um programa se comunica com outro e obtém dados ou executa ações sem conhecer o funcionamento interno dele. Na prática, «trabalhar com uma API» significa quase sempre consumir uma API web sobre o protocolo HTTP: o cliente envia uma requisição a um endereço específico (o endpoint) e o servidor devolve uma resposta.
Quando falamos de extrair dados via API, referimo-nos ao ciclo completo de obtenção e parsing dos dados de um serviço externo:
- Construir e enviar uma requisição HTTP correta.
- Receber a resposta do servidor.
- Verificar o código de status e os cabeçalhos.
- Parsear o corpo da resposta (quase sempre JSON) e convertê-lo em objetos do programa.
- Tratar os erros, as novas tentativas e a paginação.
Veja também: o artigo «Parsing de JSON» foca na etapa específica de parsear os dados, ou seja, em converter uma string JSON em objetos. O presente artigo cobre o processo completo de trabalho com uma API, do qual esse parsing é apenas uma parte.
Diferentemente do web scraping de HTML, o trabalho com uma API se apoia em respostas estruturadas em um formato legível por máquina; por isso é mais confiável, mais estável e quase sempre preferível quando o serviço dispõe de uma API oficial.
Anatomia de uma requisição HTTP
Toda requisição a uma API web se compõe de várias partes.
Método (verbo HTTP)
O método descreve a intenção da requisição:
- GET — obter dados (não modifica o estado).
- POST — criar um recurso novo ou enviar dados.
- PUT / PATCH — atualizar um recurso (por completo / parcialmente).
- DELETE — excluir um recurso.
URL e parâmetros de consulta
O endereço do endpoint pode incluir parâmetros de consulta para filtrar, ordenar e paginar:
https://api.example.com/users?role=admin&page=2&limit=50Cabeçalhos (headers)
Os cabeçalhos transportam os metadados da requisição. Os mais importantes ao trabalhar com uma API:
Authorization— dados de autenticação (token, chave).Content-Type— formato do corpo enviado (por exemplo,application/json).Accept— formato no qual o cliente deseja receber a resposta.User-Agent— identificador do cliente.
Corpo da requisição (body)
Em POST/PUT/PATCH, os dados viajam no corpo, em regra como uma string JSON que antes é preciso serializar a partir dos objetos do programa.
Anatomia da resposta e seu parsing
A resposta do servidor consiste em um código de status, cabeçalhos e corpo.
Códigos de status
Antes de parsear o corpo, é preciso verificar o código de status:
- 2xx — sucesso (
200 OK,201 Created,204 No Content). - 3xx — redirecionamento.
- 4xx — erro do lado do cliente (
400requisição incorreta,401não autenticado,403proibido,404não encontrado,429requisições demais). - 5xx — erro do lado do servidor.
Só faz sentido parsear o corpo como dados válidos com códigos 2xx. Ainda assim, o corpo de um erro também costuma conter um JSON útil com a descrição do problema.
Cabeçalhos da resposta
Dos cabeçalhos da resposta extraem-se informações importantes: Content-Type (formato do corpo), parâmetros de paginação, limites de requisições (X-RateLimit-Remaining) e diretivas de cache.
Corpo da resposta
O corpo são os dados em si. Na maioria das APIs é JSON, que precisa ser parseado (veja «Parsing de JSON»). Com menor frequência aparecem outros formatos de texto — XML (veja «Parsing de XML») e CSV (veja «Parsing de CSV») —, além de formatos binários.
Autenticação e autorização
A maioria das APIs exige provar que o cliente tem direito de acesso. Os métodos mais difundidos:
- Chave de API — uma string simples que é enviada em um cabeçalho ou em um parâmetro da requisição.
- Token Bearer / OAuth 2.0 — um token no cabeçalho
Authorization: Bearer <token>; a abordagem mais comum nas APIs modernas. - Basic Auth — usuário e senha em forma codificada.
- HMAC / assinatura da requisição — a requisição é assinada com um segredo; usa-se em APIs de pagamentos e de nuvem.
Importante: as chaves e os tokens são segredos. Não devem ser guardados no código nem enviados ao repositório; utilize variáveis de ambiente ou cofres protegidos.
Dificuldades ao trabalhar com uma API
A extração de dados via API raramente se reduz a uma única requisição. Estas são as dificuldades típicas para as quais vale se preparar.
Paginação
Uma API quase nunca entrega as coleções grandes de uma só vez: os dados são divididos em páginas. Os modelos principais:
- Offset/limit —
?page=2&limit=50ou?offset=100&limit=50. - Por cursor — a resposta inclui um ponteiro para a página seguinte (
next_cursor), que é enviado na requisição seguinte. - Keyset — a página seguinte é solicitada a partir do valor do último elemento (por exemplo, seu
idou sua data).
Para reunir todos os dados, é preciso um laço que percorra as páginas até esgotá-las.
Limite de frequência de requisições (rate limiting)
Os serviços limitam o número de requisições por período. Ao ultrapassá-lo, chega o código 429. A solução correta é vigiar os cabeçalhos de limites e aplicar um backoff exponencial com novas tentativas.
Redes pouco confiáveis e novas tentativas
As requisições de rede podem falhar por timeout ou por erros temporários 5xx. Para ganhar robustez, aplicam-se:
- timeouts razoáveis;
- novas tentativas (retry) para requisições idempotentes, com esperas crescentes;
- o padrão circuit breaker diante de falhas sistemáticas.
Dados mutáveis e validação
A resposta de uma API pode chegar incompleta, com campos null ou com a estrutura alterada após uma atualização de versão. Nunca convém confiar cegamente na estrutura da resposta: é preciso verificar os campos e validar os dados.
Versionamento
As APIs evoluem e suas versões mudam (/v1/, /v2/). Vale fixar uma versão específica e acompanhar os avisos de descontinuação (deprecation).
Aninhamento e formatos diversos
Os dados úteis costumam estar escondidos no fundo de uma estrutura aninhada (data.items[0].attributes.name). Às vezes, em vez de JSON, chega XML (veja «Parsing de XML») ou CSV (veja «Parsing de CSV»), o que exige outro parser.
Exemplos de implementação em várias linguagens
A seguir, exemplos mínimos do ciclo completo: requisição à API, verificação do status e parsing da resposta JSON. Por uniformidade, usa-se o endpoint fictício https://api.example.com/users/42.
Python
A popular biblioteca requests cuida tanto da requisição quanto do parsing do JSON.
import requests
url = "https://api.example.com/users/42"
headers = {"Authorization": "Bearer YOUR_TOKEN"}
response = requests.get(url, headers=headers, timeout=10)
# Verificação do status
response.raise_for_status() # lança uma exceção com 4xx/5xx
# Parsing da resposta JSON para um dicionário
user = response.json()
print(user["name"])Exemplo de coleta de todas as páginas (offset/limit):
def fetch_all_users():
users, page = [], 1
while True:
resp = requests.get(
"https://api.example.com/users",
params={"page": page, "limit": 50},
timeout=10,
)
resp.raise_for_status()
batch = resp.json()["data"]
if not batch:
break
users.extend(batch)
page += 1
return usersJavaScript (Node.js / navegador)
O fetch integrado devolve uma promise; o JSON é parseado com o método .json().
const url = "https://api.example.com/users/42";
const response = await fetch(url, {
headers: { Authorization: "Bearer YOUR_TOKEN" },
});
// Verificação do status
if (!response.ok) {
throw new Error(`Erro de API: ${response.status}`);
}
// Parsing do JSON
const user = await response.json();
console.log(user.name);Exemplo de envio de dados (POST):
const response = await fetch("https://api.example.com/users", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer YOUR_TOKEN",
},
body: JSON.stringify({ name: "Ana", role: "admin" }), // serialização
});
const created = await response.json();Java
O Java moderno inclui um HttpClient integrado; para parsear o JSON, usa-se uma biblioteca (aqui, Jackson).
import java.net.URI;
import java.net.http.*;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.JsonNode;
public class ApiExample {
public static void main(String[] args) throws Exception {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/users/42"))
.header("Authorization", "Bearer YOUR_TOKEN")
.GET()
.build();
HttpResponse<String> response =
client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() != 200) {
throw new RuntimeException("Erro de API: " + response.statusCode());
}
ObjectMapper mapper = new ObjectMapper();
JsonNode user = mapper.readTree(response.body());
System.out.println(user.get("name").asText());
}
}Go
A biblioteca padrão oferece tanto o cliente HTTP (net/http) quanto o parser (encoding/json).
package main
import (
"encoding/json"
"fmt"
"net/http"
)
type User struct {
Name string `json:"name"`
}
func main() {
req, _ := http.NewRequest("GET", "https://api.example.com/users/42", nil)
req.Header.Set("Authorization", "Bearer YOUR_TOKEN")
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
panic(fmt.Sprintf("Erro de API: %d", resp.StatusCode))
}
var user User
json.NewDecoder(resp.Body).Decode(&user) // parsing do corpo em fluxo
fmt.Println(user.Name)
}C
No .NET, usam-se o HttpClient e o System.Text.Json integrado.
using System;
using System.Net.Http;
using System.Net.Http.Json;
using System.Threading.Tasks;
record User(string Name);
class Program
{
static async Task Main()
{
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer YOUR_TOKEN");
var response = await client.GetAsync("https://api.example.com/users/42");
response.EnsureSuccessStatusCode();
// Parsing do JSON diretamente para um objeto tipado
var user = await response.Content.ReadFromJsonAsync<User>();
Console.WriteLine(user?.Name);
}
}PHP
Com cURL faz-se a requisição, e json_decode parseia a resposta.
<?php
$ch = curl_init("https://api.example.com/users/42");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer YOUR_TOKEN"]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($status !== 200) {
throw new Exception("Erro de API: $status");
}
// Parsing do JSON para um array associativo
$user = json_decode($body, true);
echo $user["name"];Rust
A combinação habitual: o cliente assíncrono reqwest e o serde para o parsing.
use serde::Deserialize;
#[derive(Deserialize)]
struct User {
name: String,
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = reqwest::Client::new();
let response = client
.get("https://api.example.com/users/42")
.header("Authorization", "Bearer YOUR_TOKEN")
.send()
.await?;
if !response.status().is_success() {
return Err(format!("Erro de API: {}", response.status()).into());
}
// Parsing do JSON para uma struct
let user: User = response.json().await?;
println!("{}", user.name);
Ok(())
}Comparativo de ferramentas
| Linguagem | Cliente HTTP | Parsing de JSON |
|---|---|---|
| Python | requests / httpx |
.json() (json) |
| JavaScript | fetch / axios |
.json() (JSON) |
| Java | HttpClient |
Jackson / Gson |
| Go | net/http |
encoding/json |
| C# | HttpClient |
System.Text.Json |
| PHP | cURL / Guzzle | json_decode |
| Rust | reqwest |
serde / serde_json |
Boas práticas
Para construir uma integração confiável com uma API, vale seguir alguns princípios:
- Verifique sempre o código de status antes de parsear o corpo da resposta.
- Envolva o parsing em tratamento de erros — dados externos não são confiáveis.
- Não guarde segredos no código — use variáveis de ambiente.
- Respeite os limites de requisições — aplique esperas e backoff diante de um 429.
- Defina timeouts em cada requisição para não ficar travado.
- Registre as requisições e os erros — isso simplifica a depuração das integrações.
- Faça cache dos dados que mudam pouco para reduzir a carga e não esgotar os limites.
- Fixe a versão da API e acompanhe os avisos de descontinuação.
- Valide a estrutura da resposta antes de usar seus campos.
Conclusão
A extração de dados via API é o ciclo completo de interação com um serviço externo: construção da requisição, autenticação, verificação do status e dos cabeçalhos, parsing do corpo da resposta e tratamento dos casos-limite — a paginação, os limites e as falhas de rede. Tecnicamente, a etapa de parsing quase sempre se resume a processar JSON, mas uma integração robusta exige levar em conta tudo o que está ao redor.
Há ferramentas em todas as linguagens difundidas: em umas, o cliente HTTP e o parser vêm integrados; em outras, recorre-se a bibliotecas populares. O princípio é o mesmo em toda parte — converter a resposta de um serviço remoto em dados confiáveis com os quais se possa trabalhar sem surpresas no código.
Para aprofundar a etapa de parsing dos dados em si, consulte o artigo «Parsing de JSON»; sobre o parsing de outros formatos de resposta, os artigos «Parsing de XML» e «Parsing de CSV».