<?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: veenkes</title>
    <description>The latest articles on DEV Community by veenkes (@veenkes).</description>
    <link>https://dev.to/veenkes</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%2F4241968%2F29a08765-cfcf-4776-ac47-5f2722c69da9.png</url>
      <title>DEV Community: veenkes</title>
      <link>https://dev.to/veenkes</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/veenkes"/>
    <language>en</language>
    <item>
      <title>How to Export Substack Newsletter Posts</title>
      <dc:creator>veenkes</dc:creator>
      <pubDate>Fri, 13 Nov 2026 06:03:14 +0000</pubDate>
      <link>https://dev.to/veenkes/how-to-export-substack-newsletter-posts-5601</link>
      <guid>https://dev.to/veenkes/how-to-export-substack-newsletter-posts-5601</guid>
      <description>&lt;h1&gt;
  
  
  How to Export Substack Newsletter Posts
&lt;/h1&gt;

&lt;p&gt;Substack is now a primary publishing platform, not just a mailing list. If you research a niche, track competitors, or want an archive of a newsletter you already read, it is reasonable to want a structured export: titles, dates, free versus paid, and engagement. Substack does not offer a simple 'download every post as CSV' button for any publication. This guide shows how to export Substack newsletter posts, including publications that run on a custom domain.&lt;/p&gt;

&lt;p&gt;You will use the &lt;a href="https://apify.com/veenkes/veenkes-substack-posts" rel="noopener noreferrer"&gt;Substack Scraper&lt;/a&gt; on Apify. It collects post metadata (title, URL, date, audience, authors, tags, likes, comments, restacks, word count) and can optionally add the plain text of free posts. Paywalled content is never accessed.&lt;/p&gt;

&lt;p&gt;By the end of this article you will have a CSV or JSON file on your machine, and a short Python snippet you can rerun or schedule. The same Actor is also callable from JavaScript and from AI agents through the &lt;a href="https://docs.apify.com/platform/integrations/mcp" rel="noopener noreferrer"&gt;Apify MCP server&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you need
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;An &lt;a href="https://apify.com" rel="noopener noreferrer"&gt;Apify&lt;/a&gt; account. The Free plan is enough to try a small run.&lt;/li&gt;
&lt;li&gt;A list of inputs for the Actor (URLs, IDs or search terms — details in the steps below).&lt;/li&gt;
&lt;li&gt;Optional: Python 3 with the &lt;code&gt;apify-client&lt;/code&gt; package if you want to automate the run.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No Substack login is required. You only need the public URL of the publication or of a single post.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to run it in the Apify Console
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Open the Actor
&lt;/h3&gt;

&lt;p&gt;Open &lt;a href="https://apify.com/veenkes/veenkes-substack-posts" rel="noopener noreferrer"&gt;https://apify.com/veenkes/veenkes-substack-posts&lt;/a&gt; and click &lt;strong&gt;Try for free&lt;/strong&gt;. A free Apify account is enough to run a first export.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Paste the publication URL
&lt;/h3&gt;

&lt;p&gt;Use the newsletter's home page, whether it lives on &lt;code&gt;*.substack.com&lt;/code&gt; or a custom domain such as &lt;code&gt;https://www.lennysnewsletter.com&lt;/code&gt;. You can also paste individual post URLs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Choose how many posts, and whether you need full text
&lt;/h3&gt;

&lt;p&gt;Set a max posts value for a first run (10 is plenty). Leave full text off if you only need an inventory of titles and engagement; turn it on only for free posts when you need the body.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Export
&lt;/h3&gt;

&lt;p&gt;Start the run. When it finishes, download the dataset as CSV or JSON. Filter later by &lt;code&gt;audience&lt;/code&gt; (free vs paid) or by date.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;since&lt;/em&gt; date and the audience filter are useful for incremental archives. Full text is billed as a separate event, only for free posts you actually retrieve.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to run it from Python
&lt;/h2&gt;

&lt;p&gt;Install the official client and call the Actor by its Store id &lt;code&gt;veenkes/veenkes-substack-posts&lt;/code&gt;. Replace &lt;code&gt;YOUR_APIFY_TOKEN&lt;/code&gt; with a token from &lt;a href="https://console.apify.com/account/integrations" rel="noopener noreferrer"&gt;Apify Console &amp;gt; Integrations&lt;/a&gt;. Do not commit the token to a public repository.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip install apify-client
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;apify_client&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;ApifyClient&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ApifyClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"YOUR_APIFY_TOKEN"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;actor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"veenkes/veenkes-substack-posts"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run_input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"publications"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s2"&gt;"https://www.lennysnewsletter.com"&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"maxPostsPerPublication"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"includeContent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Write every dataset item, then export from the Console or keep going in code.&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"defaultDatasetId"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;iterate_items&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The JavaScript client (&lt;code&gt;apify-client&lt;/code&gt; on npm) uses the same Actor id and the same input object. You can also start the run with a plain HTTP &lt;code&gt;POST&lt;/code&gt; to the Apify API if you prefer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sample output
&lt;/h2&gt;

