<?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: CleanScrape</title>
    <description>The latest articles on DEV Community by CleanScrape (@cleanscrape).</description>
    <link>https://dev.to/cleanscrape</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%2F4126565%2F7f3a2bef-43ea-47db-a8a6-f8923b4ae916.png</url>
      <title>DEV Community: CleanScrape</title>
      <link>https://dev.to/cleanscrape</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/cleanscrape"/>
    <language>en</language>
    <item>
      <title>Comparing store prices on Google Shopping: the same Sony headphones were $199.99 and $349.99</title>
      <dc:creator>CleanScrape</dc:creator>
      <pubDate>Fri, 06 Nov 2026 06:58:47 +0000</pubDate>
      <link>https://dev.to/cleanscrape/comparing-store-prices-on-google-shopping-the-same-sony-headphones-were-19999-and-34999-o5</link>
      <guid>https://dev.to/cleanscrape/comparing-store-prices-on-google-shopping-the-same-sony-headphones-were-19999-and-34999-o5</guid>
      <description>&lt;p&gt;Google Shopping shows one store per product and a note that more stores sell it. Checking each one by hand gets old quickly, so I built a Google Shopping scraper and price tracker on Apify. You type a product name, model number or barcode, pick a country, and get every listing back as a row you can export. Turn on "Compare stores" and each product also gets a table of the stores Google lists for it. Put it on a schedule and each run keeps only price changes.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/MupcEQaofxY" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  The input
&lt;/h2&gt;

&lt;p&gt;This is the run from the video:&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;"queries"&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="s2"&gt;"sony wh-1000xm5"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"country"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"us"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"compareStores"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"storesForTopProducts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&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;A search can be words, a model number, a UPC/EAN barcode, or a Google Shopping link. A results link keeps the filters you set on Google (price range, brand, store); a product link reads that product's stores directly. There are 32 countries, each in its own currency.&lt;/p&gt;

&lt;h2&gt;
  
  
  The output
&lt;/h2&gt;

&lt;p&gt;The search returned 40 products in under a minute, together with the store pages for 3 of them. One product row:&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;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Sony WH-1000XM5 Wireless Noise Canceling Headphones"&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;224.99&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"currency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"USD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"originalPrice"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;398&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"discountPercent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;43&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"store"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Best Buy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"moreStores"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"delivery"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Free delivery"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"rating"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;4.6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reviewCount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;13000&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;The Stores table for that product listed five stores: Sony at $199.99 (marked "Best price"), Best Buy at $224.99, Greentoe at $249, a Walmart seller at $249.99 and TikTok Shop at $349.99. That's $150 apart for the same headphones. Each store row has stock, delivery, returns and the link to the product on the store's own site, and the product carries Google's typical price range: $199 to $300.&lt;/p&gt;

&lt;h2&gt;
  
  
  What testing found
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Google sometimes answers from the wrong country.&lt;/strong&gt; Requests go through Apify's Google search proxy, and about one page in seven came back localised to wherever the exit IP was: a US search priced in Brazilian reais, Saudi riyals or Colombian pesos. The &lt;code&gt;gl&lt;/code&gt; parameter didn't stop it, and neither did Google's &lt;code&gt;uule&lt;/code&gt; location parameter (5 of 24 US searches were still wrong). What works is checking the answer: a page from another country prices nearly everything in its own currency. Those pages are asked again and never charged, and a search that keeps coming back from abroad fails for free. In a 100-search test, 17 such answers were caught and retried.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Two card layouts.&lt;/strong&gt; US and Canadian results use one card layout, UK and European ones another ("Current price ... And more prices"). Class names change often, so the parser reads each card's text and accessibility labels in order instead. A "3D" badge on cards with a 3D viewer looked like an amount and pushed the title into the store column until a fixture caught it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prices come in every format.&lt;/strong&gt; &lt;code&gt;$1,299.99&lt;/code&gt;, &lt;code&gt;£199.00&lt;/code&gt;, &lt;code&gt;2 547,00 kr&lt;/code&gt;, &lt;code&gt;CHF 199.00&lt;/code&gt;, &lt;code&gt;R$417.08&lt;/code&gt;, &lt;code&gt;¥20,800&lt;/code&gt;, &lt;code&gt;US$249&lt;/code&gt;. A bare &lt;code&gt;$&lt;/code&gt; means US, Canadian or Australian dollars depending on the country searched.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Some product pages have no details.&lt;/strong&gt; Google answers some of them with "Details aren't available for this product". Those pages are free, and the run tries the next product instead, so you still get the number of store lists you asked for. Products Google sells at several stores go first; a single marketplace listing has only one store to show.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tracking needed three fixes.&lt;/strong&gt; The first version flagged cent-level changes on sellers abroad (exchange rates move), treated every eBay seller as one store, and called listings "new" when Google simply rotated them back in. It now follows Google's listing IDs, ignores moves under 0.5% and exchange-rate-only changes, and remembers listings for 30 days.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tracking prices
&lt;/h2&gt;

&lt;p&gt;Turn on "Only new products and price changes", give it a tracking name and schedule it. Google rotates marketplace listings (eBay, Poshmark and similar) between runs, so the first few runs still find listings they haven't seen; "Skip new listings" keeps a schedule to real price moves. In my test, the first run saved 49 listings for three products, and a later run with "Skip new listings" found no price changes and saved nothing.&lt;/p&gt;

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

&lt;p&gt;$1.50 per 1,000 products, $6 per 1,000 searches and $7 per 1,000 product pages for store comparison, less on paid Apify plans. One search with 40 products costs about seven cents. Searches that fail, wrong-country answers and product pages without details are free.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://apify.com/cleanscrape/google-shopping-scraper" rel="noopener noreferrer"&gt;https://apify.com/cleanscrape/google-shopping-scraper&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Questions or ideas: &lt;a href="mailto:contact.cleanscrape@gmail.com"&gt;contact.cleanscrape@gmail.com&lt;/a&gt;, or the Issues tab on the Actor page.&lt;/p&gt;

&lt;p&gt;Niko, CleanScrape developer&lt;/p&gt;

</description>
      <category>api</category>
      <category>javascript</category>
      <category>ecommerce</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Tracking Google Flights fares: New York to London was $197 on December 8 and $512 on December 20</title>
      <dc:creator>CleanScrape</dc:creator>
      <pubDate>Thu, 05 Nov 2026 18:51:45 +0000</pubDate>
      <link>https://dev.to/cleanscrape/tracking-google-flights-fares-new-york-to-london-was-197-on-december-8-and-512-on-december-20-45j3</link>
      <guid>https://dev.to/cleanscrape/tracking-google-flights-fares-new-york-to-london-was-197-on-december-8-and-512-on-december-20-45j3</guid>
      <description>&lt;p&gt;Flight prices change every day, and checking them by hand gets old quickly. So I built a Google Flights scraper and fare tracker on Apify. You type routes the way you'd say them, pick dates, and get every flight back as a row in a table you can export. Put it on a schedule and each run gives you only new flights or lower prices.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/IeIzIX1f_hc" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  The input
