<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Antonio Fernando Rincon de Mendonça</title>
    <description>The latest articles on DEV Community by Antonio Fernando Rincon de Mendonça (@antonio_fernandorincond).</description>
    <link>https://dev.to/antonio_fernandorincond</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4013780%2F13154c06-2ae0-4238-8392-b3749c4d80f5.jpg</url>
      <title>DEV Community: Antonio Fernando Rincon de Mendonça</title>
      <link>https://dev.to/antonio_fernandorincond</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/antonio_fernandorincond"/>
    <language>en</language>
    <item>
      <title>A API pública de catálogo da VTEX (que quase ninguém usa)</title>
      <dc:creator>Antonio Fernando Rincon de Mendonça</dc:creator>
      <pubDate>Sat, 04 Jul 2026 19:54:15 +0000</pubDate>
      <link>https://dev.to/antonio_fernandorincond/a-api-publica-de-catalogo-da-vtex-que-quase-ninguem-usa-40li</link>
      <guid>https://dev.to/antonio_fernandorincond/a-api-publica-de-catalogo-da-vtex-que-quase-ninguem-usa-40li</guid>
      <description>&lt;p&gt;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.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;https://{dominio-da-loja}/api/catalog_system/pub/products/search
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Exemplo real — buscar "notebook" na Americanas:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"https://www.americanas.com.br/api/catalog_system/pub/products/search?ft=notebook&amp;amp;_from=0&amp;amp;_to=9"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Os parâmetros que importam
&lt;/h2&gt;

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

&lt;h2&gt;
  
  
  O status 206 não é erro
&lt;/h2&gt;

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

&lt;h2&gt;
  
  
  Onde mora o preço (e a pegadinha do typo)
&lt;/h2&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"productId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"123456789"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"productName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Notebook ..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"brand"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Acer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"linkText"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"notebook-acer-aspire-5"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"items"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"itemId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"987"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"sellers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"sellerName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"commertialOffer"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;2799.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"ListPrice"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;3299.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"PriceWithoutDiscount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;2999.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"AvailableQuantity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sim, é &lt;strong&gt;&lt;code&gt;commertialOffer&lt;/code&gt;&lt;/strong&gt; — 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.&lt;/p&gt;

&lt;p&gt;Os campos: &lt;code&gt;Price&lt;/code&gt; é o preço atual de venda (com promoção aplicada), &lt;code&gt;ListPrice&lt;/code&gt; o preço "de" (riscado), &lt;code&gt;AvailableQuantity&lt;/code&gt; 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 &lt;code&gt;linkText&lt;/code&gt;: &lt;code&gt;https://{dominio}/{linkText}/p&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Caso de uso: monitorar preço de concorrente
&lt;/h2&gt;

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

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

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limites, declarados
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Só funciona em loja VTEX.&lt;/strong&gt; 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: &lt;code&gt;/api/catalog_system/pub/products/search?ft=teste&lt;/code&gt; respondendo JSON é a assinatura.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Janela de 50 por request&lt;/strong&gt;, 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.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;ft=&lt;/code&gt; é busca por relevância&lt;/strong&gt; — o resultado é o que a busca da loja devolveria, não um dump exaustivo do catálogo. Para acompanhar produtos específicos, &lt;code&gt;fq=&lt;/code&gt; por &lt;code&gt;productId&lt;/code&gt; é mais determinístico.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Preço é por seller e por canal de venda&lt;/strong&gt; (existe um parâmetro &lt;code&gt;sc=&lt;/code&gt; de &lt;em&gt;sales channel&lt;/em&gt;). Para comparação consistente ao longo do tempo, compare sempre o mesmo seller no mesmo canal.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Se você não quiser manter o estado
&lt;/h2&gt;

&lt;p&gt;A parte chata dessa receita não é o GET — é o &lt;strong&gt;estado&lt;/strong&gt;: guardar snapshot, comparar execuções, agendar, exportar. Empacotei isso num Apify Actor: &lt;a href="https://apify.com/geodetic_marigold/vtex-price-monitor" rel="noopener noreferrer"&gt;vtex-price-monitor&lt;/a&gt;. 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 &lt;code&gt;currentPrice&lt;/code&gt;, &lt;code&gt;previousPrice&lt;/code&gt;, &lt;code&gt;delta&lt;/code&gt;, &lt;code&gt;deltaPercent&lt;/code&gt; e a flag &lt;code&gt;priceChanged&lt;/code&gt; — em JSON/CSV/Excel, com API e MCP. O modelo de cobrança segue o valor: &lt;strong&gt;$0.01 por produto cujo preço mudou&lt;/strong&gt; — primeira execução e preço estável custam zero.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

</description>
      <category>portuguese</category>
      <category>webscraping</category>
      <category>api</category>
      <category>ecommerce</category>
    </item>
    <item>
      <title>How to pull App Store reviews via Apple's official RSS feed (no API key)</title>
      <dc:creator>Antonio Fernando Rincon de Mendonça</dc:creator>
      <pubDate>Fri, 03 Jul 2026 15:11:58 +0000</pubDate>
      <link>https://dev.to/antonio_fernandorincond/how-to-pull-app-store-reviews-via-apples-official-rss-feed-no-api-key-1hkk</link>
      <guid>https://dev.to/antonio_fernandorincond/how-to-pull-app-store-reviews-via-apples-official-rss-feed-no-api-key-1hkk</guid>
      <description>&lt;p&gt;If you've ever needed Apple App Store reviews outside of App Store Connect, you've&lt;br&gt;
