---
title: "Web scraping vs. API: qual você deve usar?"
description: "Uma API entrega os dados que o provedor decide compartilhar, em formato fixo; o web scraping lê a própria página. Comparamos e testamos os dois caminhos."
url: https://proxynet.io/pt-br/blog/web-scraping-vs-api
date: 2026-09-29
author: "Acar Diveroli"
category: "Comparativos, Web scraping"
lang: pt-BR
---

# Web scraping vs. API: qual você deve usar?

Você precisa da mesma lista toda manhã: as issues abertas de um repositório do GitHub, os preços da página de categoria de uma loja, as citações de um site. O GitHub documenta uma API para as issues, então um script pode pedi-las e receber JSON de volta. A loja talvez não ofereça nada além das próprias páginas, e aí o script precisa baixar o HTML e tirar os preços de dentro dele. Essa é toda a diferença entre uma API e o web scraping, e na maioria dos projetos a escolha é feita pelo que o outro lado oferece, não por gosto.

Este artigo explica o que é uma API e o que é scraping, compara os dois em dez pontos e depois coleta os mesmos 100 registros pelos dois caminhos em Python: uma vez a partir de páginas HTML e outra a partir do endpoint JSON que a própria página do site chama. Em seguida vêm os endpoints JSON ocultos, os serviços de API de scraping, os limites de requisições, a whitelist de IP, o custo de cada caminho e quando combinar os dois. Todos os exemplos de código rodaram em 29 de setembro de 2026 com Python 3.13, Requests 2.34.2 e beautifulsoup4 4.15.0.

> **Nota: Resposta rápida**
>
> Use uma API oficial quando ela existir e trouxer os campos de que você precisa: os dados chegam estruturados em um formato versionado, e os limites estão por escrito. Faça scraping quando não houver API, quando a API deixar de fora campos que a página mostra ou quando a cota ou o preço dela não servirem para o trabalho. Entre os dois fica o endpoint JSON que as próprias páginas de um site chamam. Ele é mais leve que o HTML, mas ninguém prometeu mantê-lo, então os termos de uso do site e um ritmo moderado de requisições continuam valendo. Muitos projetos usam os dois: a API para os registros principais e o scraping para o que a API não traz.

## O que é uma API?

Uma API (application programming interface, interface de programação de aplicações) é um conjunto de requisições fixas que um programa pode enviar a outro, junto com as regras sobre o que enviar e o que volta. Na web, isso normalmente significa uma requisição HTTP para um endereço como `https://api.github.com/repos/python/cpython/issues` e uma resposta em JSON. Uma página web é escrita para uma pessoa ler; uma resposta de API, para um programa analisar.

Uma API web é formada por algumas partes:

- **Endpoint:** o endereço de uma operação, como "listar issues" ou "obter um produto".
- **Parâmetros:** o que você pede, por exemplo `state=open` ou `page=2`.
- **Autenticação:** uma chave de API, um token ou OAuth que diz ao serviço quem está chamando. Muitas APIs também respondem a chamadas anônimas, com um limite menor.
- **Formato de resposta:** geralmente JSON, com nomes de campo que se mantêm iguais de uma chamada para outra.
- **Limites e termos:** quantas chamadas você pode fazer por hora e o que pode fazer com os dados.
- **Documentação:** muitas APIs publicam uma descrição legível por máquina no formato OpenAPI. A [OpenAPI Specification](https://spec.openapis.org/oas/latest.html), na versão 3.2.1 desde 10 de setembro de 2026, se define como uma descrição de interface padrão e independente de linguagem para APIs HTTP, para que pessoas e ferramentas descubram o que um serviço oferece sem ler o código-fonte dele.

Nem toda API está aberta a todos. Bancos, exchanges e muitos serviços corporativos só emitem chaves para titulares de conta, e alguns aceitam chamadas apenas de endereços IP cadastrados com antecedência; voltamos a esse ponto mais abaixo.

## O que é web scraping?

Web scraping é um programa fazendo o que o seu navegador faz e depois guardando só os dados: ele baixa a página, lê o HTML e seleciona os valores com seletores CSS ou XPath. O site não concordou com nada. O layout da página é o único "contrato", e o site pode mudá-lo em qualquer dia pelos próprios motivos. [O que é web scraping e como funciona?](/pt-br/blog/what-is-web-scraping) percorre o processo inteiro, e a diferença entre seguir links e extrair campos está em [Web scraping vs. web crawling](/pt-br/blog/web-scraping-vs-web-crawling).

A vantagem do scraping é o alcance: dá para chegar a tudo o que um visitante vê sem fazer login. O preço é que cada valor precisa ser encontrado de novo em uma marcação feita para o design, não para os dados.

## Como cada caminho obtém os dados?

De longe, as etapas parecem iguais. A diferença está em quem decide o formato da resposta.

Com uma API:

1. **Você lê a documentação** e encontra o endpoint, os parâmetros e os limites.
2. **Você obtém uma chave** se a API exigir, e a guarda em uma variável de ambiente, não no código.
3. **Você envia uma requisição** com parâmetros, por exemplo `?page=2`.
4. **O serviço retorna JSON** com campos nomeados e, geralmente, um campo ou cabeçalho que aponta para a próxima página.
5. **Você lê os campos pelo nome.** Um redesign do site não mexe neles.

Com scraping:

1. **Você estuda a página** e o HTML por trás dela para descobrir onde fica cada valor.
2. **Você confere o `robots.txt` e os termos do site** ([como ler o robots.txt](/pt-br/blog/robots-txt)).
3. **Você baixa a página** como um navegador faria, ou a renderiza em um navegador headless se o JavaScript monta o conteúdo.
4. **Você analisa o HTML** e seleciona cada valor com um seletor como `span.text` ([o que é parsing de dados](/pt-br/blog/what-is-data-parsing)).
5. **Você limpa e armazena os valores,** e repete o trabalho quando o layout muda.

## Web scraping vs. API: tabela comparativa

| | API oficial | Endpoint JSON do próprio site | Web scraping (HTML) |
|---|---|---|---|
| Cobertura dos dados | Só os campos que o provedor expõe | O que a página precisa para se montar | Tudo o que um visitante consegue ver |
| Formato | JSON ou XML documentado | JSON sem documentação | HTML que você precisa analisar |
| Estabilidade | Versionada; mudanças são anunciadas | Pode mudar a cada release do front-end | Quebra quando o layout muda |
| Limites de requisições | Publicados, muitas vezes nos cabeçalhos da resposta | Não publicados; você define o seu ritmo | Não publicados; você define o seu ritmo |
| Autenticação | Chave, token ou OAuth; às vezes uma whitelist de IP | Às vezes os cookies ou tokens de uma sessão da página | Geralmente nenhuma em páginas públicas |
| Termos de uso | Os termos da API dizem o que é permitido | Valem os termos do site; o site não promete nada | Valem os termos do site e o robots.txt |
| Custo | Cota gratuita ou plano pago | Sem taxa; o seu tempo e o seu tráfego | Sem taxa; desenvolvimento, manutenção, proxies, renderização |
| Manutenção | Baixa; atualizar quando uma versão é descontinuada | Média; ficar de olho em campos renomeados | Alta; os seletores falham depois de redesigns |
| Tamanho de uma resposta | Pequeno, só os dados | Pequeno, só os dados | Páginas inteiras com layout e marcação |
| Conteúdo montado por JavaScript | Não é problema | Não é problema | Precisa de navegador headless ou do caminho JSON |

A API REST do GitHub mostra o que "versionada" significa na prática. Uma requisição pode informar a versão no cabeçalho `X-GitHub-Api-Version`, e quando sai uma versão nova, a anterior continua com suporte por pelo menos mais 24 meses ([versões da API REST do GitHub](https://docs.github.com/en/rest/about-the-rest-api/api-versions)). Lá, remover ou renomear um campo da resposta conta como mudança incompatível e precisa esperar por uma nova versão. Requisições sem esse cabeçalho continuam recebendo a versão 2022-11-28, que tem suporte até 10 de março de 2028. Nenhum site faz esse tipo de promessa sobre as próprias classes CSS.

## Os mesmos dados pelos dois caminhos: um exemplo testado em Python

O site de treino [quotes.toscrape.com](https://quotes.toscrape.com/) é um ambiente de testes criado para exercícios de scraping; o rodapé dá o crédito à Zyte. Ele lista 100 citações em dez páginas HTML, de `/page/1/` a `/page/10/`. A versão com rolagem infinita, em `/scroll`, carrega as mesmas citações de um endpoint JSON, `/api/quotes?page=N`, e toda resposta traz um campo `has_next`. O site retorna 404 para `/robots.txt`, o que, pelo padrão do robots.txt, significa que não há regras de rastreamento; mesmo assim, o script espera um segundo entre as páginas.

O script coleta as 100 citações pelos dois caminhos com uma única sessão compartilhada. Cada requisição tem um timeout, a sessão se identifica pelo nome no User-Agent e faz novas tentativas em respostas `429` e `5xx`:

```python
"""As mesmas citações duas vezes: extraídas das páginas HTML e lidas do endpoint JSON."""
import os
import time

import requests
from bs4 import BeautifulSoup
from requests.adapters import HTTPAdapter
from urllib3.util import Retry

BASE = "https://quotes.toscrape.com"
DELAY = 1.0  # pausa entre as páginas

def make_session():
    retry = Retry(
        total=4,
        backoff_factor=1,  # espera 0, 2, 4, 8 s entre as tentativas
        status_forcelist=[429, 500, 502, 503, 504],
        allowed_methods=["GET"],
        respect_retry_after_header=True,  # um cabeçalho Retry-After substitui o backoff
    )
    session = requests.Session()
    session.mount("https://", HTTPAdapter(max_retries=retry))
    session.mount("http://", HTTPAdapter(max_retries=retry))
    session.headers["User-Agent"] = "quotes-compare/1.0 (contact: you@example.com)"
    proxy = os.environ.get("PROXY_URL")  # ex.: http://user:pass@pr.proxynet.io:8000
    if proxy:
        session.proxies = {"http": proxy, "https": proxy}
    return session

def scrape_html(session):
    """Caminho 1: baixar cada página HTML e extrair os campos com seletores CSS."""
    quotes, page, size = [], 1, 0
    while True:
        r = session.get(f"{BASE}/page/{page}/", timeout=(5, 20))
        r.raise_for_status()
        size += len(r.content)
        soup = BeautifulSoup(r.content, "lxml")
        for q in soup.select("div.quote"):
            quotes.append({
                "text": q.select_one("span.text").get_text(strip=True),
                "author": q.select_one("small.author").get_text(strip=True),
                "tags": [a.get_text(strip=True) for a in q.select("a.tag")],
            })
        if soup.select_one("li.next > a") is None:  # sem link "Next": última página
            return quotes, page, size
        page += 1
        time.sleep(DELAY)

def fetch_api(session):
    """Caminho 2: chamar o endpoint JSON que a própria página de rolagem do site usa."""
    quotes, page, size = [], 1, 0
    while True:
        r = session.get(f"{BASE}/api/quotes", params={"page": page}, timeout=(5, 20))
        r.raise_for_status()
        size += len(r.content)
        data = r.json()
        for q in data["quotes"]:
            quotes.append({
                "text": q["text"],
                "author": q["author"]["name"],
                "tags": q["tags"],
            })
        if not data["has_next"]:  # a API informa quando a lista termina
            return quotes, page, size
        page += 1
        time.sleep(DELAY)

session = make_session()
results = {}
for name, collect in (("HTML", scrape_html), ("API", fetch_api)):
    quotes, pages, size = collect(session)
    results[name] = quotes
    print(f"{name}: {len(quotes)} quotes from {pages} pages, {size / 1024:.1f} KiB")

print("same data:", results["HTML"] == results["API"])
print(results["API"][0])
```

A saída foi idêntica tanto rodando o script diretamente quanto por um proxy de teste local definido em `PROXY_URL`:

```text
HTML: 100 quotes from 10 pages, 106.1 KiB
API: 100 quotes from 10 pages, 30.2 KiB
same data: True
{'text': '“The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.”', 'author': 'Albert Einstein', 'tags': ['change', 'deep-thoughts', 'thinking', 'world']}
```

Os 100 registros dos dois caminhos bateram campo a campo. O caminho HTML baixou 106,1 KiB para obtê-los e o caminho JSON, 30,2 KiB, contados depois da descompressão; as páginas HTML também carregam o layout, a navegação, a barra lateral de tags e a marcação em volta de cada valor. Pelo proxy, a única `Session` enviou as 20 requisições por um só túnel do proxy.

Os dois loops guardam o sinal de parada em lugares diferentes. O caminho HTML termina quando a página não tem link "Next", enquanto a API diz isso de forma explícita com `has_next: false`. Contar páginas até dar erro não funcionaria neste site: `/page/11/` responde `200` sem citações, e `/api/quotes?page=11` responde `200` com uma lista vazia. Outras condições de parada estão em [O que é paginação e como percorrer todas as páginas?](/pt-br/blog/pagination-web-scraping)

A configuração de novas tentativas atende aos dois caminhos. Apontamos a sessão para um servidor de teste local que respondeu `429` duas vezes com `Retry-After: 2`: a sessão esperou dois segundos a cada vez, devolveu a terceira resposta depois de 4,0 segundos e o código que a chamou nunca viu um 429. Contra um servidor que continuava respondendo `503` sem esse cabeçalho, ela esperou 0, 2, 4 e 8 segundos e, depois de 14 segundos, lançou `requests.exceptions.RetryError` com "too many 503 error responses". `allowed_methods=["GET"]` é proposital: a [RFC 9110](https://www.rfc-editor.org/rfc/rfc9110.html#name-idempotent-methods) diz que um cliente não deve repetir automaticamente uma requisição com um método não idempotente, como POST.

### Campos que um caminho tem e o outro não

Os dois caminhos não trazem exatamente os mesmos campos. Cada registro da API inclui o link do autor no Goodreads e um slug, que a página de lista não mostra:

```json
{
  "author": {
    "goodreads_link": "/author/show/9810.Albert_Einstein",
    "name": "Albert Einstein",
    "slug": "Albert-Einstein"
  },
  "tags": [
    "change",
    "deep-thoughts",
    "thinking",
    "world"
  ],
  "text": "“The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.”"
}
```

A página de lista em HTML, por sua vez, liga cada autor a uma página "about" com data e local de nascimento ("March 14, 1879" e "in Ulm, Germany" no caso de Einstein). Essa página não tem equivalente em JSON: `/api/author/Albert-Einstein` retorna 404. Projetos reais são bem parecidos. A API guarda IDs internos e números exatos de estoque, enquanto a página mostra os textos, os selos e os preços que os visitantes realmente veem.

Uma API também pode falhar de maneiras que parecem estranhas do ponto de vista do código. `/api/quotes?page=abc` retorna o status `500` com uma página de erro em HTML, e chamar `.json()` nesse corpo lança `JSONDecodeError: Expecting value: line 1 column 1 (char 0)`. No script acima, o adaptador de novas tentativas pega o 500 primeiro e termina em um `RetryError`; sem ele, `raise_for_status()` interrompe a execução antes do `.json()`. As outras causas desse erro estão em [Como corrigir JSONDecodeError: Expecting Value](/pt-br/blog/jsondecodeerror-expecting-value).

## Endpoints JSON ocultos: o caminho do meio

Muitas páginas que parecem HTML comum carregam os dados como JSON em segundo plano, assim como a página `/scroll` acima chama `/api/quotes`. Você encontra essas requisições nas ferramentas de desenvolvedor do navegador: abra o painel **Rede**, filtre por **Fetch/XHR**, recarregue a página e procure respostas que contenham os seus dados. O procedimento completo está em [Páginas estáticas e dinâmicas no web scraping](/pt-br/blog/static-vs-dynamic-pages), e como transformar uma requisição copiada em código Python é explicado em [POST com JSON no Python Requests](/pt-br/blog/python-requests-post-json).

Um endpoint assim costuma ser um bom meio-termo: dados estruturados com uma fração do tamanho da página. Mesmo assim, não é uma API pública, então algumas regras se aplicam:

- **Só dados públicos.** Se a requisição só funciona com o cookie da sua sessão logada, os dados não são públicos, e um script agendado rodando com o seu cookie coloca a sua própria conta em risco.
- **Nenhuma promessa de estabilidade.** Nomes de campo e parâmetros podem mudar a cada release do front-end do site, sem aviso. Confira o formato da resposta a cada execução e faça o script falhar de forma clara quando faltar uma chave.
- **Mesmos termos, mesmo ritmo.** Os termos de uso do site e o `robots.txt` valem para o endpoint tanto quanto para as páginas. Requisições JSON são pequenas, o que torna fácil enviá-las muito mais rápido do que uma pessoa faria; mantenha a pausa.
- **Pare nos parâmetros assinados.** Se a requisição carrega uma assinatura ou um token de curta duração gerado pelo script da página, ela não foi feita para ser reutilizada. Procure uma API oficial ou entre em contato com o site.
- **Prefira o caminho documentado.** Quando o site oferece uma API oficial para os mesmos dados, use-a.

O lado jurídico da coleta de dados públicos, que varia de país para país, está em [Web scraping é legal?](/pt-br/blog/is-data-web-scraping-legal)

## O que é uma API de scraping?

"API de scraping" também é o nome de um tipo de serviço comercial, algo diferente da API do próprio site. Você envia ao serviço uma URL de destino; ele baixa a página para você, muitas vezes em um navegador headless e pelo pool de proxies do próprio serviço, repete as requisições que falharam e devolve o HTML, ou campos já extraídos, em JSON. Você o chama como uma API, mas os dados continuam vindo do scraping da página de destino.

Esses serviços atendem equipes que precisam de páginas de muitos sites sem manter navegadores e rotação de proxies por conta própria. Eles cobram pelas requisições que você envia por eles, então a comparação é entre essa conta e o custo de manter o seu próprio scraper. Eles não mudam de quem são as regras: os termos do site de destino e o `robots.txt` continuam valendo para você, e uma API de scraping não é motivo para ignorar uma API oficial que já existe.

## Limites de requisições, 429 e chaves de API

Uma API oficial informa os seus limites, muitas vezes em cada resposta. A API REST do GitHub permite 60 requisições por hora sem autenticação, contadas pelo endereço IP de origem, e 5.000 por hora com um personal access token ([limites de requisições da API REST do GitHub](https://docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api)). O endpoint `/rate_limit` mostra a sua situação, e chamá-lo não conta para o limite primário:

```python
import requests

r = requests.get(
    "https://api.github.com/rate_limit",
    headers={"Accept": "application/vnd.github+json"},
    timeout=(5, 20),
)
core = r.json()["resources"]["core"]
print(r.status_code, "limit:", core["limit"], "remaining:", core["remaining"], "reset:", core["reset"])
print({k: v for k, v in r.headers.items() if k.lower().startswith("x-ratelimit")})
```

```text
200 limit: 60 remaining: 58 reset: 1790650244
{'X-RateLimit-Limit': '60', 'X-RateLimit-Remaining': '58', 'X-RateLimit-Used': '2', 'X-RateLimit-Resource': 'core', 'X-RateLimit-Reset': '1790650244'}
```

O valor de reset é um timestamp Unix em UTC, 02:50:44 de 29 de setembro de 2026 nesta execução, e duas requisições já tinham sido feitas a partir do nosso endereço naquela hora. Quando o limite acaba, o GitHub responde `403` ou `429` com `x-ratelimit-remaining` em 0, e você deve esperar até o horário em `x-ratelimit-reset`. Para os limites secundários, ele envia `retry-after` quando pode e, caso contrário, pede que você espere pelo menos um minuto.

É aqui que uma lógica genérica de novas tentativas não dá conta. A sessão do nosso script repete em `429`, mas não em `403`, e 14 segundos de backoff não servem de nada contra uma janela que zera uma vez por hora. Com uma API, leia os cabeçalhos e espere até o reset.

O código de status em si vem da [RFC 6585](https://www.rfc-editor.org/rfc/rfc6585.html#section-4): `429 Too Many Requests` significa que o cliente enviou requisições demais em um determinado intervalo de tempo; a resposta deve explicar a condição e pode incluir `Retry-After`. A RFC deixa em aberto, de propósito, como o servidor identifica o cliente e conta as requisições, então um limite pode ser por IP, por chave ou por conta. A RFC 9110 define `Retry-After` como uma data ou um número de segundos, e o urllib3 lê as duas formas. Um site do qual você faz scraping raramente publica algo disso, então você mesmo define o ritmo e desacelera no primeiro 429 ([429 Too Many Requests explicado](/pt-br/blog/http-429-too-many-requests)).

## Por que algumas APIs pedem um endereço IP fixo?

Algumas APIs verificam de onde vem uma chamada, além de qual chave ela traz. Exchanges, marketplaces, bancos e muitos serviços de dados corporativos permitem cadastrar um ou mais endereços IP para uma chave, e uma chamada de qualquer outro endereço é recusada mesmo com a chave certa. Isso quebra assim que o script roda em algum lugar com endereço variável: uma conexão residencial, um notebook em trânsito, uma função serverless sem IP de saída fixo. Como descobrir o seu endereço de saída, e quatro formas de resolver o problema, estão em [IP estático para API](/pt-br/blog/static-ip-for-api-access); o caso das exchanges está em [Whitelist de IP na API da exchange cripto](/pt-br/blog/crypto-exchange-api-ip-whitelist).

Em integrações que envolvem pagamentos, dados de cartão ou registros de saúde, o endereço cadastrado deve ser o do seu próprio servidor ou da sua própria conexão. Para ambientes de teste e clientes que não lidam com dados sensíveis, um proxy com endereço fixo também resolve: com [Proxies ISP](https://proxynet.io/pt-br/static-isp-residential-proxy) ou [Proxies de datacenter](https://proxynet.io/pt-br/datacenter-proxy), o seu script ganha um único IP de saída, que você cadastra na API uma vez. Esses produtos por IP vêm por padrão com uma restrição de site de destino, então você informa o host da API ao fazer o pedido; o acesso a todos os sites é um complemento pago.

O scraping tem a necessidade oposta: muitas páginas ao longo do tempo, às vezes do jeito que os visitantes de outro país as veem. É para isso que servem os [Proxies rotativos](https://proxynet.io/pt-br/rotating-proxy). Nenhum dos dois tipos de proxy muda as regras acima: o proxy muda o endereço, não os termos do site nem o ritmo de requisições que você deve respeitar.

## Quanto custa cada abordagem?

**Uma API oficial.** O preço está na página de preços do provedor. Algumas APIs são gratuitas até uma cota, outras cobram desde a primeira chamada e algumas só estão disponíveis com contrato empresarial. O custo de engenharia é baixo: um cliente para uma API JSON documentada costuma ter algumas dezenas de linhas, como o de cima. Os custos ocultos são dois. Um é a cota, porque um trabalho que precisa de mais chamadas do que o plano permite tem de esperar ou pagar. O outro é o controle do provedor: termos, preços e acesso podem mudar, e uma API pode ser encerrada.

**Scraping.** O site não cobra nada, mas todo o resto fica com você: escrever o parser, corrigi-lo depois de redesigns, um navegador headless quando o JavaScript monta a página (muito mais pesado que uma requisição simples; veja a seção de custos de [Páginas estáticas e dinâmicas no web scraping](/pt-br/blog/static-vs-dynamic-pages)), proxies quando o volume ou o país exigem, e um monitoramento que perceba quando um seletor passa a não retornar nada, sem aviso. O tamanho também pesa. No nosso teste, o caminho HTML baixou cerca de 3,5 vezes mais bytes para os mesmos registros, e se o seu plano de proxy é cobrado por tráfego, essa proporção aparece na fatura.

**Um serviço de API de scraping.** Você paga por requisição e não mantém navegadores nem proxies. Se o serviço devolve HTML bruto, o parsing continua sendo por sua conta.

## Quando combinar scraping e API?

Usar os dois é comum e geralmente segue um de quatro padrões:

- **A API para a lista, as páginas para os detalhes.** No nosso exemplo, você pegaria as 100 citações do endpoint JSON e visitaria cada uma das 50 páginas de autor uma vez para obter as datas de nascimento.
- **A API para os seus dados, as páginas para a visão pública.** Uma API de vendedor retorna os seus anúncios com IDs e estoque; a página pública do produto mostra os selos e o número de avaliações que os clientes veem. Junte os dois pelo ID do produto.
- **A API para o registro, a página para um país.** Uma API pode retornar um único preço de tabela, enquanto um visitante de outro país vê na página moeda local, impostos e promoção. Essa comparação exige a página carregada a partir daquele país.
- **A página como checagem da API.** Um pequeno scraper que confere algumas páginas por dia confirma que o que a API retorna ainda bate com o que os visitantes veem.

## Casos de uso

- **Monitoramento de preços e estoque:** os seus anúncios pela API da plataforma, as páginas públicas dos concorrentes por um scraper ([monitorar preços da concorrência](/pt-br/blog/competitor-price-tracking)).
- **Dados de repositórios, issues e releases:** a API do GitHub com paginação pelo cabeçalho `Link` ([paginação de API](/pt-br/blog/pagination-web-scraping)).
- **Dados de exchanges e bots de trading:** uma chave de API vinculada a um endereço cadastrado ([whitelist de IP na API da exchange](/pt-br/blog/crypto-exchange-api-ip-whitelist)).
- **Uma tabela para uma planilha, uma única vez:** uma função de importação ou algumas linhas de Python ([extrair dados de um site](/pt-br/blog/extract-data-from-website)).
- **Guardar os resultados:** os mesmos registros gravados em CSV, JSON ou SQLite ([salvar dados de scraping](/pt-br/blog/save-scraped-data-csv-json-sqlite)).
- **Encontrar todas as páginas antes da extração:** um crawler que descobre URLs no site inteiro ([web crawler](/pt-br/web-crawler)).
- **Coleta grande e agendada:** filas, controle de ritmo e saídas em vários países ([extração de dados](/pt-br/data-scraping)).

## Erros comuns

- **Fazer scraping de um site que oferece uma API para os mesmos dados.** Você assume a manutenção à toa, e os termos da API podem ser os únicos que permitem acesso automatizado.
- **Tratar um endpoint oculto como API pública.** Ele não tem versão nem promessa; confira o formato da resposta a cada execução.
- **Uma única regra de novas tentativas para todos os erros.** Um backoff curto serve para um `503` passageiro; uma cota por hora precisa de `x-ratelimit-reset`, e um `403` nem é repetido pela configuração acima.
- **Repetir requisições POST automaticamente.** Um pedido ou uma mensagem pode ser enviado duas vezes; limite as novas tentativas automáticas a GET.
- **Chamar `.json()` em qualquer coisa que volte.** Confira antes o código de status e o `Content-Type`; uma página de erro é HTML.
- **Percorrer números de página até algo falhar.** No site de treino, a página 11 respondeu `200` sem nada dentro; siga `has_next` ou o link "Next".
- **Deixar uma chave de API no código.** Leia-a de uma variável de ambiente e mantenha-a fora do repositório.
- **Culpar o site por um erro de proxy.** O adaptador de novas tentativas também repete um login de proxy que falhou: com uma senha de proxy errada, nosso script tentou cinco vezes em 14 segundos antes de lançar `ProxyError` com `407 Proxy Authentication Required`. Confira as credenciais primeiro.

## Guia de decisão

| Necessidade | Recomendação |
|---|---|
| O site tem uma API oficial com os campos de que você precisa | Use a API; leia antes os limites e os termos |
| A API existe, mas faltam alguns campos | API para os registros principais, scraping para o resto, unidos por um ID |
| Não há API e os dados estão no HTML | Requests e BeautifulSoup, com uma pausa entre as páginas |
| Não há API e o JavaScript carrega os dados | Procure primeiro a requisição JSON; navegador headless só se ela não puder ser reutilizada |
| A API só aceita chamadas de IPs cadastrados | Um endereço de saída fixo: o seu próprio servidor ou, para clientes sem dados sensíveis, um proxy ISP estático ou de datacenter |
| A cota da API acaba toda hora | Leia os cabeçalhos de limite, distribua as chamadas, peça um plano maior |
| Páginas de muitos sites sem infraestrutura própria | Um serviço de API de scraping, se os termos dos sites de destino permitirem a coleta |
| Milhares de páginas por dia de sites que você já avaliou | O seu próprio scraper com [Proxies rotativos](https://proxynet.io/pt-br/rotating-proxy) e um orçamento de requisições por site |

## Perguntas frequentes

### Qual é a diferença entre web scraping e API?

Uma API é um caminho que o provedor construiu para programas: você envia uma requisição documentada e recebe dados estruturados, com limites e termos publicados. O web scraping lê as páginas feitas para pessoas e extrai os valores do HTML. Na API, quem decide os campos que você recebe é o provedor; o scraping alcança tudo o que é visível, mas quebra quando a página muda.

### Web scraping é melhor do que usar uma API?

Não de forma geral. Quando uma API oficial traz os campos de que você precisa, um script baseado nela é mais rápido de escrever e continua funcionando quando o site é redesenhado. O scraping é a melhor escolha quando não há API, quando a API deixa de fora dados que a página mostra ou quando a cota ou o preço dela não servem para o trabalho.

### Todo site tem uma API?

Não. Muitos sites não têm nenhuma API pública, e muitos têm uma que cobre só parte dos dados ou exige uma conta empresarial. Alguns sites carregam as páginas a partir de endpoints JSON internos; eles podem ser usados com cuidado para dados públicos, mas não são uma API publicada.

### É legal usar a API oculta de um site?

Depende dos dados, dos termos de uso do site e da lei dos lugares onde você e o site atuam. Ler dados públicos de um endpoint que a própria página chama é tecnicamente o mesmo que ler a página, e valem os mesmos termos. Entrar com a conta de outra pessoa, contornar controles de acesso ou coletar dados pessoais levanta outras questões. O panorama geral está em [Web scraping é legal?](/pt-br/blog/is-data-web-scraping-legal)

### Uma API de scraping é a mesma coisa que a API de um site?

Não. A API de um site é publicada pelo próprio site e retorna os dados dele em formato fixo. Uma API de scraping é um serviço de terceiros que baixa a página de destino para você e devolve o HTML ou campos já extraídos. Os dados continuam vindo da página, e os termos do site de destino continuam valendo.

### Preciso de um proxy para chamar uma API?

Normalmente não. Você precisa de um quando a API só aceita chamadas de endereços IP cadastrados e o seu endereço muda, ou quando precisa ver uma API ou página do jeito que ela responde a partir de outro país. Para pagamentos e outras integrações sensíveis, cadastre o endereço do seu próprio servidor.

## Em resumo

Uma API é o caminho que o provedor construiu para programas, com formato versionado e limites por escrito. O scraping lê o que o provedor construiu para pessoas; alcança tudo o que é visível e quebra quando a página muda. Verifique primeiro se existe uma API oficial, use com cuidado o endpoint JSON do próprio site quando não houver uma e faça scraping do HTML para o que sobrar. Quando uma API exigir um endereço fixo ou um trabalho de scraping precisar de volume em vários países, compare os nossos [planos de proxy](/pt-br/proxy).