&lt;/h2&gt;

&lt;p&gt;This is the run from the video:&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;"routes"&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="s2"&gt;"New York to London"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"departureDates"&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="s2"&gt;"+30"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"maxFlightsPerSearch"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20&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;A route can be airport codes (&lt;code&gt;JFK-LAX&lt;/code&gt;), a city code that covers all of a city's airports (&lt;code&gt;NYC-LON&lt;/code&gt;), or plain city names. Dates can be one day, a range like &lt;code&gt;2026-12-01..2026-12-31&lt;/code&gt;, or &lt;code&gt;+30&lt;/code&gt;, which means 30 days after the run date. That last one is what makes scheduled runs useful: a daily schedule always looks the same distance ahead.&lt;/p&gt;

&lt;p&gt;There's also round trips (with the real return flights for the cheapest outbound options), passengers, cabin, nonstop or one-stop limits, airlines or alliances, 22 currencies and the country you're buying from.&lt;/p&gt;

&lt;h2&gt;
  
  
  The output
&lt;/h2&gt;

&lt;p&gt;New York to London for December 5 came back with 20 flights in 3 seconds, from all three New York airports to three London airports. One row looks like this:&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;"route"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"NYC-LON"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"from"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"JFK"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"to"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"LHR"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"departure"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-12-05T18:00:00-05:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"arrival"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-12-06T06:20:00+00:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"duration"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"7 h 20 min"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"stops"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;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;"airlines"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Virgin Atlantic"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"flightNumbers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"VS4"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"aircraft"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Airbus A330-900neo"&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="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"currency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"USD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"co2Kg"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;369&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;Each search also gets a row in a second table with the cheapest fare ($270 here), the cheapest nonstop ($300), Google's typical price for the trip ($279) and its verdict: low, typical or high. It includes Google's own price history for about the last 60 days. For this trip it was $197 two months ago and dipped to $109 one day.&lt;/p&gt;

&lt;h2&gt;
  
  
  The cheapest day in a month
&lt;/h2&gt;

&lt;p&gt;One search per departure day from December 5 to January 4, keeping only the cheapest flight each day, turns the Searches table into a price calendar. December 8 to 10 cost $197. December 20 cost $512, and Google marked it high. That's 31 searches, which ran in 14 seconds.&lt;/p&gt;

&lt;h2&gt;
  
  
  What testing found
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Time zones matter more than they look.&lt;/strong&gt; An evening flight from New York lands in London the next morning. Each time keeps its UTC offset, so &lt;code&gt;arrival&lt;/code&gt; above is Dec 6 even though the search was for Dec 5. I checked this on every row of a 300-search run: no arrival before its departure, and every duration matches the two timestamps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Google's price level isn't three values.&lt;/strong&gt; I first mapped it to low, typical and high, and an audit showed "high" days sitting inside Google's typical range. The real scale has five steps: one below the typical range, three inside it and one above. Comparing every search with its range made that obvious.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Not every city code works.&lt;/strong&gt; City codes like NYC, LON, PAR and TYO cover several airports, but Google rejects Dallas's code and treats a few others as a single airport. I tested every code in the table and mapped those cities to their main airport. An example task that searched "Dallas to Cancun" caught it before release.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Memory.&lt;/strong&gt; Google's result pages are 3.5 MB each, and the first version ran out of memory at 256 MB with six searches in parallel. It now decodes only the part of the page that holds the flights, about 70 KB, and peaks around 150 MB.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pages sometimes arrive incomplete.&lt;/strong&gt; About one page in twelve came back without flight data and worked on the next try, so every search retries a few times before it counts as failed. In a 300-search test, all 300 got an answer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tracking fares
&lt;/h2&gt;

&lt;p&gt;Turn on "Only new flights or lower prices", give it a tracking name and schedule it. The first run saves everything it finds. Later runs save a flight only if it's new for that route and date, or its price dropped, with the previous price next to it. In my two test runs 12 minutes apart nothing had changed, so the second run saved nothing, and its Searches rows showed the cheapest fare at the same $270 and $384 as before.&lt;/p&gt;

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

&lt;p&gt;$1 per 1,000 flights saved and $0.50 per 1,000 searches, less on paid Apify plans. One route on one date with 20 flights costs about two cents. The search fee is there so scheduled checks that find nothing new still cover their cost; failed searches are free.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://apify.com/cleanscrape/google-flights-scraper" rel="noopener noreferrer"&gt;https://apify.com/cleanscrape/google-flights-scraper&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Questions or ideas: &lt;a href="mailto:contact.cleanscrape@gmail.com"&gt;contact.cleanscrape@gmail.com&lt;/a&gt;, or the Issues tab on the Actor page.&lt;/p&gt;

&lt;p&gt;Niko, CleanScrape developer&lt;/p&gt;

</description>
      <category>api</category>
      <category>javascript</category>
      <category>travel</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Turning podcasts into text: a 52-minute NASA episode, 8,022 words, and the bugs real audio found</title>
      <dc:creator>CleanScrape</dc:creator>
      <pubDate>Sat, 31 Oct 2026 17:41:45 +0000</pubDate>
      <link>https://dev.to/cleanscrape/turning-podcasts-into-text-a-52-minute-nasa-episode-8022-words-and-the-bugs-real-audio-found-56n0</link>
      <guid>https://dev.to/cleanscrape/turning-podcasts-into-text-a-52-minute-nasa-episode-8022-words-and-the-bugs-real-audio-found-56n0</guid>
      <description>&lt;p&gt;A podcast holds a lot of useful information, and almost none of it can be searched, quoted or passed to another tool. So I built a podcast transcription tool on Apify. You paste a feed, an Apple Podcasts link or a link to an audio file, and you get the text back with timestamps and subtitle files.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/XYkpm8EJe4s" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  The input
&lt;/h2&gt;

&lt;p&gt;One link is enough. This is the run from the video, an episode of NASA's Houston We Have a Podcast:&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;"sources"&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="s2"&gt;"https://podcasts.apple.com/us/podcast/the-moon-base/id1262594123?i=1000789048885"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"quality"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"accurate"&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;It also accepts an RSS feed, a podcast's website, or a direct link to an MP3, M4A, WAV or MP4 file. At launch, Standard used the Whisper base model and Accurate used Whisper small, both inside the Actor. Standard now runs Whisper large-v3-turbo on Cloudflare Workers AI (see the update below). Either way there's no OpenAI key to bring.&lt;/p&gt;