&lt;p&gt;Here is a shortened example of one dataset record (field values are illustrative of the schema, taken from a real sample run shape):&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="nl"&gt;"publicationUrl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"https://www.lennysnewsletter.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"postId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;224206488&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"State of the tech job market: late 2026"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"subtitle"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"PMs are winning, engineers are plateauing, designers are hurting, and the biggest AI hiring boom isn’t in software"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"https://www.lennysnewsletter.com/p/state-of-the-tech-job-market-late"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"publishedAt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"2026-11-10T15:04:03.497Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"newsletter"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"audience"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"paid"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"authors"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Lenny Rachitsky"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;"handle"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"lenny"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"tags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s2"&gt;"Career"&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"section"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"wordCount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2183&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"likes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;323&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"comments"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;33&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"restacks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"PMs are winning, engineers are plateauing, designers are hurting, and the biggest AI hiring boom isn’t in software"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"previewText"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"👋 Hey there, I’m Lenny. Each week, I share deeply researched product, growth, and career advice. For more: Lenny’s Jobs | Lenny’s Podcast | Lennybot | How I AI | Become an AI-Nativ…"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"contentText"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"coverImage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"https://substackcdn.com/image/fetch/$s_!-6sL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8be609d0-ed10-464c-8b94…"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"podcastUrl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"scrapedAt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"2026-11-12T20:04:50.732Z"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the run has finished, open the dataset in the Console and use &lt;strong&gt;Export &amp;gt; CSV&lt;/strong&gt; if a spreadsheet is your destination. JSON is usually better for code and for language models.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to use the data for
&lt;/h2&gt;

&lt;p&gt;Writers use this to audit their own archive or to study how often a newsletter hits the paywall. Researchers track topic frequency and engagement over time. Growth teams watch a set of newsletters in a niche and note which posts get restacks and comments. Because the schema is flat, you can load the file into a spreadsheet or a notebook and join it with your own annotations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pricing
&lt;/h2&gt;

&lt;p&gt;Pay-per-result pricing starts at $1.50 per 1,000 posts on the Apify Free plan. Adding the full text of a free post is a separate event (from $1.00 per 1,000). There is also a small per-run start fee. Paywalled bodies are never accessed. Always check the live pricing on the &lt;a href="https://apify.com/veenkes/veenkes-substack-posts" rel="noopener noreferrer"&gt;Actor page&lt;/a&gt;, because plans and unit prices can change. For a first test, keep the input tiny so you can inspect the schema before you scale up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scheduling and automations
&lt;/h2&gt;

&lt;p&gt;In the Apify Console you can schedule the Actor (daily or hourly) and leave the same input in place. For incremental jobs, prefer a "since" or "posted after" style filter when the Actor offers one, so you are not paying to re-collect old rows. When a run finishes you can send a webhook, or connect the Actor in Zapier, Make or n8n. AI agents can discover and call &lt;code&gt;veenkes/veenkes-substack-posts&lt;/code&gt; through Apify's MCP server using the same input.&lt;/p&gt;

&lt;h2&gt;
  
  
  Good practices and limits
&lt;/h2&gt;

&lt;p&gt;Only publicly visible metadata (and optional free-post text) is collected. Do not treat this as a way to bypass a paywall. Respect the writer's copyright: an export for research, analysis or personal archiving is a different thing from republishing someone else's issues. The Actor is not affiliated with Substack.&lt;/p&gt;

&lt;p&gt;If a run returns fewer rows than you expected, check the input caps, language or country filters, and the Actor log. A zero-row run is usually a filter or an unavailable source, not a silent crash — read the log before you increase limits.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next step
&lt;/h2&gt;

&lt;p&gt;Open the Actor and run a small job: &lt;a href="https://apify.com/veenkes/veenkes-substack-posts" rel="noopener noreferrer"&gt;Substack Scraper: Export Newsletter Posts&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you later want this on a schedule, save the input that worked, add a date filter where it applies, and export CSV automatically from the dataset or from the API snippet above.&lt;/p&gt;

