Formatos e fontes de dados 9 min de leitura

Extração de dados via API

Extração de dados via API em vez de HTML: autenticação, limites de requisições, paginação e parsing de respostas JSON, com exemplos em 7 linguagens.

EW
Equipe Web-Scraping.biz
Coleta de dados para as demandas do negócio
Publicado: 22 fevereiro 2025

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:

  1. Construir e enviar uma requisição HTTP correta.
  2. Receber a resposta do servidor.
  3. Verificar o código de status e os cabeçalhos.
  4. Parsear o corpo da resposta (quase sempre JSON) e convertê-lo em objetos do programa.
  5. 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:

code
https://api.example.com/users?role=admin&page=2&limit=50

Cabeç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 (400 requisição incorreta, 401 não autenticado, 403 proibido, 404 não encontrado, 429 requisiçõ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=50 ou ?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 id ou 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.

python
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):

python
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 users

JavaScript (Node.js / navegador)

O fetch integrado devolve uma promise; o JSON é parseado com o método .json().

javascript
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):

javascript
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).

java
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).

go
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.

c#
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
<?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.

rust
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».