&lt;h2&gt;
  
  
  The output
&lt;/h2&gt;

&lt;p&gt;The 52:18 episode became 8,022 words in 23 minutes on Accurate. Each row has the show, the title, the date, the length, the detected language, the full text and timestamped segments:&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;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"The Moon Base"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"duration"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"00:52:18"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"language"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"en"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"wordCount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8022&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"segments"&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="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;80.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;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"a home for astronauts on another celestial body."&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;You also get TXT, SRT and VTT files, so the transcript can go straight onto a video or into a search index.&lt;/p&gt;

&lt;h2&gt;
  
  
  What real audio taught me
&lt;/h2&gt;

&lt;p&gt;The first version passed its tests on short clean clips. Real podcasts found the interesting bugs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Memory grows with the audio.&lt;/strong&gt; Whisper's memory use went up by about 55 MB per minute of audio. A 5-minute test looked fine, and a 5-hour file crashed a 4 GB run. The fix was to feed it 5-minute chunks, each cut at the quietest tenth of a second near the boundary so no word gets split. A 5-hour file now peaks at 1.75 GB.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The first 30 seconds can lie.&lt;/strong&gt; Whisper detects the language from the start of the file. Podcasts often start with an ad, and one Finnish episode opened with an English ad, so the whole episode was transcribed as English. It now takes short samples across the file and lets them vote, weighted by confidence.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The same show, pasted twice, cost double.&lt;/strong&gt; A feed link and an Apple link for one show are different links to the same audio. Episodes already in the run are now skipped, whichever link they came from.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Real speech costs more than a test sample.&lt;/strong&gt; My first benchmark used a clean narration. Real conversation ran about 25% slower, and Finnish was the slowest of the languages I tried. That changed the price of the Accurate tier before launch.&lt;/p&gt;

&lt;h2&gt;
  
  
  Following a show
&lt;/h2&gt;

&lt;p&gt;Give a run a watch name and put it on a schedule. The first run takes the newest episode, and each later run transcribes only what has appeared since. A check with nothing new costs $0.003.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it can't do
&lt;/h2&gt;

&lt;p&gt;It doesn't label speakers. On Apify only it works on one language per file; Standard detects the language for every 90 seconds or so. Some podcast hosts refuse automated downloads: in a test of 134 shows, the newest episode of 127 could be reached. The rest are reported in the run and their audio isn't charged. YouTube, TikTok and Spotify links aren't supported.&lt;/p&gt;

&lt;h2&gt;
  
  
  Update, November 2026: a faster Standard
&lt;/h2&gt;

&lt;p&gt;Standard now sends the audio in 90-second pieces to Whisper large-v3-turbo on Cloudflare Workers AI. On this same episode it made 4.2% word errors against NASA's own transcript, against 5.2% for the run above, and took about a minute instead of 23. One setting mattered: with &lt;code&gt;condition_on_previous_text&lt;/code&gt; left on, the model invented about 300 words in repeating loops.&lt;/p&gt;

&lt;p&gt;When Cloudflare's daily capacity is used up or it doesn't answer, Whisper base on Apify finishes the file at the same price, and the &lt;code&gt;model&lt;/code&gt; field says which ran. Accurate is now called Apify only, for audio that has to stay on Apify.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://apify.com/cleanscrape/podcast-transcription" rel="noopener noreferrer"&gt;https://apify.com/cleanscrape/podcast-transcription&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;$6 per 1,000 audio minutes on Standard and $15 on Apify only, less on paid Apify plans. The episode above cost $0.80. You can export to Excel, CSV or JSON, or call it from your own code or an AI agent through Apify's MCP server.&lt;/p&gt;

&lt;p&gt;Niko, CleanScrape developer&lt;/p&gt;

</description>
      <category>ai</category>
      <category>python</category>
      <category>api</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>An economic calendar built only from official sources: the Fed, ECB, BLS, ONS and more in one table</title>
      <dc:creator>CleanScrape</dc:creator>
      <pubDate>Tue, 27 Oct 2026 16:07:47 +0000</pubDate>
      <link>https://dev.to/cleanscrape/an-economic-calendar-built-only-from-official-sources-the-fed-ecb-bls-ons-and-more-in-one-table-3i3k</link>
      <guid>https://dev.to/cleanscrape/an-economic-calendar-built-only-from-official-sources-the-fed-ecb-bls-ons-and-more-in-one-table-3i3k</guid>
      <description>&lt;p&gt;Every number on an economic calendar has a release time set by an official agency: the Bureau of Labor Statistics for CPI and jobs, the BEA for GDP, the Fed and the ECB for rate decisions. Those agencies all publish their schedules openly, but each in its own format: an iCalendar file, a JSON feed, an HTML table, a paged API. So I built a small tool on Apify that reads 13 of those official calendars and returns one clean table.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/NvZ4CM56lpc" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  The input
&lt;/h2&gt;

&lt;p&gt;Pick economies and dates. Everything else is optional.&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;"countries"&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="s2"&gt;"US"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"EA"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"UK"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CA"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AU"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"dateRange"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"next7"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"minimumImpact"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"medium"&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;That run took 26 seconds and saved 20 releases for 27 October to 2 November 2026. Wednesday had Australia's CPI at 00:30 UTC, the Bank of Canada at 13:45 and the Fed's decision at 18:00. Thursday had US GDP at 12:30 and the ECB at 13:15. The UK had no medium or high impact releases that week.&lt;/p&gt;

&lt;h2&gt;
  
  
  Times you can trust, and when you can't
&lt;/h2&gt;

&lt;p&gt;Every row has the time in UTC and in the agency's local time, plus a status:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;confirmed&lt;/strong&gt;: the agency published the exact time (14 of the 20)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;standard&lt;/strong&gt;: a central bank's usual time, like the ECB's 14:15 Frankfurt time&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;provisional&lt;/strong&gt;: a weekly schedule that can shift around holidays (US jobless claims)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;date only&lt;/strong&gt;: the agency publishes the day but not the time&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Past releases come with values
&lt;/h2&gt;

&lt;p&gt;For past CPI releases the tool adds the value. US CPI and euro area HICP carry the first published annual rate; the UK, Canada and Australia carry the current official series value. One run over the past year returned 76 inflation releases from all five economies. In the euro area, inflation went from 1.7% in January 2026 to 3.8% in September.&lt;/p&gt;

