1

A API pública de catálogo da VTEX (que quase ninguém usa)

Uma fatia enorme do e-commerce brasileiro — Americanas, Submarino, Shoptime e milhares de outras lojas — roda na mesma plataforma: VTEX. E quase todo mundo que precisa de preço dessas lojas faz do jeito difícil: browser headless, seletor de HTML, proxy, e um scraper que quebra a cada deploy do front da loja.

O jeito fácil está documentado e aberto: toda loja VTEX expõe uma API REST pública de catálogo, sem chave e sem login. É a mesma API que o front da loja consome. O endpoint é este:

https://{dominio-da-loja}/api/catalog_system/pub/products/search

Exemplo real — buscar "notebook" na Americanas:

curl -s "https://www.americanas.com.br/api/catalog_system/pub/products/search?ft=notebook&_from=0&_to=9"

Volta um array JSON de produtos, tipado e estável. Sem renderizar JavaScript, sem anti-bot, sem parser de HTML. Este artigo documenta os parâmetros que importam, a estrutura de preço (que tem uma pegadinha histórica), e um caso de uso onde essa API brilha: monitorar preço de concorrente.

Os parâmetros que importam

  • ft= — busca full-text (free text). ?ft=notebook busca como a barra de busca da loja.
  • _from / _to — paginação por janela de índice: _from=0&_to=49 traz os 50 primeiros. A janela máxima é 50 itens por request (_to - _from <= 49); pedir mais dá erro. Para a página seguinte, _from=50&_to=99, e assim por diante.
  • O= — ordenação: O=OrderByPriceASC, OrderByPriceDESC, OrderByTopSaleDESC, OrderByReleaseDateDESC, entre outras.
  • fq= — filtro estruturado, para quando você não quer busca textual: fq=productId:123456 pega um produto exato; também filtra por categoria e marca.

O status 206 não é erro

A primeira coisa que confunde quem integra: quando há mais resultados do que a janela pedida, a VTEX responde HTTP 206 (Partial Content) — e isso é a resposta normal de sucesso da paginação, não um problema. O header resources da resposta indica o range retornado e o total (ex.: resources: 0-49/1234). Na prática: trate 200 e 206 como sucesso, e avance a janela até vir menos itens que o tamanho dela.

Onde mora o preço (e a pegadinha do typo)

A estrutura do produto é: produto → items[] (os SKUs — cada variação de cor/tamanho) → sellers[] (quem vende — lojas VTEX são marketplaces) → e o preço dentro da oferta do seller:

{
  "productId": "123456789",
  "productName": "Notebook ...",
  "brand": "Acer",
  "linkText": "notebook-acer-aspire-5",
  "items": [{
    "itemId": "987",
    "sellers": [{
      "sellerName": "...",
      "commertialOffer": {
        "Price": 2799.0,
        "ListPrice": 3299.0,
        "PriceWithoutDiscount": 2999.0,
        "AvailableQuantity": 12
      }
    }]
  }]
}

Sim, é commertialOffer — com o typo. O erro de grafia está na API desde sempre e nunca foi corrigido, porque corrigir quebraria toda integração existente. Escreva errado ou não encontre o preço.

Os campos: Price é o preço atual de venda (com promoção aplicada), ListPrice o preço "de" (riscado), AvailableQuantity o estoque do seller. Dois detalhes de quem já tropeçou: (1) num marketplace o mesmo SKU pode ter vários sellers com preços diferentes — para monitoramento simples, o primeiro seller é o padrão da loja; (2) a URL canônica do produto se monta com o linkText: https://{dominio}/{linkText}/p.

Caso de uso: monitorar preço de concorrente

Preço em e-commerce é vivo — muda de madrugada, muda no fim de semana. Quem revende ou compete não precisa do catálogo do concorrente: precisa do diff — o que mudou desde ontem. A receita com essa API:

  1. rode a busca (ft= da categoria que você acompanha, ou fq= dos produtos exatos) num agendador, a cada N horas;
  2. guarde um snapshot {productId → preço} de cada execução;
  3. na execução seguinte, compare produto a produto e emita só o que variou, com delta e percentual.

Sem browser e sem proxy, cada execução é um punhado de GETs — rápido e barato. E como o contrato é a API (não o HTML), o monitor não quebra quando a loja redesenha a página.

Limites, declarados

  • Só funciona em loja VTEX. Americanas, Submarino, Shoptime e milhares de outras — mas não é um scraper universal de e-commerce. (Como saber se uma loja é VTEX? Teste o próprio endpoint: /api/catalog_system/pub/products/search?ft=teste respondendo JSON é a assinatura.)
  • Janela de 50 por request, e o índice de busca corta paginação muito profunda (na casa de ~2.500 resultados por consulta, limite documentado pela VTEX). Para varrer catálogo grande, segmente por categoria, marca ou faixa de preço em vez de paginar uma busca única.
  • ft= é busca por relevância — o resultado é o que a busca da loja devolveria, não um dump exaustivo do catálogo. Para acompanhar produtos específicos, fq= por productId é mais determinístico.
  • Preço é por seller e por canal de venda (existe um parâmetro sc= de sales channel). Para comparação consistente ao longo do tempo, compare sempre o mesmo seller no mesmo canal.

Se você não quiser manter o estado

A parte chata dessa receita não é o GET — é o estado: guardar snapshot, comparar execuções, agendar, exportar. Empacotei isso num Apify Actor: vtex-price-monitor. Você dá o domínio da loja e uma busca (ou lista de URLs de produto), agenda a recorrência, e ele mantém o snapshot entre execuções e devolve cada produto com currentPrice, previousPrice, delta, deltaPercent e a flag priceChanged — em JSON/CSV/Excel, com API e MCP. O modelo de cobrança segue o valor: $0.01 por produto cujo preço mudou — primeira execução e preço estável custam zero.

Mas a API é pública e os parâmetros estão todos aí em cima — se você só precisava saber que ela existe, vai construir. Esse era o ponto do artigo.

Carregando publicação patrocinada...
0