</description>
      <category>webscraping</category>
      <category>substack</category>
      <category>newsletter</category>
      <category>python</category>
    </item>
    <item>
      <title>How to Scrape Shopify Products to CSV</title>
      <dc:creator>veenkes</dc:creator>
      <pubDate>Fri, 13 Nov 2026 03:07:02 +0000</pubDate>
      <link>https://dev.to/veenkes/how-to-scrape-shopify-products-to-csv-4an8</link>
      <guid>https://dev.to/veenkes/how-to-scrape-shopify-products-to-csv-4an8</guid>
      <description>&lt;h1&gt;
  
  
  How to Scrape Shopify Products to CSV
&lt;/h1&gt;

&lt;p&gt;Every public Shopify store exposes a product catalog that is good enough to shop from — and, if you are careful and respectful, good enough to research. Price monitoring, assortment analysis, affiliate catalogs and competitor scoring all start from the same question: can I get this store's products, variants, prices and stock into a CSV? This guide shows how, without a Shopify admin login.&lt;/p&gt;

&lt;p&gt;You will use the &lt;a href="https://apify.com/veenkes/veenkes-shopify-products" rel="noopener noreferrer"&gt;Shopify Product Scraper&lt;/a&gt; on Apify. Point it at a store or a collection URL and export titles, vendors, types, tags, prices, compare-at prices, discount percent, availability, variants, SKUs and images. You can choose one row per product or one row per variant (size, colour, SKU).&lt;/p&gt;

&lt;p&gt;By the end of this article you will have a CSV or JSON file on your machine, and a short Python snippet you can rerun or schedule. The same Actor is also callable from JavaScript and from AI agents through the &lt;a href="https://docs.apify.com/platform/integrations/mcp" rel="noopener noreferrer"&gt;Apify MCP server&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you need
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;An &lt;a href="https://apify.com" rel="noopener noreferrer"&gt;Apify&lt;/a&gt; account. The Free plan is enough to try a small run.&lt;/li&gt;
&lt;li&gt;A list of inputs for the Actor (URLs, IDs or search terms — details in the steps below).&lt;/li&gt;
&lt;li&gt;Optional: Python 3 with the &lt;code&gt;apify-client&lt;/code&gt; package if you want to automate the run.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Actor reads the store's public catalog (including the well-known &lt;code&gt;products.json&lt;/code&gt; feed when the merchant leaves it available). You do not need the merchant's password or an admin API token.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to run it in the Apify Console
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Open the Actor
&lt;/h3&gt;

&lt;p&gt;Open &lt;a href="https://apify.com/veenkes/veenkes-shopify-products" rel="noopener noreferrer"&gt;https://apify.com/veenkes/veenkes-shopify-products&lt;/a&gt; and click &lt;strong&gt;Try for free&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Paste store URLs
&lt;/h3&gt;

&lt;p&gt;A homepage such as &lt;code&gt;https://colourpop.com&lt;/code&gt; works. A &lt;code&gt;/collections/...&lt;/code&gt; URL limits the run to that collection.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Choose row shape and a market
&lt;/h3&gt;

&lt;p&gt;Use product rows for a catalog overview, or variant rows for price and stock monitoring. Set the country if you need market-specific currency and pricing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Run and export CSV
&lt;/h3&gt;

&lt;p&gt;Cap &lt;em&gt;max products&lt;/em&gt; on the first run. When the dataset is ready, export CSV or JSON. Turn on descriptions only if you need the body copy.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Only available&lt;/em&gt; skips sold-out items. &lt;em&gt;Updated since&lt;/em&gt; is the right filter for incremental monitoring so you are not re-exporting an unchanged catalog every day.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to run it from Python
&lt;/h2&gt;

&lt;p&gt;Install the official client and call the Actor by its Store id &lt;code&gt;veenkes/veenkes-shopify-products&lt;/code&gt;. Replace &lt;code&gt;YOUR_APIFY_TOKEN&lt;/code&gt; with a token from &lt;a href="https://console.apify.com/account/integrations" rel="noopener noreferrer"&gt;Apify Console &amp;gt; Integrations&lt;/a&gt;. Do not commit the token to a public repository.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip install apify-client
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;apify_client&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;ApifyClient&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ApifyClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"YOUR_APIFY_TOKEN"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;actor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"veenkes/veenkes-shopify-products"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run_input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"stores"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s2"&gt;"https://colourpop.com"&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"maxProductsPerStore"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"outputLevel"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"product"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Write every dataset item, then export from the Console or keep going in code.&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"defaultDatasetId"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;iterate_items&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The JavaScript client (&lt;code&gt;apify-client&lt;/code&gt; on npm) uses the same Actor id and the same input object. You can also start the run with a plain HTTP &lt;code&gt;POST&lt;/code&gt; to the Apify API if you prefer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sample output
&lt;/h2&gt;