&lt;p&gt;There are no consensus forecasts: those are compiled privately and are not free to reuse.&lt;/p&gt;

&lt;h2&gt;
  
  
  Following changes
&lt;/h2&gt;

&lt;p&gt;Give the run a watch name and schedule it daily. After the first run, each run returns only what changed: new, moved, cancelled or released events.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://apify.com/cleanscrape/economic-calendar-api" rel="noopener noreferrer"&gt;https://apify.com/cleanscrape/economic-calendar-api&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;$3 per 1,000 events plus half a cent per country checked, less on paid Apify plans. The week above cost $0.08. Export to Excel, CSV or Google Sheets, or call it from your own code or an AI agent through Apify's MCP server.&lt;/p&gt;

&lt;p&gt;Niko, CleanScrape developer&lt;/p&gt;

</description>
      <category>datascience</category>
      <category>webscraping</category>
      <category>api</category>
      <category>fintech</category>
    </item>
    <item>
      <title>Every cut in a 15-minute film: extracting 166 scene frames from a video link</title>
      <dc:creator>CleanScrape</dc:creator>
      <pubDate>Sun, 25 Oct 2026 14:21:19 +0000</pubDate>
      <link>https://dev.to/cleanscrape/every-cut-in-a-15-minute-film-extracting-166-scene-frames-from-a-video-link-4o66</link>
      <guid>https://dev.to/cleanscrape/every-cut-in-a-15-minute-film-extracting-166-scene-frames-from-a-video-link-4o66</guid>
      <description>&lt;p&gt;If you want to send a video to an AI vision model, build a thumbnail picker, or index a lecture by its slides, you rarely want every frame. You want one frame per shot. So I built a small tool on Apify that takes a video link and returns those frames as images, with their exact times.&lt;/p&gt;

&lt;p&gt;Here is what it does on Sintel, the 14:48 open movie from the Blender Foundation.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/Er-PwjK-e8c" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  The input
&lt;/h2&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;"videos"&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="s2"&gt;"https://archive.org/download/Sintel/sintel-2048-stereo.mp4"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"scenes"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"contactSheet"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&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;It found &lt;strong&gt;166 shots&lt;/strong&gt; and saved one frame for each. The scan took 3 minutes 22 seconds.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it finds the cuts
&lt;/h2&gt;

&lt;p&gt;In this precise mode it compares every frame with the one before. When the picture changes completely, that is a cut, and a frame is saved there. When the camera only pans or zooms, the picture changes a little at a time; that is not counted as a cut. (An earlier version did count fast camera moves, which is how I learned to require a sharp peak rather than a large change.)&lt;/p&gt;

&lt;h2&gt;
  
  
  When you don't need every cut
&lt;/h2&gt;

&lt;p&gt;Overview mode reads only the keyframes, the full pictures a video file stores every few seconds. On the same film it returned 24 frames in &lt;strong&gt;21 seconds&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Slides and screen recordings
&lt;/h2&gt;

&lt;p&gt;On a 60-second lecture recording with a moving mouse pointer, precise mode with high sensitivity found all &lt;strong&gt;6 slides&lt;/strong&gt; (at 0:00, 0:10, 0:20, 0:30, 0:40 and 0:50) and ignored the pointer.&lt;/p&gt;

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

&lt;p&gt;Every frame comes with its time, timecode, size and an image link (JPG, PNG or WebP), and every video gets a contact sheet: all its frames on one page. Other modes: a frame every N seconds, a set number of evenly spaced frames, or exact timestamps.&lt;/p&gt;

&lt;p&gt;It reads MP4, MOV, WebM, MKV, AVI, MPEG-TS, OGV and animated GIF from direct links, S3 and other storage links, Dropbox and Google Drive.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://apify.com/cleanscrape/video-frame-extractor" rel="noopener noreferrer"&gt;https://apify.com/cleanscrape/video-frame-extractor&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;$2 per 1,000 frames, $0.005 per video, and in precise mode $0.01 per minute of 1080p video at 30 fps (smaller videos count less): the 166-frame Sintel run costs about $0.45, the overview run about $0.05. Use it on videos you own or may use.&lt;/p&gt;

&lt;p&gt;Sintel © Blender Foundation, durian.blender.org, CC BY 3.0.&lt;/p&gt;

&lt;p&gt;Niko, CleanScrape developer&lt;/p&gt;

</description>
      <category>video</category>
      <category>ai</category>
      <category>automation</category>
      <category>ffmpeg</category>
    </item>
    <item>
      <title>Which AI companies are hiring engineers, and what they pay: 691 jobs from career pages in 9 seconds</title>
      <dc:creator>CleanScrape</dc:creator>
      <pubDate>Sun, 25 Oct 2026 14:20:08 +0000</pubDate>
      <link>https://dev.to/cleanscrape/which-ai-companies-are-hiring-engineers-and-what-they-pay-691-jobs-from-career-pages-in-9-seconds-34e4</link>
      <guid>https://dev.to/cleanscrape/which-ai-companies-are-hiring-engineers-and-what-they-pay-691-jobs-from-career-pages-in-9-seconds-34e4</guid>
      <description>&lt;p&gt;Job boards repost, duplicate and keep expired listings. The cleanest source for who is hiring is the company's own careers page, and most companies run it on one of a handful of job board systems: Greenhouse, Lever, Ashby, Workable, Recruitee, Personio or Teamtailor. So I built a small tool on Apify that reads those boards directly.&lt;/p&gt;

&lt;p&gt;Here is one real run: engineering jobs at seven AI companies.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/T7sMVYYw1ys" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  The input
&lt;/h2&gt;

&lt;p&gt;You can paste a careers link, the company's website, or just its name. The tool finds the right board and says how it found it, so nothing is guessed.&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;"companies"&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="s2"&gt;"https://jobs.ashbyhq.com/openai"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"https://job-boards.greenhouse.io/anthropic"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"perplexity.ai"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"ElevenLabs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"https://jobs.ashbyhq.com/cohere"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"https://jobs.lever.co/mistral"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"huggingface.co"&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;"titleKeywords"&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="s2"&gt;"engineer"&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;The run took 9 seconds and saved 691 open jobs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who is hiring
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;OpenAI&lt;/strong&gt;: 356 engineering jobs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Anthropic&lt;/strong&gt;: 209&lt;/li&gt;
&lt;li&gt;Perplexity 46, ElevenLabs 41, Cohere 34, Hugging Face 5&lt;/li&gt;
&lt;li&gt;Mistral's board had no open jobs that day&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What they pay
&lt;/h2&gt;