probably run into two dead ends. App Store Connect only shows reviews for apps you&lt;br&gt;
own, and it's not built for exporting into a spreadsheet or a dashboard. And most&lt;br&gt;
"App Store review scrapers" parse the store's HTML — which quietly breaks every time&lt;br&gt;
Apple changes the page markup. Neither is a good foundation to build on.&lt;/p&gt;

&lt;p&gt;There's a third option most people don't know about: Apple publishes customer&lt;br&gt;
reviews through an official RSS/JSON feed. It's a plain, public HTTP endpoint — no&lt;br&gt;
authentication, no API key, no App Store Connect access. The URL looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://itunes.apple.com/us/rss/customerreviews/page=1/id=310633997/sortby=mostrecent/json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three parts matter. The country code (&lt;code&gt;us&lt;/code&gt;, &lt;code&gt;gb&lt;/code&gt;, &lt;code&gt;de&lt;/code&gt;, &lt;code&gt;jp&lt;/code&gt;, &lt;code&gt;br&lt;/code&gt;, ...) selects&lt;br&gt;
which storefront's reviews you get — reviews are localized per country, which is why&lt;br&gt;
the same app has a different review stream in each market. The &lt;code&gt;id&lt;/code&gt; is the app's&lt;br&gt;
numeric App Store id — if you only have the store URL&lt;br&gt;
(&lt;code&gt;https://apps.apple.com/us/app/whatsapp-messenger/id310633997&lt;/code&gt;), the id is the&lt;br&gt;
number right after &lt;code&gt;id&lt;/code&gt;. And &lt;code&gt;page&lt;/code&gt; paginates: each page returns up to 50 reviews,&lt;br&gt;
and Apple serves up to 10 pages, so the practical ceiling is about &lt;strong&gt;500 recent&lt;br&gt;
reviews per country&lt;/strong&gt;. Swap &lt;code&gt;/json&lt;/code&gt; for the default and you get Atom XML instead;&lt;br&gt;
the JSON variant is friendlier to parse.&lt;/p&gt;

&lt;p&gt;Each review entry gives you the fields you actually want for analysis: the star&lt;br&gt;
rating (&lt;code&gt;im:rating&lt;/code&gt;), the title and body text, the app version the review was left&lt;br&gt;
on (&lt;code&gt;im:version&lt;/code&gt;), and helpful-vote counts (&lt;code&gt;im:voteSum&lt;/code&gt; / &lt;code&gt;im:voteCount&lt;/code&gt;). That&lt;br&gt;
app-version field is the underrated one — it lets you group reviews by build and&lt;br&gt;
answer the question every product team asks after shipping: &lt;em&gt;did this release make&lt;br&gt;
things better or worse?&lt;/em&gt; One caveat to know going in: the very first entry in the&lt;br&gt;
feed is sometimes app metadata rather than a review, so filter for entries that&lt;br&gt;
actually have a rating and a review id before you treat them as reviews. A couple of&lt;br&gt;
practical notes: throttle your requests (a short delay between pages is polite to&lt;br&gt;
&lt;code&gt;itunes.apple.com&lt;/code&gt;), and dedup by review id if you pull multiple countries, since a&lt;br&gt;
few ids can repeat across storefronts.&lt;/p&gt;

&lt;p&gt;Be clear-eyed about what this feed is and isn't. It is a stable, official, no-auth&lt;br&gt;
source of &lt;strong&gt;fresh&lt;/strong&gt; reviews across every country's storefront. It is &lt;strong&gt;not&lt;/strong&gt; a full&lt;br&gt;
historical archive — ~500 recent reviews per country is the hard limit Apple gives&lt;br&gt;
you, and no amount of clever pagination gets past it. For the most common job —&lt;br&gt;
monitoring sentiment and bug reports per release, across markets, on a schedule —&lt;br&gt;
that freshness is exactly what you want. For a complete multi-year export, this&lt;br&gt;
isn't the right source, and it's better to know that before you build on it.&lt;/p&gt;

&lt;p&gt;If you'd rather not write the fetch/paginate/dedup/normalize loop yourself, I&lt;br&gt;
packaged all of this into an Apify Actor:&lt;br&gt;
&lt;a href="https://apify.com/geodetic_marigold/app-store-reviews-scraper" rel="noopener noreferrer"&gt;app-store-reviews-scraper&lt;/a&gt;.&lt;br&gt;
You paste an app id or URL, pick your countries, and it returns normalized rows&lt;br&gt;
(rating, title, text, app version, helpful votes, country, date) as JSON/CSV/Excel,&lt;br&gt;
with an API and MCP support if you want an AI agent to call it. It's pay-per-review&lt;br&gt;
with no start fee. But honestly, the endpoint above is public — if you just needed&lt;br&gt;
to know it exists, go build it. That's the point of this post.&lt;/p&gt;

</description>
      <category>apple</category>
      <category>webscraping</category>
      <category>tutorial</category>
      <category>api</category>
    </item>
  </channel>
</rss>