&lt;p&gt;Here is a shortened example of one dataset record (field values are illustrative of the schema, taken from a real sample run shape):&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="nl"&gt;"store"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"https://www.tentree.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"storeName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Tentree"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"collection"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"productId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;8143544320186&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Lookout Long Puffer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"handle"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"lookout-long-puffer-forest-pine"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"https://www.tentree.com/products/lookout-long-puffer-forest-pine"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"vendor"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"tentree"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"productType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Womens"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"tags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s2"&gt;"back-to-school"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"best-sellers"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"bogo-tote"&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"currency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"USD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"market"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"US"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"createdAt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"2025-06-06T10:20:29-07:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"publishedAt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"2026-11-11T14:23:07-07:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"updatedAt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"2026-11-12T14:01:32-07:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"imageUrl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"https://cdn.shopify.com/s/files/1/2341/3995/files/Green-Cloud-Shell-Long-Puffer-TCW3532-5590_11.jpg?v=1749230441"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;348&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"priceMax"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;348&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"compareAtPrice"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"discountPercent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"available"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"variantsCount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"options"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Color"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;"values"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s2"&gt;"FOREST PINE"&lt;/span&gt;
      &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Size"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;"values"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s2"&gt;"XS"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"S"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"M"&lt;/span&gt;
      &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"variants"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;44905051095226&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"FOREST PINE / XS"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;"sku"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"TCW3532-5590-XS"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;"price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;348&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;"compareAtPrice"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;"available"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;44905051127994&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"FOREST PINE / S"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;"sku"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"TCW3532-5590-S"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;"price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;348&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;"compareAtPrice"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;"available"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;44905051160762&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"FOREST PINE / M"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;"sku"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"TCW3532-5590-M"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;"price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;348&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;"compareAtPrice"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;"available"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="err"&gt;…&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the run has finished, open the dataset in the Console and use &lt;strong&gt;Export &amp;gt; CSV&lt;/strong&gt; if a spreadsheet is your destination. JSON is usually better for code and for language models.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to use the data for
&lt;/h2&gt;

&lt;p&gt;Brands track competitors' prices and discounting. Retail analysts map an assortment by vendor and product type. Affiliate and comparison sites keep a fresh product file with images and canonical URLs. Because variant mode is one row per SKU, restock and price-change jobs are straightforward to schedule.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pricing
&lt;/h2&gt;

&lt;p&gt;Pay-per-result pricing starts at $1.00 per 1,000 products on the Apify Free plan, with lower unit prices on paid Apify plans, plus a small per-run start fee. Always check the live pricing on the &lt;a href="https://apify.com/veenkes/veenkes-shopify-products" rel="noopener noreferrer"&gt;Actor page&lt;/a&gt;, because plans and unit prices can change. For a first test, keep the input tiny so you can inspect the schema before you scale up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scheduling and automations
&lt;/h2&gt;

&lt;p&gt;In the Apify Console you can schedule the Actor (daily or hourly) and leave the same input in place. For incremental jobs, prefer a "since" or "posted after" style filter when the Actor offers one, so you are not paying to re-collect old rows. When a run finishes you can send a webhook, or connect the Actor in Zapier, Make or n8n. AI agents can discover and call &lt;code&gt;veenkes/veenkes-shopify-products&lt;/code&gt; through Apify's MCP server using the same input.&lt;/p&gt;

&lt;h2&gt;
  
  
  Good practices and limits
&lt;/h2&gt;

&lt;p&gt;Only scrape public storefront data, and respect the merchant's terms and robots rules. Do not use this to copy a store's creative assets or descriptions into a competing shop. Some merchants disable public JSON endpoints; results then depend on what the storefront still exposes. The Actor is not affiliated with Shopify.&lt;/p&gt;

&lt;p&gt;If a run returns fewer rows than you expected, check the input caps, language or country filters, and the Actor log. A zero-row run is usually a filter or an unavailable source, not a silent crash — read the log before you increase limits.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next step
&lt;/h2&gt;

&lt;p&gt;Open the Actor and run a small job: &lt;a href="https://apify.com/veenkes/veenkes-shopify-products" rel="noopener noreferrer"&gt;Shopify Product Scraper: Prices, Variants &amp;amp; Stock&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you later want this on a schedule, save the input that worked, add a date filter where it applies, and export CSV automatically from the dataset or from the API snippet above.&lt;/p&gt;