&lt;p&gt;497 of the 691 jobs publish a yearly salary range in US dollars. Take the middle of each range, and half of them are above &lt;strong&gt;$355,500 a year&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where
&lt;/h2&gt;

&lt;p&gt;483 of the jobs (about 7 in 10) are in the United States. The rest are spread across 20 other countries.&lt;/p&gt;

&lt;h2&gt;
  
  
  Following hiring over time
&lt;/h2&gt;

&lt;p&gt;Give the run a watch name and schedule it. The first run saves every job as a starting point; after that, each run saves only the jobs that are new, and lists the ones that closed in a separate table.&lt;/p&gt;

&lt;p&gt;I tried it on 25 tech companies with 7,492 open jobs between them. Checked again five hours later (on a Saturday), the next run found 1 new job (a senior engineer at Cloudflare) and 1 that closed (a compliance role at Stripe). Small, but that is the point: a daily check returns only what changed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://apify.com/cleanscrape/career-site-jobs-scraper" rel="noopener noreferrer"&gt;https://apify.com/cleanscrape/career-site-jobs-scraper&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;$1 per 1,000 jobs plus $2 per 1,000 companies, less on paid Apify plans: the run above costs about $0.70. Export to Excel, CSV or Google Sheets, or call it from your own code or an AI agent through Apify's MCP server.&lt;/p&gt;

&lt;p&gt;Niko, CleanScrape developer&lt;/p&gt;

</description>
      <category>datascience</category>
      <category>webscraping</category>
      <category>career</category>
      <category>ai</category>
    </item>
    <item>
      <title>Checking casino affiliate pages for 18+ notices and terms, page by page</title>
      <dc:creator>CleanScrape</dc:creator>
      <pubDate>Mon, 19 Oct 2026 10:23:50 +0000</pubDate>
      <link>https://dev.to/cleanscrape/checking-casino-affiliate-pages-for-18-notices-and-terms-page-by-page-4hjk</link>
      <guid>https://dev.to/cleanscrape/checking-casino-affiliate-pages-for-18-notices-and-terms-page-by-page-4hjk</guid>
      <description>&lt;p&gt;Casino brands work with hundreds of affiliate and review sites, and each of those pages tells players something about the brand's bonus. Whether a page shows an age notice, links to safer-gambling help and puts the terms next to the offer is something compliance teams check by hand, page by page. I built a small tool on Apify that does the reading: it searches Google from the country you choose, reads every page through a connection in that country, and returns one row per page.&lt;/p&gt;

&lt;p&gt;No casino is named or shown in this video: brands, sites, offers, promo codes and links are blurred. It is about checking pages, not about any offer.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/e8HLxSPI0T8" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  What one run found
&lt;/h2&gt;

&lt;p&gt;The numbers are from one real run on Oct 18 2026 (United Kingdom, one brand): 10 pages on 8 sites, checked in under a minute and a half. Across those pages the brand's offer appeared in 9 different versions. All 5 affiliate pages showed an age notice and linked to safer-gambling help; 4 pages showed the offer with no terms next to it. Pages built with JavaScript are marked, so a notice they show later is not counted as missing. The rows report what each page shows and what is missing from it; they are not a legal verdict, since the rules differ by country and by who a page targets.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it reads a page
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The offer:&lt;/strong&gt; from the title, the main heading, offer cards and comparison tables. In a table that lists several casinos, an offer counts only when it sits in your brand's row.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Notices:&lt;/strong&gt; age notices (18+, 19+, 21+), safer-gambling links and helplines (BeGambleAware, GamCare, GAMSTOP, 1-800-GAMBLER, ConnexOntario and others), an ad or affiliate disclosure, and wording such as "risk-free".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Terms next to the offer:&lt;/strong&gt; T&amp;amp;Cs, wagering, expiry or minimum deposit in the offer's box, or within about 500 characters of the offer's numbers in the page text.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Country:&lt;/strong&gt; each page is read from the country being checked; in our tests one page in eight showed a different offer to visitors from another country.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pages it cannot read&lt;/strong&gt; (bot checks, files, JavaScript-only pages) are listed in the run report with the reason, and never charged.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The price is $8 per 1,000 saved pages; pages without an offer and pages that could not be read are free.&lt;/p&gt;

&lt;p&gt;Try it: &lt;a href="https://apify.com/cleanscrape/casino-bonus-checker" rel="noopener noreferrer"&gt;https://apify.com/cleanscrape/casino-bonus-checker&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Maintained by CleanScrape. Questions or a reproducible issue: &lt;a href="mailto:contact.cleanscrape@gmail.com"&gt;contact.cleanscrape@gmail.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;CleanScrape is independent of Apify and of every casino, affiliate site and network.&lt;/p&gt;

</description>
      <category>datascience</category>
      <category>webscraping</category>
      <category>marketing</category>
      <category>analytics</category>
    </item>
    <item>
      <title>Which Kick streamers your competitors sponsor: 26 energy-drink sponsorships from 10,000 live streams</title>
      <dc:creator>CleanScrape</dc:creator>
      <pubDate>Mon, 12 Oct 2026 17:15:31 +0000</pubDate>
      <link>https://dev.to/cleanscrape/which-kick-streamers-your-competitors-sponsor-26-energy-drink-sponsorships-from-10000-live-streams-3388</link>
      <guid>https://dev.to/cleanscrape/which-kick-streamers-your-competitors-sponsor-26-energy-drink-sponsorships-from-10000-live-streams-3388</guid>
      <description>&lt;p&gt;Kick streamers advertise their sponsors in plain sight: a chat command in the stream title, a brand link in the channel's link panel, sometimes a promo code. Nobody collects that across channels, so I built a small tool on Apify that does: it scans the live streams on Kick, reads each channel's titles, links and the last month of broadcasts, and returns one row per streamer and brand.&lt;/p&gt;

&lt;p&gt;Here is one real use: an energy-drink brand for gamers checking which streamers its competitors sponsor.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/_Ph2BLiKzeM" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  The input
&lt;/h2&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;"brands"&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="s2"&gt;"HOLY"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"G FUEL"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Red Bull"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DZIK"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"maxLiveStreams"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"minViewers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&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;The run looked through 10,000 live streams, picked the 26 streamers whose live title named one of the brands, and checked each channel in depth. It took about four minutes.&lt;/p&gt;

&lt;h2&gt;
  
  
  One row per streamer and brand
&lt;/h2&gt;

&lt;p&gt;Each sponsorship row says where the brand shows up (live title, past broadcast titles, channel links, bio), how many of the streamer's last 30 broadcasts named it, how many hours those were, and any promo or affiliate codes. One example: &lt;strong&gt;rybsonlol&lt;/strong&gt;, live with 20,518 viewers at the time, named HOLY in all 30 recent broadcasts, 276.6 hours.&lt;/p&gt;

