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=notebookbusca como a barra de busca da loja._from/_to— paginação por janela de índice:_from=0&_to=49traz 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:123456pega 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:
- rode a busca (
ft=da categoria que você acompanha, oufq=dos produtos exatos) num agendador, a cada N horas; - guarde um snapshot
{productId → preço}de cada execução; - 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=testerespondendo 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=porproductIdé 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.