</description>
      <category>webscraping</category>
      <category>ecommerce</category>
      <category>shopify</category>
      <category>python</category>
    </item>
    <item>
      <title>How to Scrape Jobs from Greenhouse, Lever, Ashby and Workable</title>
      <dc:creator>veenkes</dc:creator>
      <pubDate>Fri, 13 Nov 2026 00:53:52 +0000</pubDate>
      <link>https://dev.to/veenkes/how-to-scrape-jobs-from-greenhouse-lever-ashby-and-workable-2ib9</link>
      <guid>https://dev.to/veenkes/how-to-scrape-jobs-from-greenhouse-lever-ashby-and-workable-2ib9</guid>
      <description>&lt;h1&gt;
  
  
  How to Scrape Jobs from Greenhouse, Lever, Ashby and Workable
&lt;/h1&gt;

&lt;p&gt;A large share of tech and growth-stage hiring still runs through a handful of applicant tracking systems: Greenhouse, Lever, Ashby, Workable and SmartRecruiters. Each company gets a public board URL, and those boards are the most reliable place to see &lt;em&gt;current&lt;/em&gt; openings — better than a stale aggregate job site. If you want those jobs in one spreadsheet, you should not have to write a parser per ATS. This guide shows how to scrape them in a single run.&lt;/p&gt;

&lt;p&gt;You will use the &lt;a href="https://apify.com/veenkes/veenkes-ats-jobs" rel="noopener noreferrer"&gt;ATS Job Scraper&lt;/a&gt; on Apify. Paste career-board URLs; the Actor detects the ATS automatically and returns a normalized schema: title, location, remote flag, department, salary (when the board publishes it), dates and the full description.&lt;/p&gt;

&lt;p&gt;By the end of this article you will have a CSV or JSON file on your machine, and a short Python snippet you can rerun or schedule. The same Actor is also callable from JavaScript and from AI agents through the &lt;a href="https://docs.apify.com/platform/integrations/mcp" rel="noopener noreferrer"&gt;Apify MCP server&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you need
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;An &lt;a href="https://apify.com" rel="noopener noreferrer"&gt;Apify&lt;/a&gt; account. The Free plan is enough to try a small run.&lt;/li&gt;
&lt;li&gt;A list of inputs for the Actor (URLs, IDs or search terms — details in the steps below).&lt;/li&gt;
&lt;li&gt;Optional: Python 3 with the &lt;code&gt;apify-client&lt;/code&gt; package if you want to automate the run.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You do not need an ATS API key or a login. Public board URLs are enough.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to run it in the Apify Console
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Open the Actor
&lt;/h3&gt;

&lt;p&gt;Go to &lt;a href="https://apify.com/veenkes/veenkes-ats-jobs" rel="noopener noreferrer"&gt;https://apify.com/veenkes/veenkes-ats-jobs&lt;/a&gt; and click &lt;strong&gt;Try for free&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Paste board URLs
&lt;/h3&gt;

&lt;p&gt;Examples: &lt;code&gt;https://boards.greenhouse.io/stripe&lt;/code&gt; or &lt;code&gt;https://jobs.ashbyhq.com/ramp&lt;/code&gt;. Mix Greenhouse, Lever, Ashby, Workable and SmartRecruiters in the same run if you want.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Optional filters
&lt;/h3&gt;

&lt;p&gt;Add keywords, a location string, remote-only, or a &lt;em&gt;posted since&lt;/em&gt; date if you are building a daily new-jobs alert. Set a modest &lt;em&gt;max jobs per company&lt;/em&gt; for the first run.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Run and export
&lt;/h3&gt;

&lt;p&gt;Start the run and export the dataset as CSV or JSON. Full descriptions are included as plain text and HTML at no extra charge.&lt;/p&gt;

&lt;p&gt;If a URL is not a supported board, the run will skip or report it rather than silently inventing jobs. You pay only for jobs that are actually saved.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to run it from Python
&lt;/h2&gt;

&lt;p&gt;Install the official client and call the Actor by its Store id &lt;code&gt;veenkes/veenkes-ats-jobs&lt;/code&gt;. Replace &lt;code&gt;YOUR_APIFY_TOKEN&lt;/code&gt; with a token from &lt;a href="https://console.apify.com/account/integrations" rel="noopener noreferrer"&gt;Apify Console &amp;gt; Integrations&lt;/a&gt;. Do not commit the token to a public repository.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip install apify-client
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;apify_client&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;ApifyClient&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ApifyClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"YOUR_APIFY_TOKEN"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;actor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"veenkes/veenkes-ats-jobs"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run_input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"companies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s2"&gt;"https://boards.greenhouse.io/stripe"&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"maxJobsPerCompany"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"includeDescription"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Write every dataset item, then export from the Console or keep going in code.&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"defaultDatasetId"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;iterate_items&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The JavaScript client (&lt;code&gt;apify-client&lt;/code&gt; on npm) uses the same Actor id and the same input object. You can also start the run with a plain HTTP &lt;code&gt;POST&lt;/code&gt; to the Apify API if you prefer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sample output
&lt;/h2&gt;