&lt;h2&gt;
  
  
  The brand summary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;HOLY&lt;/strong&gt;: 16 streamers, 26,211 live viewers at the time, 357 broadcasts and 2,507 hours in the past month.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DZIK&lt;/strong&gt; and &lt;strong&gt;G FUEL&lt;/strong&gt;: 5 streamers each.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Red Bull&lt;/strong&gt;: none in this scan.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A sponsorship here means the channel points viewers to the brand. It is what the channel shows publicly, not a confirmed contract.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running it every day
&lt;/h2&gt;

&lt;p&gt;Give the run a watch name and schedule it. Each row then carries &lt;code&gt;isNew&lt;/code&gt;, so a new sponsorship (a streamer starting with a brand) stands out. Export any table to Excel or CSV.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://apify.com/cleanscrape/kick-sponsor-tracker" rel="noopener noreferrer"&gt;https://apify.com/cleanscrape/kick-sponsor-tracker&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;About 110 brands are recognised out of the box (drinks, gear, game stores, VPNs, skin sites, sportsbooks and more), and any brand you type is tracked the same way. $3 per 1,000 sponsorships, no start fee: this run cost about $0.18. No login, public data only.&lt;/p&gt;

&lt;p&gt;Niko, CleanScrape developer&lt;/p&gt;

</description>
      <category>datascience</category>
      <category>webscraping</category>
      <category>marketing</category>
      <category>analytics</category>
    </item>
    <item>
      <title>Who sold the most Pokémon cards on Whatnot in a day: two checks of the top 50 sellers</title>
      <dc:creator>CleanScrape</dc:creator>
      <pubDate>Mon, 12 Oct 2026 16:00:06 +0000</pubDate>
      <link>https://dev.to/cleanscrape/who-sold-the-most-pokemon-cards-on-whatnot-in-a-day-two-checks-of-the-top-50-sellers-2dh6</link>
      <guid>https://dev.to/cleanscrape/who-sold-the-most-pokemon-cards-on-whatnot-in-a-day-two-checks-of-the-top-50-sellers-2dh6</guid>
      <description>&lt;p&gt;Whatnot ranks its top sellers per category every week, but the leaderboard does not say how much each seller sold or who is growing. I built a small tool on Apify that saves the top sellers with their lifetime items sold, followers, rating and rank, and compares every run with the one before.&lt;/p&gt;

&lt;p&gt;Here is one real use: the top 50 Pokémon card sellers, checked twice, a day apart.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/KDPCNiUV4ao" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  The input
&lt;/h2&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;"categories"&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="s2"&gt;"Pokémon Cards"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"topSellers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"historyName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Daily check"&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;The history name ties runs together: each run is compared with the last run under the same name. The first run, on Oct 11 at 14:51 UTC, saved the 50 sellers as a starting point. The second, 24.5 hours later, saved them again in 15 seconds.&lt;/p&gt;

&lt;h2&gt;
  
  
  What changed in a day
&lt;/h2&gt;

&lt;p&gt;Each row of the second run has the change since the first: items sold since the last check, sales per day, new followers and the rank change.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The 48 sellers in the top 50 on both days sold &lt;strong&gt;41,380 items&lt;/strong&gt; between the checks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;outsidehits&lt;/strong&gt; sold 2,633 items, gained 1,447 followers and moved from #13 to #10.&lt;/li&gt;
&lt;li&gt;The biggest climber, &lt;strong&gt;pokeynpulls&lt;/strong&gt;, went from #23 to #16.&lt;/li&gt;
&lt;li&gt;Two sellers are new in the top 50.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Items sold come from Whatnot's own lifetime count on each profile, so "sold since the last check" is simply the difference between two checks. Whatnot can show the count a little late for sellers who are live, which is why a daily schedule works better than an hourly one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running it every day
&lt;/h2&gt;

&lt;p&gt;Save the input as a task and schedule it daily. Every run adds a day of sales, followers and rank for each seller, and the "Changes since last check" view shows only the columns that moved. Export any view to Excel or CSV.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://apify.com/cleanscrape/whatnot-seller-tracker" rel="noopener noreferrer"&gt;https://apify.com/cleanscrape/whatnot-seller-tracker&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;$3 per 1,000 sellers, no start fee: a daily check of 50 sellers costs about $0.15. No login, public data only.&lt;/p&gt;

&lt;p&gt;Niko, CleanScrape developer&lt;/p&gt;

</description>
      <category>datascience</category>
      <category>webscraping</category>
      <category>python</category>
      <category>pokemon</category>
    </item>
    <item>
      <title>What a PSA 10 Charizard costs on Whatnot, from 73 live listings</title>
      <dc:creator>CleanScrape</dc:creator>
      <pubDate>Sun, 11 Oct 2026 15:43:20 +0000</pubDate>
      <link>https://dev.to/cleanscrape/what-a-psa-10-charizard-costs-on-whatnot-from-73-live-listings-3dl2</link>
      <guid>https://dev.to/cleanscrape/what-a-psa-10-charizard-costs-on-whatnot-from-73-live-listings-3dl2</guid>
      <description>&lt;p&gt;Whatnot is the live shopping marketplace where a lot of trading cards, sneakers and collectibles change hands, often during live streams. It has no public API for listings, so I built a small tool on Apify that turns a Whatnot search into a table, with the seller's track record on every row.&lt;/p&gt;

&lt;p&gt;Here is one real use: what does a PSA 10 Charizard cost on Whatnot right now, and who is selling them?&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/LdirO9TUpPI" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  The search
&lt;/h2&gt;

&lt;p&gt;The input works like the filters on the website: a keyword, a category, and Whatnot's own card filters.&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;"searchQueries"&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="s2"&gt;"charizard"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"categories"&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="s2"&gt;"Pokémon Cards"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"gradedOnly"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"gradingServices"&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="s2"&gt;"PSA"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"grades"&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="s2"&gt;"10"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"buyFormat"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"buy_it_now"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sort"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"price_asc"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"maxResultsPerSearch"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;300&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;The run returned 73 listings in 16 seconds. Each row has the price, currency, quantity, category, the date it was listed and a link, plus the seller's items sold on Whatnot, followers, rating and number of reviews.&lt;/p&gt;

&lt;h2&gt;
  
  
  The prices
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Price&lt;/th&gt;
&lt;th&gt;Listings&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Under $100&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;$100 to $499&lt;/td&gt;
&lt;td&gt;36&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;$500 to $1,999&lt;/td&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;$2,000 and up&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The cheapest was $15 and the most expensive $490,000, a 1999 first edition. One more listing asked $999,999, a placeholder price rather than a real offer, so I left it out of the table. The middle listing of all 73 was $300 ($292.50 without the placeholder).&lt;/p&gt;