&lt;p&gt;Here is a shortened example of one dataset record (field values are illustrative of the schema, taken from a real sample run shape):&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="nl"&gt;"platform"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"ashby"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"company"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"ramp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"companyName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"jobId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"b7fd55f4-6c5a-42f5-be04-aa09a4d5b7a9"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Senior Design Engineer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"https://jobs.ashbyhq.com/ramp/b7fd55f4-6c5a-42f5-be04-aa09a4d5b7a9"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"applyUrl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"https://jobs.ashbyhq.com/ramp/b7fd55f4-6c5a-42f5-be04-aa09a4d5b7a9/application"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"location"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Remote (US)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"allLocations"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s2"&gt;"Remote (US)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"San Francisco, CA"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"New York, NY (HQ)"&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"country"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"United States"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"remote"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"workplaceType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Remote"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"employmentType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"FullTime"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"department"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Engineering"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"team"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Engineering"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"salaryText"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"$188,000 - $259,250"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"salaryMin"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;188000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"salaryMax"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;259250&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"salaryCurrency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"USD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"salaryInterval"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"1 YEAR"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"publishedAt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"2026-11-12T20:01:54.072Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"updatedAt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"descriptionText"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"ABOUT RAMP\n\nRamp is building the smart infrastructure for finance teams, embedded in the transaction flow of every dollar a business spends. We automate how over $275B in annualize…"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"descriptionHtml"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;h1&amp;gt;&amp;lt;strong&amp;gt;About Ramp&amp;lt;/strong&amp;gt;&amp;lt;/h1&amp;gt;&amp;lt;p style=\"min-height:1.5em\"&amp;gt;Ramp is building the smart infrastructure for finance teams, embedded in the transaction flow of every dollar a busi…"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"scrapedAt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"2026-11-12T20:38:24.077Z"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the run has finished, open the dataset in the Console and use &lt;strong&gt;Export &amp;gt; CSV&lt;/strong&gt; if a spreadsheet is your destination. JSON is usually better for code and for language models.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to use the data for
&lt;/h2&gt;

&lt;p&gt;Recruiters watch a list of target companies and get new roles the day they go live. Founders and compensation researchers compare titles, locations and published salaries. Lead-generation teams treat a burst of hiring in a department as a buying signal. Job-board products use a normalized schema so Greenhouse and Ashby jobs can live in the same table.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pricing
&lt;/h2&gt;

&lt;p&gt;Pay-per-result pricing starts at $2.00 per 1,000 saved jobs on the Apify Free plan, with lower unit prices on paid Apify plans. Full descriptions are included. There is also a small per-run start fee. Always check the live pricing on the &lt;a href="https://apify.com/veenkes/veenkes-ats-jobs" rel="noopener noreferrer"&gt;Actor page&lt;/a&gt;, because plans and unit prices can change. For a first test, keep the input tiny so you can inspect the schema before you scale up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scheduling and automations
&lt;/h2&gt;

&lt;p&gt;In the Apify Console you can schedule the Actor (daily or hourly) and leave the same input in place. For incremental jobs, prefer a "since" or "posted after" style filter when the Actor offers one, so you are not paying to re-collect old rows. When a run finishes you can send a webhook, or connect the Actor in Zapier, Make or n8n. AI agents can discover and call &lt;code&gt;veenkes/veenkes-ats-jobs&lt;/code&gt; through Apify's MCP server using the same input.&lt;/p&gt;

&lt;h2&gt;
  
  
  Good practices and limits
&lt;/h2&gt;

&lt;p&gt;Job descriptions are written by the employer and may be copyrighted; use them for search, analysis or application workflows, not to republish a competing board wholesale. Always follow the company's and the ATS vendor's terms. The Actor is not affiliated with Greenhouse, Lever, Ashby, Workable or SmartRecruiters.&lt;/p&gt;

&lt;p&gt;If a run returns fewer rows than you expected, check the input caps, language or country filters, and the Actor log. A zero-row run is usually a filter or an unavailable source, not a silent crash — read the log before you increase limits.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next step
&lt;/h2&gt;

&lt;p&gt;Open the Actor and run a small job: &lt;a href="https://apify.com/veenkes/veenkes-ats-jobs" rel="noopener noreferrer"&gt;ATS Job Scraper: Greenhouse, Lever, Ashby &amp;amp; Workable&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you later want this on a schedule, save the input that worked, add a date filter where it applies, and export CSV automatically from the dataset or from the API snippet above.&lt;/p&gt;