&lt;p&gt;Two things to keep in mind. These are asking prices: Whatnot shows sold prices only to logged-in users, and the tool never logs in. And "PSA 10" is a filter on how sellers tag their listings, so a few titles mention other grades. Read the titles before you compare.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who is selling
&lt;/h2&gt;

&lt;p&gt;The seller columns are what make this more than a price list. The cheapest PSA 10 Charizard from a seller rated 5.0 with over a thousand sales was $100, from a seller with 11,846 items sold. In Python, with the Apify client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;statistics&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;median&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;apify_client&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;YOUR_TOKEN&lt;/span&gt;&lt;span class="sh"&gt;"&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cleanscrape/whatnot-scraper&lt;/span&gt;&lt;span class="sh"&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;searchQueries&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;charizard&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;categories&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Pokémon Cards&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gradedOnly&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gradingServices&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PSA&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;grades&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;10&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;buyFormat&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;buy_it_now&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sort&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;price_asc&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;maxResultsPerSearch&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="n"&gt;rows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&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;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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;defaultDatasetId&lt;/span&gt;&lt;span class="sh"&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="n"&gt;real&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;price&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;999999&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;listings, median&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;median&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;price&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;real&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;trusted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;real&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sellerRating&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sellerSoldCount&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;trusted&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="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;price&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sellerUsername&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sellerSoldCount&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Live shows
&lt;/h2&gt;

&lt;p&gt;Switch "What to find" to live and upcoming shows and the same category gives you the streams. The top 100 live Pokémon card shows had 6,041 viewers between them when I ran it, the biggest over a thousand. Each show row has the title, viewers, start time, format (52 Singles, 29 Surprise Sets and 19 Breaks in this run), tags and seller, and you can ask for the items for sale in each show too.&lt;/p&gt;

&lt;h2&gt;
  
  
  Only new listings
&lt;/h2&gt;

&lt;p&gt;For deal alerts, sort by "Newly listed", turn on "Only listings or shows not saved before" and schedule the task. Each run looks at the newest listings and returns the ones earlier runs did not save, plus price drops on ones they did. The skipped ones are not charged.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://apify.com/cleanscrape/whatnot-scraper" rel="noopener noreferrer"&gt;Whatnot Scraper on Apify&lt;/a&gt;. You pay per listing saved, and the search above cost about $0.15. It works on all eight Whatnot marketplaces: the US, Canada, the UK, Germany, France, Belgium, the Netherlands and Australia.&lt;/p&gt;

</description>
      <category>datascience</category>
      <category>webscraping</category>
      <category>python</category>
      <category>pokemon</category>
    </item>
    <item>
      <title>What a data analyst earns in New York, Chicago and Austin, from 150 Google Jobs listings</title>
      <dc:creator>CleanScrape</dc:creator>
      <pubDate>Wed, 07 Oct 2026 18:31:59 +0000</pubDate>
      <link>https://dev.to/cleanscrape/what-a-data-analyst-earns-in-new-york-chicago-and-austin-from-150-google-jobs-listings-48ke</link>
      <guid>https://dev.to/cleanscrape/what-a-data-analyst-earns-in-new-york-chicago-and-austin-from-150-google-jobs-listings-48ke</guid>
      <description>&lt;p&gt;Google Jobs is the job search built into Google. It pulls listings from LinkedIn, Indeed, Glassdoor, company career sites and local job boards into one place, which makes it a good single source when you want to compare jobs across cities. It has no public API, so I built a small tool on Apify that turns a Google Jobs search into a table.&lt;/p&gt;

&lt;p&gt;Here is one real use: I wanted to see what data analyst jobs pay in New York, Chicago and Austin.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/mh-6QyoREqw" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  The search
&lt;/h2&gt;

&lt;p&gt;The input is a job title and a list of locations. Every title is searched in every location:&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;"queries"&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="s2"&gt;"data analyst"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"locations"&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="s2"&gt;"New York, NY"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Chicago, IL"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Austin, TX"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"maxJobsPerSearch"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;50&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;The run returned 150 jobs in 43 seconds. Most came via LinkedIn (34), Indeed (16), Dice (8) and Monster (8); the rest from company career sites and smaller boards.&lt;/p&gt;

&lt;h2&gt;
  
  
  Salaries as numbers
&lt;/h2&gt;

&lt;p&gt;Google shows pay as text, such as "$124K–$177K a year" or "$57.70–$62.50 an hour". The tool splits that into &lt;code&gt;salaryMin&lt;/code&gt;, &lt;code&gt;salaryMax&lt;/code&gt;, &lt;code&gt;salaryCurrency&lt;/code&gt; and &lt;code&gt;salaryPeriod&lt;/code&gt;, so you can sort and filter without parsing anything yourself.&lt;/p&gt;

&lt;p&gt;64 of the 150 jobs listed a salary. Taking the midpoint of each yearly range and the median per city:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;City&lt;/th&gt;
&lt;th&gt;Jobs with a yearly salary&lt;/th&gt;
&lt;th&gt;Median of range midpoints&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;New York&lt;/td&gt;
&lt;td&gt;21&lt;/td&gt;
&lt;td&gt;$106,734&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Chicago&lt;/td&gt;
&lt;td&gt;19&lt;/td&gt;
&lt;td&gt;$97,500&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Austin&lt;/td&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;td&gt;$110,000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These are small samples from one search on October 6, 2026, not a salary survey. Austin coming out on top surprised me; with 13 jobs it can easily move by the next run.&lt;/p&gt;

&lt;p&gt;In Python, with the Apify client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;statistics&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;median&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;apify_client&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;YOUR_TOKEN&lt;/span&gt;&lt;span class="sh"&gt;"&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cleanscrape/google-jobs-scraper&lt;/span&gt;&lt;span class="sh"&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;queries&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data analyst&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;locations&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;New York, NY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Chicago, IL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Austin, TX&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;maxJobsPerSearch&lt;/span&gt;&lt;span class="sh"&gt;"&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="p"&gt;})&lt;/span&gt;
&lt;span class="n"&gt;jobs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&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;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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;defaultDatasetId&lt;/span&gt;&lt;span class="sh"&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="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;city&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;New York, NY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Chicago, IL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Austin, TX&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="n"&gt;mids&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;salaryMin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;salaryMax&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;jobs&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;searchLocation&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;city&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;salaryPeriod&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;year&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mids&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;median&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mids&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;mids&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  More than 10 jobs per search
&lt;/h2&gt;