</description>
      <category>webscraping</category>
      <category>jobs</category>
      <category>recruiting</category>
      <category>python</category>
    </item>
    <item>
      <title>How to Scrape Google Play Reviews to CSV</title>
      <dc:creator>veenkes</dc:creator>
      <pubDate>Fri, 13 Nov 2026 00:53:10 +0000</pubDate>
      <link>https://dev.to/veenkes/how-to-scrape-google-play-reviews-to-csv-1n9e</link>
      <guid>https://dev.to/veenkes/how-to-scrape-google-play-reviews-to-csv-1n9e</guid>
      <description>&lt;h1&gt;
  
  
  How to Scrape Google Play Reviews to CSV
&lt;/h1&gt;

&lt;p&gt;If you ship an Android app, Play Store reviews are one of the few places users tell you — in their own words — why they stay, churn, or leave one star. The official Play Console is fine for your own app, but it will not give you a clean CSV of a competitor's reviews, and it is awkward to pipe into a spreadsheet or a sentiment model. This guide shows how to scrape Google Play reviews to CSV (or JSON) in a few minutes, then how to do the same from Python.&lt;/p&gt;

&lt;p&gt;You will use the &lt;a href="https://apify.com/veenkes/veenkes-google-play-reviews" rel="noopener noreferrer"&gt;Google Play Reviews Scraper&lt;/a&gt; on Apify. It accepts app IDs or Play Store URLs, can filter by star rating, language, country and date, and writes one flat row per review: rating, text, date, thumbs-up count, app version and the developer's reply.&lt;/p&gt;

&lt;p&gt;By the end of this article you will have a CSV or JSON file on your machine, and a short Python snippet you can rerun or schedule. The same Actor is also callable from JavaScript and from AI agents through the &lt;a href="https://docs.apify.com/platform/integrations/mcp" rel="noopener noreferrer"&gt;Apify MCP server&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you need
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;An &lt;a href="https://apify.com" rel="noopener noreferrer"&gt;Apify&lt;/a&gt; account. The Free plan is enough to try a small run.&lt;/li&gt;
&lt;li&gt;A list of inputs for the Actor (URLs, IDs or search terms — details in the steps below).&lt;/li&gt;
&lt;li&gt;Optional: Python 3 with the &lt;code&gt;apify-client&lt;/code&gt; package if you want to automate the run.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You do not need a Google account, a browser automation script, or a Play Developer API key. The Actor reads the public review pages of the Play Store.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to run it in the Apify Console
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Open the Actor
&lt;/h3&gt;

&lt;p&gt;Go to &lt;a href="https://apify.com/veenkes/veenkes-google-play-reviews" rel="noopener noreferrer"&gt;https://apify.com/veenkes/veenkes-google-play-reviews&lt;/a&gt; and click &lt;strong&gt;Try for free&lt;/strong&gt;. If you do not have an Apify account, create one; the Free plan includes monthly usage credit you can spend on this run.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Add the apps
&lt;/h3&gt;

&lt;p&gt;In the input form, paste a package name such as &lt;code&gt;com.spotify.music&lt;/code&gt; or a full Play Store URL. You can add several apps in one run.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Set a review cap and filters
&lt;/h3&gt;

&lt;p&gt;Set &lt;strong&gt;Maximum reviews per app&lt;/strong&gt; to something small first — 50 or 100 is enough to check the output. Optionally filter to 1–2 star reviews, pick a language (&lt;code&gt;en&lt;/code&gt;, &lt;code&gt;de&lt;/code&gt;, &lt;code&gt;es&lt;/code&gt;, …), a store country, or a &lt;em&gt;since&lt;/em&gt; date if you only want new reviews.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Run and export CSV
&lt;/h3&gt;

&lt;p&gt;Click &lt;strong&gt;Start&lt;/strong&gt;, wait for the run to finish, then open the dataset and choose &lt;strong&gt;Export &amp;gt; CSV&lt;/strong&gt;. You can also export JSON, Excel or XML. Each row is one review, ready for a spreadsheet or a pandas DataFrame.&lt;/p&gt;

&lt;p&gt;Useful filters: &lt;em&gt;Only reviews with text&lt;/em&gt; drops star-only reviews; &lt;em&gt;Reviews since&lt;/em&gt; plus Newest sort is a good daily incremental job; reviewer names are off by default if you want to stay conservative on personal data.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to run it from Python
&lt;/h2&gt;