&lt;p&gt;Google Jobs shows 10 jobs per search. To get more, the tool runs close variations of the same search (posted today, this week, full time, remote, senior and so on) and keeps each job once. A search stops when the variations stop finding new jobs, so a narrow search ends early instead of paying for repeats.&lt;/p&gt;

&lt;h2&gt;
  
  
  Only new jobs
&lt;/h2&gt;

&lt;p&gt;For job alerts, turn on "Only jobs not saved before" and schedule the task. Each run returns only jobs earlier runs did not save, and the skipped ones are not charged.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://apify.com/cleanscrape/google-jobs-scraper" rel="noopener noreferrer"&gt;Google Jobs Scraper on Apify&lt;/a&gt;. You pay per job saved: $2.95 per 1,000 jobs since October 21, 2026, less on paid Apify plans. At that price the 150-job run above costs about $0.44. It works in 36 countries; outside English-speaking ones, type the job title in the local language.&lt;/p&gt;

</description>
      <category>datascience</category>
      <category>jobs</category>
      <category>webscraping</category>
      <category>python</category>
    </item>
    <item>
      <title>What Chinese viewers say about coffee, second by second</title>
      <dc:creator>CleanScrape</dc:creator>
      <pubDate>Tue, 06 Oct 2026 05:50:02 +0000</pubDate>
      <link>https://dev.to/cleanscrape/what-chinese-viewers-say-about-coffee-second-by-second-1gb8</link>
      <guid>https://dev.to/cleanscrape/what-chinese-viewers-say-about-coffee-second-by-second-1gb8</guid>
      <description>&lt;p&gt;If you want to know what Chinese viewers watch and say about a topic, Bilibili is one of the best places to look. It is one of China's biggest video sites, and its viewers comment in two ways: ordinary comments under the video, and danmaku, the comments that fly across the video at the moment they were written.&lt;/p&gt;

&lt;p&gt;I built &lt;a href="https://apify.com/cleanscrape/bilibili-scraper" rel="noopener noreferrer"&gt;CleanScrape's Bilibili Scraper&lt;/a&gt; to get both into a spreadsheet without a Bilibili account. I'm the developer, so this is a walkthrough of my own tool, not an independent review. Every number in it was checked against my runs.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/VmSRpL4ruC8" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  One search term, 100 videos
&lt;/h2&gt;

&lt;p&gt;For the example I searched for 咖啡 (coffee) and turned on top comments and danmaku. You need an Apify account to run the Actor, but no Bilibili login or cookies. The same input as JSON:&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;"searchQueries"&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="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;"maxVideosPerQuery"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"includeComments"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"includeDanmaku"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"maxDanmakuPerVideo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;500&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;The run took 81 seconds and saved 100 videos with views, likes, coins, favorites, shares and tags, plus 1,357 top comments and replies and 12,055 danmaku. Videos, comments, danmaku and creators each land in their own table, so an export holds one kind of row.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it doesn't get blocked
&lt;/h2&gt;

&lt;p&gt;Bilibili limits how fast one visitor can read each kind of data, and it blocks an IP for minutes to an hour when that limit is crossed. I measured those limits field by field before building the Actor. It spreads the work over several proxy connections at once (nine in this run), keeps each one under the measured pace, and moves work elsewhere when Bilibili refuses a request. In this run, 5 of 604 requests were refused, all for danmaku, and every one was retried on another connection. No video is missing its danmaku.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the danmaku show
&lt;/h2&gt;

&lt;p&gt;The most-watched video in the results, about drinking six cups of coffee a day, has 5,108,457 views and 63,604 coins. A second run collected the 1,800 danmaku Bilibili's player loads for it (10,343 were sent in total), each with its time in the video.&lt;/p&gt;

&lt;p&gt;Count them in 20-second steps and the busiest stretch after the opening is 0:40 to 1:00, with 136 danmaku. 21 of those are about bitterness, and the running joke is that life is more bitter than coffee:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;再苦能有生活苦: "Can it be more bitter than life?"&lt;/li&gt;
&lt;li&gt;咖啡没有我的命苦: "Coffee isn't as bitter as my life"&lt;/li&gt;
&lt;li&gt;因为生活比咖啡还苦: "Because life is even more bitter than coffee"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That kind of moment-by-moment reaction is hard to get any other way. Comments tell you what people thought after watching; danmaku tell you what they felt at a given second.&lt;/p&gt;

&lt;h2&gt;
  
  
  Top comments
&lt;/h2&gt;

&lt;p&gt;Without login Bilibili shows the top comments of a video (usually 3) and the first 20 replies under each, so this is a sample, not every comment. On the coffee video, the pinned top comment asks whether anyone has managed to quit coffee. It has 5,941 likes and 714 replies.&lt;/p&gt;

&lt;h2&gt;
  
  
  Export and cost
&lt;/h2&gt;

&lt;p&gt;Open &lt;strong&gt;Storage &amp;gt; Dataset&lt;/strong&gt;, choose Default (videos), Comments, Danmaku or Creators, and download CSV, Excel or JSON. From the API, the run object lists the four datasets under &lt;code&gt;storageIds.datasets&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;At base prices a video costs $1.49 per 1,000, a comment $0.49 per 1,000 and a danmaku $0.10 per 1,000, with no start fee and platform usage included. 100 videos with tags cost about $0.15. There are also ready-made examples for &lt;a href="https://apify.com/cleanscrape/bilibili-scraper/examples/bilibili-videos-for-a-keyword" rel="noopener noreferrer"&gt;a keyword&lt;/a&gt;, &lt;a href="https://apify.com/cleanscrape/bilibili-scraper/examples/bilibili-video-danmaku-export" rel="noopener noreferrer"&gt;one video's danmaku&lt;/a&gt; and &lt;a href="https://apify.com/cleanscrape/bilibili-scraper/examples/bilibili-creator-profile-and-videos" rel="noopener noreferrer"&gt;a creator's profile and videos&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Besides search terms, you can paste video links, BV ids, creator links or UIDs, or the whole text the Bilibili app copies with Share &amp;gt; Copy link.&lt;/p&gt;

&lt;p&gt;What it can't do without login: every comment of a video, a creator's full upload history (it uses their public collections and series), subtitles, and bilibili.tv, the international site.&lt;/p&gt;

&lt;p&gt;Questions or a reproducible problem: use the Actor's Issues tab or email &lt;a href="mailto:contact.cleanscrape@gmail.com"&gt;contact.cleanscrape@gmail.com&lt;/a&gt;. CleanScrape is independent of Bilibili and Apify.&lt;/p&gt;

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