&lt;p&gt;Install the official client and call the Actor by its Store id &lt;code&gt;veenkes/veenkes-google-play-reviews&lt;/code&gt;. Replace &lt;code&gt;YOUR_APIFY_TOKEN&lt;/code&gt; with a token from &lt;a href="https://console.apify.com/account/integrations" rel="noopener noreferrer"&gt;Apify Console &amp;gt; Integrations&lt;/a&gt;. Do not commit the token to a public repository.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip install apify-client
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;apify_client&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;ApifyClient&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ApifyClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"YOUR_APIFY_TOKEN"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;actor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"veenkes/veenkes-google-play-reviews"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run_input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"apps"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s2"&gt;"com.spotify.music"&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"maxReviewsPerApp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"language"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"en"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Write every dataset item, then export from the Console or keep going in code.&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"defaultDatasetId"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;iterate_items&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The JavaScript client (&lt;code&gt;apify-client&lt;/code&gt; on npm) uses the same Actor id and the same input object. You can also start the run with a plain HTTP &lt;code&gt;POST&lt;/code&gt; to the Apify API if you prefer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sample output
&lt;/h2&gt;

&lt;p&gt;Here is a shortened example of one dataset record (field values are illustrative of the schema, taken from a real sample run shape):&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="nl"&gt;"appId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"com.spotify.music"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"appTitle"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Spotify: Music and Podcasts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"appDeveloper"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Spotify AB"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"appScore"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;4.3483925&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"appRatingsCount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;36631501&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"appReviewsCount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1858962&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"appInstalls"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"1,000,000,000+"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"appGenre"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Music &amp;amp; Audio"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"appVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"VARY"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"reviewId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"13b5c818-6009-49e7-af2e-2a076f33aaf5"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"reviewUrl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"https://play.google.com/store/apps/details?id=com.spotify.music&amp;amp;reviewId=13b5c818-6009-49e7-af2e-2a076f33aaf5"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"rating"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"id rather this than apple but the quality of the app has gone down so much since ai, its almost as if features introduced were written by ai and not tested. random features break f…"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"2026-11-11T19:54:47.254Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"thumbsUp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"appVersionReviewed"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"9.1.98.621"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"developerReply"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"developerReplyDate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"language"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"en"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"country"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"us"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"scrapedAt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"2026-11-12T20:02:33.121Z"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the run has finished, open the dataset in the Console and use &lt;strong&gt;Export &amp;gt; CSV&lt;/strong&gt; if a spreadsheet is your destination. JSON is usually better for code and for language models.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to use the data for
&lt;/h2&gt;

&lt;p&gt;Typical uses include ASO (what phrases show up in 1-star reviews), product research (which versions triggered a wave of complaints), competitor monitoring, and building a training set for a sentiment or topic model. Because the rows are flat, you can schedule a daily run, keep only reviews newer than yesterday, and append the CSV to a warehouse.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pricing
&lt;/h2&gt;

&lt;p&gt;Pay-per-result pricing starts at $0.25 per 1,000 reviews on the Apify Free plan, with lower unit prices on paid Apify plans, plus a small per-run start fee. Always check the live pricing on the &lt;a href="https://apify.com/veenkes/veenkes-google-play-reviews" rel="noopener noreferrer"&gt;Actor page&lt;/a&gt;, because plans and unit prices can change. For a first test, keep the input tiny so you can inspect the schema before you scale up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scheduling and automations
&lt;/h2&gt;

&lt;p&gt;In the Apify Console you can schedule the Actor (daily or hourly) and leave the same input in place. For incremental jobs, prefer a "since" or "posted after" style filter when the Actor offers one, so you are not paying to re-collect old rows. When a run finishes you can send a webhook, or connect the Actor in Zapier, Make or n8n. AI agents can discover and call &lt;code&gt;veenkes/veenkes-google-play-reviews&lt;/code&gt; through Apify's MCP server using the same input.&lt;/p&gt;

&lt;h2&gt;
  
  
  Good practices and limits
&lt;/h2&gt;

&lt;p&gt;Reviews are public, but they may still contain personal data if you enable reviewer info, and the text itself can be copyrighted by the authors. Use the data in line with Google Play's terms and applicable law. This is not an official Google API, and the Actor is not affiliated with Google.&lt;/p&gt;

&lt;p&gt;If a run returns fewer rows than you expected, check the input caps, language or country filters, and the Actor log. A zero-row run is usually a filter or an unavailable source, not a silent crash — read the log before you increase limits.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next step
&lt;/h2&gt;

&lt;p&gt;Open the Actor and run a small job: &lt;a href="https://apify.com/veenkes/veenkes-google-play-reviews" rel="noopener noreferrer"&gt;Google Play Reviews Scraper&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you later want this on a schedule, save the input that worked, add a date filter where it applies, and export CSV automatically from the dataset or from the API snippet above.&lt;/p&gt;

</description>
      <category>webscraping</category>
      <category>android</category>
      <category>aso</category>
      <category>python</category>
    </item>
  </channel>
</rss>
