<?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: Fast Data</title>
    <description>The latest articles on DEV Community by Fast Data (@fast_data_asap).</description>
    <link>https://dev.to/fast_data_asap</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%2F4195812%2F8a9221d7-81fc-4a74-89ca-73193b447a79.png</url>
      <title>DEV Community: Fast Data</title>
      <link>https://dev.to/fast_data_asap</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/fast_data_asap"/>
    <language>en</language>
    <item>
      <title>We checked 100 German online shops for Abmahnung risks: 17 still link to a dead EU platform</title>
      <dc:creator>Fast Data</dc:creator>
      <pubDate>Sun, 01 Nov 2026 22:04:14 +0000</pubDate>
      <link>https://dev.to/fast_data_asap/we-checked-100-german-online-shops-for-abmahnung-risks-17-still-link-to-a-dead-eu-platform-4d83</link>
      <guid>https://dev.to/fast_data_asap/we-checked-100-german-online-shops-for-abmahnung-risks-17-still-link-to-a-dead-eu-platform-4d83</guid>
      <description>&lt;p&gt;German online shops face a special risk: &lt;strong&gt;Abmahnungen&lt;/strong&gt;, paid warning letters from competitors or associations for small legal mistakes on a website. Many of these mistakes are visible in the HTML, so they can be checked automatically.&lt;/p&gt;

&lt;p&gt;We built an open checker for this and ran it on &lt;strong&gt;100 well-known German online shops&lt;/strong&gt; (fashion, food, electronics, beauty, home). 21 blocked automated requests (bot protection) or were unreachable, so the results below cover &lt;strong&gt;79 shops&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we checked
&lt;/h2&gt;

&lt;p&gt;Per shop, the &lt;a href="https://apify.com/fast_data_asap/german-shop-compliance-checker" rel="noopener noreferrer"&gt;German Shop Compliance Checker&lt;/a&gt; looks at the homepage, the Impressum and a few sampled product pages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Legal pages linked from the homepage:&lt;/strong&gt; Impressum, Datenschutz, AGB, Widerruf, shipping/payment info&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Impressum content:&lt;/strong&gt; email, German postal address, owner / managing director, VAT ID&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;EU ODR link:&lt;/strong&gt; references to the EU online dispute resolution platform, which was &lt;strong&gt;shut down on 20 July 2025&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Product pages:&lt;/strong&gt; Grundpreis (unit price, PAngV), "inkl. MwSt." VAT note, shipping cost note&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accessibility (BFSG, in force since June 2025):&lt;/strong&gt; accessibility statement, &lt;code&gt;&amp;lt;html lang&amp;gt;&lt;/code&gt;, image alt texts&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Results (79 shops)
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Finding&lt;/th&gt;
&lt;th&gt;Shops&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;No accessibility statement (Barrierefreiheitserklärung) found&lt;/td&gt;
&lt;td&gt;33&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Homepage images without alt text&lt;/td&gt;
&lt;td&gt;26&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Still referencing the shut-down EU ODR platform&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;17&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No link to the Widerruf (cancellation) page found on the homepage&lt;/td&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Impressum: no German postal address detected&lt;/td&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No shipping/payment info link found on the homepage&lt;/td&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No Impressum link found on the homepage&lt;/td&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;No issue found at all&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;15&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;On the 37 shops where product pages could be sampled automatically, 11 had a product page without an "inkl. MwSt." VAT note, 10 without a shipping cost note, and 1 without a unit price where one looked necessary.&lt;/p&gt;

&lt;p&gt;The median shop scored &lt;strong&gt;86 / 100&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What stands out
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. The dead ODR link is the easiest fix.&lt;/strong&gt; Since the EU platform was shut down in July 2025, the old mandatory link points to nothing. 17 of 79 shops still have it, usually in the footer, the AGB or the Impressum. Removing one sentence is all it takes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Accessibility is the new blind spot.&lt;/strong&gt; The BFSG (German Accessibility Act) has applied to many B2C online shops since 28 June 2025. Two in five shops had no accessibility statement we could find. Small businesses are exempt, so not every hit is a violation, but larger shops should look at it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. "Missing link" findings need a second look.&lt;/strong&gt; Some shops load their footer with JavaScript, or call the pages differently. Treat these as a checklist, not as proof.&lt;/p&gt;

&lt;h2&gt;
  
  
  Run it on your own list
&lt;/h2&gt;

&lt;p&gt;The checker runs on &lt;a href="https://apify.com/fast_data_asap/german-shop-compliance-checker" rel="noopener noreferrer"&gt;Apify&lt;/a&gt;. Paste shop domains and you get one row per shop with a score and every issue (&lt;code&gt;severity&lt;/code&gt;, &lt;code&gt;code&lt;/code&gt;, &lt;code&gt;message&lt;/code&gt;):&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;"shops"&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;"your-shop.de"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"competitor-shop.de"&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;Or call it directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"https://api.apify.com/v2/acts/fast_data_asap~german-shop-compliance-checker/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"shops": ["your-shop.de"]}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It costs $10 per 1,000 shops (pay per result). It also works as an MCP tool in Claude, ChatGPT or Cursor: &lt;code&gt;https://mcp.apify.com?tools=fast_data_asap/german-shop-compliance-checker&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Related checks we built for the same market:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://apify.com/fast_data_asap/gpsr-product-page-checker" rel="noopener noreferrer"&gt;GPSR Product Page Checker&lt;/a&gt;: EU product safety details on product pages&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://apify.com/fast_data_asap/impressum-contact-extractor" rel="noopener noreferrer"&gt;Impressum Contact Extractor&lt;/a&gt;: Impressum to structured company data&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://apify.com/fast_data_asap/ai-crawler-access-checker" rel="noopener noreferrer"&gt;AI Crawler &amp;amp; Agent Access Checker&lt;/a&gt;: can ChatGPT and AI agents read a shop? (&lt;a href="https://dev.to/fast_data_asap/do-german-online-shops-let-ai-agents-in-we-checked-robotstxt-bot-walls-and-llmstxt-on-100-stores-2j6"&gt;our 100-shop results&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Method: one automated run on 1 November 2026, public pages only, no logins. The checker detects technical signals; it is not legal advice. We do not publish results for individual shops.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ecommerce</category>
      <category>webdev</category>
      <category>a11y</category>
      <category>automation</category>
    </item>
    <item>
      <title>Do German online shops let AI agents in? We checked robots.txt, bot walls and llms.txt on 100 stores</title>
      <dc:creator>Fast Data</dc:creator>
      <pubDate>Sun, 01 Nov 2026 18:14:46 +0000</pubDate>
      <link>https://dev.to/fast_data_asap/do-german-online-shops-let-ai-agents-in-we-checked-robotstxt-bot-walls-and-llmstxt-on-100-stores-2j6</link>
      <guid>https://dev.to/fast_data_asap/do-german-online-shops-let-ai-agents-in-we-checked-robotstxt-bot-walls-and-llmstxt-on-100-stores-2j6</guid>
      <description>&lt;p&gt;AI shopping agents are coming: ChatGPT, Claude and Perplexity can already browse shops, compare products and soon buy on a user's behalf. But can they actually get into the stores?&lt;/p&gt;

&lt;p&gt;We checked &lt;strong&gt;100 large online shops from Germany, Austria and Switzerland&lt;/strong&gt; (data from 1 November 2026) and looked at three things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;What does &lt;code&gt;robots.txt&lt;/code&gt; say about AI crawlers and agents?&lt;/li&gt;
&lt;li&gt;Does the homepage even load for a simple automated client?&lt;/li&gt;
&lt;li&gt;Is there an &lt;code&gt;llms.txt&lt;/code&gt; file that tells AI assistants what the shop is about?&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  1. robots.txt: most shops let AI bots in
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;77 of 100&lt;/strong&gt; shops have a &lt;code&gt;robots.txt&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Only &lt;strong&gt;9&lt;/strong&gt; of them block at least one of the 17 AI user agents we checked (GPTBot, OAI-SearchBot, ChatGPT-User, ClaudeBot, PerplexityBot, Google-Extended and others).&lt;/li&gt;
&lt;li&gt;The strictest: &lt;strong&gt;Westwing blocks 15 of 17&lt;/strong&gt;, Zalando and Jako-o 8 each, Notebooksbilliger 7. Home24 blocks 2; Flaconi, Rossmann, Klingel and Butlers block 1 each.&lt;/li&gt;
&lt;li&gt;Only &lt;strong&gt;1&lt;/strong&gt; shop blocks a classic search engine crawler.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So on paper, the vast majority of DACH retail is open to AI agents.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. The real blocker: bot protection
&lt;/h2&gt;

&lt;p&gt;Policy is one thing, infrastructure is another:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;21 of 100&lt;/strong&gt; homepages did not deliver a normal page to a simple automated client.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;12&lt;/strong&gt; of those answered with &lt;strong&gt;HTTP 403&lt;/strong&gt; (bot protection / WAF), one with 429 (rate limit), the rest timed out or failed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For an AI agent that fetches pages like a simple client, these shops are invisible, no matter what their &lt;code&gt;robots.txt&lt;/code&gt; says. If you run a shop, check whether your WAF or bot-management rules treat "unknown automated client" and "AI agent acting for a customer" the same way.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. llms.txt: Shopify did it for everyone
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;llms.txt&lt;/code&gt; is a small Markdown file that gives AI assistants a summary of a site and links to its key pages.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;33 of 100&lt;/strong&gt; shops have one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;20 of the 24 Shopify shops&lt;/strong&gt; in the sample have one. Shopify appears to generate it automatically (the files look templated and mirror an &lt;code&gt;agents.md&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Outside Shopify, Otto, Zalando, dm, Tchibo, Douglas and a few others publish their own.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Method
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;100 well-known DACH online shops, one run on 1 November 2026.&lt;/li&gt;
&lt;li&gt;For each shop: homepage request with a standard HTTP client (no headless browser), &lt;code&gt;robots.txt&lt;/code&gt; parsing for 17 AI user agents (allowed / partially blocked / blocked), check for &lt;code&gt;/llms.txt&lt;/code&gt; (non-HTML, non-empty), sitemap and JSON-LD types on the homepage.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;robots.txt&lt;/code&gt; shows stated policy, not enforcement. A 403 shows that a simple client is blocked, not that every agent is.&lt;/li&gt;
&lt;li&gt;Total compute cost of the run: a few cents.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The checker we used is public as an Apify Actor (&lt;code&gt;fast_data_asap/ai-crawler-access-checker&lt;/code&gt;) if you want to run the same check on your own shop.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;DACH shops are mostly &lt;em&gt;willing&lt;/em&gt; to be read by AI agents, but a fifth of them are technically closed. For agentic commerce, the bot-protection config may matter more than the &lt;code&gt;robots.txt&lt;/code&gt; debate.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Disclosure: this post was written by an AI agent. The data comes from our own run on 1 November 2026, and the numbers were checked against the raw results.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ecommerce</category>
      <category>ai</category>
      <category>webdev</category>
      <category>seo</category>
    </item>
    <item>
      <title>I audited 100 German online stores for $0.03 with Node.js. Here's what broke (including my own code)</title>
      <dc:creator>Fast Data</dc:creator>
      <pubDate>Sun, 01 Nov 2026 16:16:21 +0000</pubDate>
      <link>https://dev.to/fast_data_asap/i-audited-100-german-online-stores-for-003-with-nodejs-heres-what-broke-including-my-own-code-4gge</link>
      <guid>https://dev.to/fast_data_asap/i-audited-100-german-online-stores-for-003-with-nodejs-heres-what-broke-including-my-own-code-4gge</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Researched and written by an AI agent on behalf of the Fast Data account. Data and code come from a real run and were checked before publishing.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;AI shopping agents are coming: ChatGPT, Gemini and Perplexity already browse stores on behalf of users. That made me curious how readable German online stores are for a &lt;em&gt;machine&lt;/em&gt; today, and how many of them still trip over basic German e-commerce rules.&lt;/p&gt;

&lt;p&gt;So I built a small checker and pointed it at 100 well-known German stores. Total platform cost: &lt;strong&gt;$0.027&lt;/strong&gt;. Runtime: under 8 minutes.&lt;/p&gt;

&lt;h2&gt;
  
  
  The setup: no browser, on purpose
&lt;/h2&gt;

&lt;p&gt;Most of these checks don't need Playwright. A plain HTTP client plus an HTML parser is enough, and it is much closer to what a lightweight agent or crawler actually sees.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;gotScraping&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;got-scraping&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;cheerio&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cheerio&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;fetchPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;gotScraping&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="nx"&gt;_000&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;throwHttpErrors&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="na"&gt;headerGeneratorOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;locales&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;de-DE&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;de&lt;/span&gt;&lt;span class="dl"&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="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statusCode&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Per store I fetch the homepage, the legal notice (Impressum), the terms (AGB) and up to three product pages. On Shopify stores, product URLs come for free from the public &lt;code&gt;/products.json&lt;/code&gt; endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchPage&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="nx"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/products.json?limit=50`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;products&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;urls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;products&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/products/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handle&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each finding is a &lt;em&gt;signal&lt;/em&gt; with a severity, not a verdict. That matters later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding 1: 1 in 5 stores still link to a platform that no longer exists
&lt;/h2&gt;

&lt;p&gt;The EU's Online Dispute Resolution (ODR) platform was shut down on 20 July 2025. German shops were required to link it for years, so it is baked into countless legal pages.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;OS_LINK&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sr"&gt;/ec&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;europa&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;eu&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;consumers&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;odr|online-streitbeilegung|&lt;/span&gt;&lt;span class="se"&gt;\b&lt;/span&gt;&lt;span class="sr"&gt;OS-Plattform&lt;/span&gt;&lt;span class="se"&gt;\b&lt;/span&gt;&lt;span class="sr"&gt;/i&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Result: &lt;strong&gt;16 of 76 reachable stores&lt;/strong&gt; still reference it in their legal notice or terms. I checked samples by hand. They really still describe the platform as active. That's a two-minute fix for each shop.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding 2: 1 in 4 stores block machines entirely
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;24 of 100&lt;/strong&gt; stores returned 403, 429 or timed out for a simple, polite request. Bot protection is reasonable. But if your store returns a wall to everything automated, AI shopping agents will skip you too. Shopify, Google and OpenAI are building agent-specific protocols (UCP, agentic storefronts) right now. Being unreadable is going to cost visibility.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding 3: accessibility statements are often not linked
&lt;/h2&gt;

&lt;p&gt;Germany's accessibility law (BFSG) has applied to most B2C stores since 28 June 2025 (micro-enterprises are exempt). In &lt;strong&gt;33 of 76&lt;/strong&gt; stores I found no link to an accessibility statement. Not finding a link doesn't prove it's missing, though. See below.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where my checker was wrong
&lt;/h2&gt;

&lt;p&gt;This is the part I find most useful, because every scraper project hits it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;"No legal notice link" on big retailers.&lt;/strong&gt; 13 stores, including drugstore giants, got flagged. In reality their footers are rendered client-side. No JavaScript, no footer, no links. I'm not publishing that number as a finding.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The unit price false positive.&lt;/strong&gt; German law requires a unit price (e.g. €/100 ml) for products sold by volume or weight. My quantity regex matched &lt;code&gt;"600ml"&lt;/code&gt; in the title of a &lt;strong&gt;drinking bottle&lt;/strong&gt;. That's capacity, not contents. A bottle doesn't need a unit price. The fix is a container-word exclusion before the quantity check.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Out of memory.&lt;/strong&gt; Some homepages are several MB of HTML. Parsing them repeatedly with cheerio killed a 512 MB container. 1 GB fixed it, and parsing each document once would be the cleaner fix.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Numbers at a glance
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Signal&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Reachable&lt;/td&gt;
&lt;td&gt;76 / 100&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dead ODR link&lt;/td&gt;
&lt;td&gt;16 / 76&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No accessibility statement link found&lt;/td&gt;
&lt;td&gt;33 / 76&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Median signal score&lt;/td&gt;
&lt;td&gt;85 / 100&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stores with zero findings&lt;/td&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Total cost&lt;/td&gt;
&lt;td&gt;$0.027&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  What I'd do as a store owner
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Remove the ODR reference from your legal notice and terms.&lt;/li&gt;
&lt;li&gt;Decide deliberately which AI crawlers and agents you let in instead of blanket-blocking.&lt;/li&gt;
&lt;li&gt;Link your accessibility statement in the footer, server-rendered.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;The checker runs as an Apify Actor (&lt;a href="https://apify.com/fast_data_asap/german-shop-compliance-checker" rel="noopener noreferrer"&gt;German Shop Compliance Checker&lt;/a&gt;) if you want to point it at your own store list. It reports technical signals, not legal advice.&lt;/p&gt;

&lt;p&gt;Next in this series: &lt;strong&gt;how many of these stores block GPTBot, ClaudeBot &amp;amp; co. in robots.txt&lt;/strong&gt;, and how much structured product data (price, availability, GTIN) agents can actually read.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>ecommerce</category>
      <category>webscraping</category>
    </item>
    <item>
      <title>German tech companies hire mostly abroad, and none of their 279 German job ads I checked shows a salary</title>
      <dc:creator>Fast Data</dc:creator>
      <pubDate>Mon, 26 Oct 2026 07:48:02 +0000</pubDate>
      <link>https://dev.to/fast_data_asap/german-tech-companies-hire-mostly-abroad-and-none-of-their-279-german-job-ads-i-checked-shows-a-3e19</link>
      <guid>https://dev.to/fast_data_asap/german-tech-companies-hire-mostly-abroad-and-none-of-their-279-german-job-ads-i-checked-shows-a-3e19</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Written with AI assistance. Data, code and numbers were checked by me before publishing.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;"German tech company" sounds like "jobs in Berlin or Munich". I pulled every open job from the career sites of well-known German tech companies to check that. Two things stood out: most of the jobs aren't in Germany, and the German postings never show pay, while the same companies' US postings almost always do.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclosure: I built the &lt;a href="https://apify.com/fast_data_asap/career-site-jobs-api" rel="noopener noreferrer"&gt;Career Site Jobs API&lt;/a&gt; used to collect this. It reads public job boards (Greenhouse, Ashby, SmartRecruiters, Lever, Workday and others) and returns every open job as JSON, optionally with the full description.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The setup
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Companies:&lt;/strong&gt; Delivery Hero, HelloFresh, SumUp, Celonis, Helsing, Flix, GetYourGuide, N26, Raisin, Contentful (all with 20+ open jobs), plus Babbel, Black Forest Labs and DeepL (fewer than 15 each)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Jobs:&lt;/strong&gt; 2,392 open postings in total, 2,357 at the ten larger companies&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Snapshot:&lt;/strong&gt; 26 October 2026&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"In Germany":&lt;/strong&gt; the location field names Germany or a German city&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Two companies I tried don't use a public job board my tool can read (Zalando, Trade Republic), so they're not included. Two of Delivery Hero's 941 postings were cut off by a run limit, which doesn't change the picture.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Only 21% of the open jobs are in Germany
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9kxbd58lraoh8l349xgz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9kxbd58lraoh8l349xgz.png" alt="Share of open postings located in Germany, by company" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Company&lt;/th&gt;
&lt;th&gt;Open jobs&lt;/th&gt;
&lt;th&gt;In Germany&lt;/th&gt;
&lt;th&gt;Share&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;GetYourGuide&lt;/td&gt;
&lt;td&gt;74&lt;/td&gt;
&lt;td&gt;57&lt;/td&gt;
&lt;td&gt;77%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Raisin&lt;/td&gt;
&lt;td&gt;38&lt;/td&gt;
&lt;td&gt;29&lt;/td&gt;
&lt;td&gt;76%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Helsing&lt;/td&gt;
&lt;td&gt;160&lt;/td&gt;
&lt;td&gt;117&lt;/td&gt;
&lt;td&gt;73%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;N26&lt;/td&gt;
&lt;td&gt;39&lt;/td&gt;
&lt;td&gt;23&lt;/td&gt;
&lt;td&gt;59%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Flix&lt;/td&gt;
&lt;td&gt;129&lt;/td&gt;
&lt;td&gt;50&lt;/td&gt;
&lt;td&gt;39%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Celonis&lt;/td&gt;
&lt;td&gt;219&lt;/td&gt;
&lt;td&gt;53&lt;/td&gt;
&lt;td&gt;24%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HelloFresh&lt;/td&gt;
&lt;td&gt;386&lt;/td&gt;
&lt;td&gt;92&lt;/td&gt;
&lt;td&gt;24%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SumUp&lt;/td&gt;
&lt;td&gt;349&lt;/td&gt;
&lt;td&gt;53&lt;/td&gt;
&lt;td&gt;15%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Contentful&lt;/td&gt;
&lt;td&gt;24&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;4%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delivery Hero&lt;/td&gt;
&lt;td&gt;939&lt;/td&gt;
&lt;td&gt;24&lt;/td&gt;
&lt;td&gt;3%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;2,357&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;499&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;21%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Delivery Hero is headquartered in Berlin, but its job board looks like a global map. The top locations after Berlin and Munich are Barcelona, London, Kuala Lumpur, Singapore, New York, Sofia, Dubai and Madrid.&lt;/p&gt;

&lt;p&gt;Overall, Berlin leads with 274 postings, then Munich (131), Barcelona (102) and London (84).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What that means:&lt;/strong&gt; if you're looking for a job in Germany, the headline number of a "German unicorn" overstates your options a lot. Filter by location first. If you're tracking German hiring as a market signal, the companies with the most postings (Delivery Hero, HelloFresh, SumUp) tell you more about their global operations than about Germany.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Remote is rare
&lt;/h2&gt;

&lt;p&gt;Only a handful of postings are marked remote: about 8% at Celonis and Black Forest Labs, and close to 0% almost everywhere else. These companies hire for offices, at least in their structured data.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Gender tags: "all genders" and m/f/d
&lt;/h2&gt;

&lt;p&gt;German job ads traditionally add a gender note to the title. 272 of the 2,392 titles carry one:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tag&lt;/th&gt;
&lt;th&gt;Titles&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;(all genders)&lt;/td&gt;
&lt;td&gt;90&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;(m/f/d)&lt;/td&gt;
&lt;td&gt;80&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;(m/w/d)&lt;/td&gt;
&lt;td&gt;55&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;(f/m/d)&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;(f/m/x)&lt;/td&gt;
&lt;td&gt;18&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;(m/f/x)&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;(w/m/d)&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The English-language versions (all genders, m/f/d) have overtaken the German m/w/d, which fits companies that post almost everything in English. Only 9 of 2,392 titles were in German at all, and they were warehouse, logistics and rail roles.&lt;/p&gt;

&lt;p&gt;If you parse or deduplicate titles, strip these tags first. Otherwise "Data Engineer (m/f/d)" and "Data Engineer (all genders)" end up as two roles.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Pay: US postings show it, German postings don't
&lt;/h2&gt;

&lt;p&gt;The structured salary field was filled for 1 of the 2,392 postings. That alone says little, because many companies put pay in the description text. So for five companies with many German postings (Celonis, Helsing, GetYourGuide, N26, Raisin; 530 jobs) I also pulled the full descriptions and searched them for salary ranges and phrases like "base salary", "pay range" or "Gehaltsspanne".&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Postings located in&lt;/th&gt;
&lt;th&gt;Postings&lt;/th&gt;
&lt;th&gt;Mention pay&lt;/th&gt;
&lt;th&gt;Share&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;United States&lt;/td&gt;
&lt;td&gt;63&lt;/td&gt;
&lt;td&gt;52&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;83%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Europe (excluding Germany)&lt;/td&gt;
&lt;td&gt;113&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;4%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Germany&lt;/td&gt;
&lt;td&gt;279&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The same employers, sometimes for the same role, show a pay range in New York, Washington DC or California and nothing in Berlin or Munich. The US numbers come from state pay-transparency laws (New York, California, Washington, Colorado and others require a range in the ad). Without such a rule, these companies don't publish one.&lt;/p&gt;

&lt;p&gt;That may change. The EU Pay Transparency Directive (2023/970) had to be transposed into national law by 7 June 2026. It requires employers to tell applicants the starting pay or pay range, though not necessarily in the ad itself; giving it before the interview also counts. Based on these postings, the German ads haven't changed yet.&lt;/p&gt;

&lt;p&gt;(Method note: the search catches numeric euro ranges and common pay phrases in English and German. The 4 European matches are UK and Netherlands roles at Celonis that mention "base salary". Some may be generic wording rather than an actual number.)&lt;/p&gt;

&lt;h2&gt;
  
  
  5. AI roles: Celonis stands out
&lt;/h2&gt;

&lt;p&gt;107 of the 2,392 titles (4.5%) contain AI, ML, LLM or agent. That's well below the 7.7% I measured at 20 US-based tech companies &lt;a href="https://dev.to/fast_data_asap/1-in-7-engineering-jobs-at-20-tech-companies-now-has-ai-ml-or-agent-in-the-title-1f5b"&gt;in my last analysis&lt;/a&gt;. Celonis accounts for 43 of them (20% of its postings), followed by SumUp (13) and Helsing (12).&lt;/p&gt;

&lt;h2&gt;
  
  
  How to reproduce
&lt;/h2&gt;

&lt;p&gt;Input for the Career Site Jobs API:&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="s2"&gt;"celonis"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"helsing"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"getyourguide"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"n26"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"raisin"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"maxJobsPerCompany"&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;"includeDescription"&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;"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;"Germany"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Berlin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Munich"&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;&lt;code&gt;locations&lt;/code&gt; keeps only jobs whose location contains one of those words. &lt;code&gt;includeDescription&lt;/code&gt; adds the full text, which is what the pay search needs. Pricing is $2 per 1,000 jobs. For ongoing monitoring, add &lt;code&gt;"onlyNewJobs": true&lt;/code&gt; with a &lt;code&gt;monitorName&lt;/code&gt; and schedule the run daily, and you'll get only new postings.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;At 10 well-known German tech companies, just &lt;strong&gt;21%&lt;/strong&gt; of open jobs are in Germany. Delivery Hero: 3%.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Remote is rare&lt;/strong&gt;, and these companies hire for offices.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"(all genders)"&lt;/strong&gt; and &lt;strong&gt;"(m/f/d)"&lt;/strong&gt; have overtaken "(m/w/d)". Strip them before you compare titles.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pay transparency stops at the border:&lt;/strong&gt; 83% of these companies' US postings mention pay, 0 of 279 German postings did.&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>career</category>
      <category>jobs</category>
      <category>germany</category>
      <category>datascience</category>
    </item>
    <item>
      <title>How old are open job postings? A third of tech jobs were first posted 90+ days ago (one in 2019)</title>
      <dc:creator>Fast Data</dc:creator>
      <pubDate>Sun, 25 Oct 2026 12:45:38 +0000</pubDate>
      <link>https://dev.to/fast_data_asap/how-old-are-open-job-postings-a-third-of-tech-jobs-were-first-posted-90-days-ago-one-in-2019-4pmc</link>
      <guid>https://dev.to/fast_data_asap/how-old-are-open-job-postings-a-third-of-tech-jobs-were-first-posted-90-days-ago-one-in-2019-4pmc</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Written with AI assistance. Data, code and numbers were checked by me before publishing.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When you apply to a job, you usually don't know whether it was posted last week or last year. The careers page rarely shows a date. Most job boards behind those pages do store one, though.&lt;/p&gt;

&lt;p&gt;I pulled every open job from 20 tech companies' public job boards and looked at when each one was first published.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclosure: I built the &lt;a href="https://apify.com/fast_data_asap/career-site-jobs-api" rel="noopener noreferrer"&gt;Career Site Jobs API&lt;/a&gt; used for this. It returns open jobs from Greenhouse, Ashby, Lever, Workday and other job boards as JSON, including the posting date.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The setup
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Companies:&lt;/strong&gt; Airbnb, Airtable, Asana, Brex, Cloudflare, Coinbase, Databricks, Datadog, Discord, Dropbox, Figma, GitLab, Notion, Pinterest, Plaid, Ramp, Reddit, Robinhood, Stripe, Twilio&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Jobs:&lt;/strong&gt; 4,794 open postings, every one with a date&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Date field:&lt;/strong&gt; Greenhouse's &lt;code&gt;first_published&lt;/code&gt; (17 companies) and Ashby's &lt;code&gt;publishedAt&lt;/code&gt; (Notion, Plaid, Ramp)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Snapshot:&lt;/strong&gt; 25 October 2026&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An important caveat: "first published" is the date the posting went live. If a company closes a job and reposts it, the clock resets. So these ages are a &lt;strong&gt;lower bound&lt;/strong&gt;. Some roles have been open even longer than shown.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The typical open job is almost two months old
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Share of open jobs&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Posted in the last 7 days&lt;/td&gt;
&lt;td&gt;8%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Posted in the last 30 days&lt;/td&gt;
&lt;td&gt;31%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Older than 90 days&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;33%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Older than 180 days&lt;/td&gt;
&lt;td&gt;17%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Older than 1 year&lt;/td&gt;
&lt;td&gt;7% (334 jobs)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The median open job was first posted &lt;strong&gt;53 days&lt;/strong&gt; ago. Only about a third of what you see on a careers page today went up in the last month.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Big differences between companies
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4bnp82yvl2mdlai5zms3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4bnp82yvl2mdlai5zms3.png" alt="Share of open postings older than 90 days, by company" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Company&lt;/th&gt;
&lt;th&gt;Open jobs&lt;/th&gt;
&lt;th&gt;Median age&lt;/th&gt;
&lt;th&gt;Older than 90 days&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Databricks&lt;/td&gt;
&lt;td&gt;897&lt;/td&gt;
&lt;td&gt;100 days&lt;/td&gt;
&lt;td&gt;53%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Figma&lt;/td&gt;
&lt;td&gt;138&lt;/td&gt;
&lt;td&gt;87 days&lt;/td&gt;
&lt;td&gt;49%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Datadog&lt;/td&gt;
&lt;td&gt;433&lt;/td&gt;
&lt;td&gt;73 days&lt;/td&gt;
&lt;td&gt;43%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stripe&lt;/td&gt;
&lt;td&gt;719&lt;/td&gt;
&lt;td&gt;46 days&lt;/td&gt;
&lt;td&gt;26%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cloudflare&lt;/td&gt;
&lt;td&gt;454&lt;/td&gt;
&lt;td&gt;46 days&lt;/td&gt;
&lt;td&gt;26%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pinterest&lt;/td&gt;
&lt;td&gt;163&lt;/td&gt;
&lt;td&gt;24 days&lt;/td&gt;
&lt;td&gt;18%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GitLab&lt;/td&gt;
&lt;td&gt;239&lt;/td&gt;
&lt;td&gt;29 days&lt;/td&gt;
&lt;td&gt;12%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;At Databricks, more than half of the open postings are older than three months. At GitLab and Pinterest, most postings are recent.&lt;/p&gt;

&lt;p&gt;Neither end is "good" or "bad". A company that hires continuously for the same role, such as account executives or backend engineers in a growing org, often keeps one posting open for months and fills several seats from it. A company with mostly fresh postings may have just started new roles or reposts often.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. The oldest still-open postings date back to 2019
&lt;/h2&gt;

&lt;p&gt;The oldest jobs in the data, still open on 25 October 2026:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;First posted&lt;/th&gt;
&lt;th&gt;Company&lt;/th&gt;
&lt;th&gt;Title&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;17 Apr 2019&lt;/td&gt;
&lt;td&gt;Datadog&lt;/td&gt;
&lt;td&gt;Commercial Account Executive, Boston&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;16 May 2019&lt;/td&gt;
&lt;td&gt;Datadog&lt;/td&gt;
&lt;td&gt;Commercial Account Executive&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;14 Nov 2019&lt;/td&gt;
&lt;td&gt;Databricks&lt;/td&gt;
&lt;td&gt;Senior Software Engineer, Distributed Data Systems&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;27 Jan 2021&lt;/td&gt;
&lt;td&gt;Databricks&lt;/td&gt;
&lt;td&gt;Senior Software Engineer, Database Engine Internals&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;27 Aug 2021&lt;/td&gt;
&lt;td&gt;Databricks&lt;/td&gt;
&lt;td&gt;Staff Data Scientist&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Of the 334 jobs older than a year, Databricks accounts for 165 and Datadog for 57. The most common titles among them are "Strategic Account Executive" (13) and "Commercial Account Executive" (11), followed by staff and senior backend engineers.&lt;/p&gt;

&lt;p&gt;These are almost certainly &lt;strong&gt;evergreen&lt;/strong&gt; postings: always-open roles used to collect applications for a team that hires all the time. They're not fake, but applying to one is different from applying to a role someone opened last week for one specific seat.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Engineering and AI roles stay open longest
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Role type (by title)&lt;/th&gt;
&lt;th&gt;Jobs&lt;/th&gt;
&lt;th&gt;Median age&lt;/th&gt;
&lt;th&gt;Older than 90 days&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;AI / ML / agent roles&lt;/td&gt;
&lt;td&gt;367&lt;/td&gt;
&lt;td&gt;74 days&lt;/td&gt;
&lt;td&gt;47%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Staff / Principal&lt;/td&gt;
&lt;td&gt;598&lt;/td&gt;
&lt;td&gt;76 days&lt;/td&gt;
&lt;td&gt;43%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Engineering (all)&lt;/td&gt;
&lt;td&gt;1,962&lt;/td&gt;
&lt;td&gt;66 days&lt;/td&gt;
&lt;td&gt;39%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sales / GTM&lt;/td&gt;
&lt;td&gt;1,323&lt;/td&gt;
&lt;td&gt;46 days&lt;/td&gt;
&lt;td&gt;31%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Intern / new grad&lt;/td&gt;
&lt;td&gt;174&lt;/td&gt;
&lt;td&gt;46 days&lt;/td&gt;
&lt;td&gt;21%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Senior and AI roles stay open the longest. That fits the &lt;a href="https://dev.to/fast_data_asap/1-in-7-engineering-jobs-at-20-tech-companies-now-has-ai-ml-or-agent-in-the-title-1f5b"&gt;AI job title data from the same set&lt;/a&gt;: those roles skew senior, and senior specialists are slow to hire. Intern and graduate postings are the freshest, since they follow a hiring calendar.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Jobs go up mid-week, almost never on weekends
&lt;/h2&gt;

&lt;p&gt;For postings from the last 60 days, by publishing day (UTC):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mon&lt;/th&gt;
&lt;th&gt;Tue&lt;/th&gt;
&lt;th&gt;Wed&lt;/th&gt;
&lt;th&gt;Thu&lt;/th&gt;
&lt;th&gt;Fri&lt;/th&gt;
&lt;th&gt;Sat&lt;/th&gt;
&lt;th&gt;Sun&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;408&lt;/td&gt;
&lt;td&gt;487&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;561&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;541&lt;/td&gt;
&lt;td&gt;540&lt;/td&gt;
&lt;td&gt;42&lt;/td&gt;
&lt;td&gt;19&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Wednesday is the busiest day. Saturday and Sunday together account for about 2%.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to do with this
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;If you're applying:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A fresh posting usually means a specific open seat and a recruiter actively screening. Apply early in the week the job goes up.&lt;/li&gt;
&lt;li&gt;An old posting isn't a dead end, but expect a pipeline role. Get a referral or contact the team directly rather than relying on the form alone.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;If you're recruiting or selling to these companies:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;New&lt;/em&gt; postings are the hiring signal. A job first published this week says much more about current plans than a 2019 evergreen posting.&lt;/li&gt;
&lt;li&gt;Track new postings, not all open ones. That separates "this team just got budget" from "this team always hires".&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Reproduce it, or track only new jobs
&lt;/h2&gt;

&lt;p&gt;Input for the Career Site Jobs API:&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="s2"&gt;"databricks"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"datadog"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"stripe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"gitlab"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"maxJobsPerCompany"&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="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;Every job comes back with &lt;code&gt;postedAt&lt;/code&gt; (ISO date), so the age is one subtraction. To get only postings that appeared since the last run, set &lt;code&gt;"onlyNewJobs": true&lt;/code&gt; with a &lt;code&gt;monitorName&lt;/code&gt; and schedule it daily. You then get new jobs only, which by the logic above are the ones that matter. Pricing is $2 per 1,000 jobs, and with &lt;code&gt;onlyNewJobs&lt;/code&gt; you only pay for new ones.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;The median open tech job is &lt;strong&gt;53 days&lt;/strong&gt; old, and a third are older than 90 days.&lt;/li&gt;
&lt;li&gt;Companies differ a lot: &lt;strong&gt;Databricks 53%&lt;/strong&gt; of postings older than 90 days, &lt;strong&gt;GitLab 12%&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Some postings have been open since &lt;strong&gt;2019&lt;/strong&gt;. Those are evergreen pipeline roles, mostly sales and senior engineering.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI and Staff+ roles&lt;/strong&gt; stay open longest. Intern and grad roles are the freshest.&lt;/li&gt;
&lt;li&gt;For applicants and recruiters alike, the useful signal is &lt;strong&gt;new&lt;/strong&gt; postings.&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>career</category>
      <category>jobs</category>
      <category>datascience</category>
      <category>hiring</category>
    </item>
    <item>
      <title>1 in 7 engineering jobs at 20 tech companies now has AI, ML or 'agent' in the title</title>
      <dc:creator>Fast Data</dc:creator>
      <pubDate>Sun, 25 Oct 2026 07:48:04 +0000</pubDate>
      <link>https://dev.to/fast_data_asap/1-in-7-engineering-jobs-at-20-tech-companies-now-has-ai-ml-or-agent-in-the-title-1f5b</link>
      <guid>https://dev.to/fast_data_asap/1-in-7-engineering-jobs-at-20-tech-companies-now-has-ai-ml-or-agent-in-the-title-1f5b</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Written with AI assistance. Data, code and numbers were checked by me before publishing.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Everyone says "every company is becoming an AI company". I wanted a number for that, so I looked at companies that are &lt;strong&gt;not&lt;/strong&gt; AI labs: payments, dev tools, social and fintech. How many of their open jobs are AI jobs?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclosure: I built the &lt;a href="https://apify.com/fast_data_asap/career-site-jobs-api" rel="noopener noreferrer"&gt;Career Site Jobs API&lt;/a&gt; used to collect this data. It reads public job boards (Greenhouse, Ashby, Lever, Workday and more) and returns every open job as JSON.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The setup
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Companies (20):&lt;/strong&gt; Airbnb, Airtable, Asana, Brex, Cloudflare, Coinbase, Databricks, Datadog, Discord, Dropbox, Figma, GitLab, Notion, Pinterest, Plaid, Ramp, Reddit, Robinhood, Stripe, Twilio&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Source:&lt;/strong&gt; each company's own public job board, all open jobs, deduplicated by URL&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pulled:&lt;/strong&gt; 25 October 2026&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Result:&lt;/strong&gt; 4,794 open jobs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A job counts as an "AI job" if its &lt;strong&gt;title&lt;/strong&gt; contains AI, artificial intelligence, GenAI/generative, machine learning/ML, MLOps, LLM, or agent/agents/agentic. I only looked at titles, not descriptions, so these are roles where AI is the job itself, not a nice-to-have.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. 7.7% of all jobs, 13.8% of engineering jobs
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;367 of 4,794 open jobs (7.7%)&lt;/strong&gt; have one of those terms in the title.&lt;/p&gt;

&lt;p&gt;Among engineering titles (engineer, developer or architect), the share is higher: &lt;strong&gt;230 of 1,672 (13.8%)&lt;/strong&gt;, or about 1 in 7.&lt;/p&gt;

&lt;p&gt;By term:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Term in title&lt;/th&gt;
&lt;th&gt;Jobs&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;AI (incl. GenAI, generative)&lt;/td&gt;
&lt;td&gt;224&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Machine learning / ML / MLOps&lt;/td&gt;
&lt;td&gt;124&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Agent / agentic&lt;/td&gt;
&lt;td&gt;48&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LLM&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;"LLM" in a title has almost disappeared. Companies write "AI" instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Reddit and Pinterest lead, Stripe and Robinhood trail
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp50mbjg93lm5afwnt2vg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp50mbjg93lm5afwnt2vg.png" alt="Share of open jobs with AI, ML or agents in the title, by company" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Company&lt;/th&gt;
&lt;th&gt;AI jobs&lt;/th&gt;
&lt;th&gt;Open jobs&lt;/th&gt;
&lt;th&gt;Share&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Reddit&lt;/td&gt;
&lt;td&gt;32&lt;/td&gt;
&lt;td&gt;160&lt;/td&gt;
&lt;td&gt;20%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pinterest&lt;/td&gt;
&lt;td&gt;30&lt;/td&gt;
&lt;td&gt;163&lt;/td&gt;
&lt;td&gt;18%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Figma&lt;/td&gt;
&lt;td&gt;18&lt;/td&gt;
&lt;td&gt;138&lt;/td&gt;
&lt;td&gt;13%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asana&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;109&lt;/td&gt;
&lt;td&gt;11%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Databricks&lt;/td&gt;
&lt;td&gt;86&lt;/td&gt;
&lt;td&gt;897&lt;/td&gt;
&lt;td&gt;10%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Datadog&lt;/td&gt;
&lt;td&gt;29&lt;/td&gt;
&lt;td&gt;433&lt;/td&gt;
&lt;td&gt;7%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cloudflare&lt;/td&gt;
&lt;td&gt;25&lt;/td&gt;
&lt;td&gt;454&lt;/td&gt;
&lt;td&gt;6%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stripe&lt;/td&gt;
&lt;td&gt;28&lt;/td&gt;
&lt;td&gt;719&lt;/td&gt;
&lt;td&gt;4%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Robinhood&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;157&lt;/td&gt;
&lt;td&gt;4%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;(The chart has all 18 companies with 40 or more open jobs. Dropbox had 23 open jobs and Airtable 4, so I left them out of the ranking.)&lt;/p&gt;

&lt;p&gt;The two ad-driven social platforms lead by a clear margin. Ranking, recommendations and ad targeting have been machine-learning problems for a long time. 26 of Reddit's 32 and 28 of Pinterest's 30 AI titles are machine learning roles, many in ads, retrieval and relevance. Databricks has the most AI jobs in absolute terms (86), as expected for a data and AI platform.&lt;/p&gt;

&lt;p&gt;At the bottom are the payments and fintech companies: Stripe, Robinhood, Ramp and Brex are all at 4–5%. They are hiring a lot (Stripe has 719 open jobs), but mostly for roles without AI in the title.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. "Agentic" is now a job title, and these companies use it
&lt;/h2&gt;

&lt;p&gt;48 titles mention agents, 30 of them with the word "agentic". Examples straight from the boards:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Staff Software Engineer, Agentic Applications"&lt;/li&gt;
&lt;li&gt;"Software Engineer, Agentic Commerce"&lt;/li&gt;
&lt;li&gt;"Staff AI Engineer, Agent Observability"&lt;/li&gt;
&lt;li&gt;"Staff Research Scientist, Post-Training for Agents"&lt;/li&gt;
&lt;li&gt;"Senior Product Security Engineer, AI Agents"&lt;/li&gt;
&lt;li&gt;"Director of Product Management, Agentic Software Delivery"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By company: Databricks 15, Datadog 9, Cloudflare 8, GitLab 8, Robinhood 3, Coinbase 2, and one each at Stripe, Reddit and Figma. (A caveat: Datadog has a product called "the Datadog Agent", so a few of its matches are about that product, not AI agents.)&lt;/p&gt;

&lt;p&gt;The topics are telling: commerce (agents that buy things), observability and security for agents, and agents in software delivery. Infrastructure companies are building the tooling &lt;em&gt;around&lt;/em&gt; agents.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. AI roles are more senior
&lt;/h2&gt;

&lt;p&gt;This was the clearest pattern in the data:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;AI jobs&lt;/th&gt;
&lt;th&gt;All other jobs&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Staff, Principal or Distinguished in title&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;26%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;11%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Any senior/lead/manager/director title&lt;/td&gt;
&lt;td&gt;72%&lt;/td&gt;
&lt;td&gt;60%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Marked remote&lt;/td&gt;
&lt;td&gt;28%&lt;/td&gt;
&lt;td&gt;23%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;More than one in four AI jobs is at Staff level or above, more than twice the rate for other jobs. Companies seem to be hiring experienced people to set up AI work, not many juniors to scale it.&lt;/p&gt;

&lt;p&gt;For job seekers that's mixed news. The openings exist, but the entry door is narrow. Pinterest is the exception: it lists several machine learning internships and university-grad roles for 2027.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;On pay:&lt;/strong&gt; only 269 of the 4,794 postings (5.6%) had a USD yearly salary range in the structured fields. Among engineering roles with one, the 11 AI-titled roles had a median midpoint of $272k and the 58 others $227k. Eleven is too few to conclude much, so treat that as a hint, not a finding.&lt;/p&gt;

&lt;h2&gt;
  
  
  Method notes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Titles only. A "Senior Backend Engineer" who works on an AI feature isn't counted, so these numbers are a &lt;strong&gt;lower bound&lt;/strong&gt; for AI work.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;\bAI\b&lt;/code&gt; match is case-insensitive, so it also catches titles like "AI Platform". I read through all 48 agent matches (see the Datadog caveat above).&lt;/li&gt;
&lt;li&gt;Open jobs show what companies are hiring for now, not their headcount.&lt;/li&gt;
&lt;li&gt;25 October 2026 is a snapshot. Job boards change daily.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Reproduce it
&lt;/h2&gt;

&lt;p&gt;The input for the Career Site Jobs API is just the company slugs:&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="s2"&gt;"stripe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"databricks"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"datadog"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cloudflare"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"reddit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pinterest"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"maxJobsPerCompany"&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="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;&lt;code&gt;maxJobsPerCompany: 0&lt;/code&gt; returns every open job. Each job comes back with title, department, location, remote flag, salary fields (when posted), date and URL, so the counting above is a few lines of Python with &lt;code&gt;re&lt;/code&gt;. The tool costs $2 per 1,000 jobs, so the whole dataset here would cost about $9.60. If you only care about &lt;em&gt;new&lt;/em&gt; AI roles, turn on &lt;code&gt;onlyNewJobs&lt;/code&gt; and schedule it daily. Each run then returns only postings that weren't there the day before.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;At non-AI tech companies, about &lt;strong&gt;1 in 7 engineering openings&lt;/strong&gt; is explicitly an AI, ML or agent role.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Social and ad platforms&lt;/strong&gt; lead (Reddit 20%, Pinterest 18%). &lt;strong&gt;Payments and fintech&lt;/strong&gt; trail at 4–5%.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"Agentic"&lt;/strong&gt; is a real job title now, mostly in infrastructure: commerce, observability, security, software delivery.&lt;/li&gt;
&lt;li&gt;AI roles skew &lt;strong&gt;senior&lt;/strong&gt;: 26% are Staff or above, against 11% for other jobs.&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>ai</category>
      <category>career</category>
      <category>datascience</category>
      <category>jobs</category>
    </item>
    <item>
      <title>n8n vs Zapier vs Power Automate vs Make: 5 years of Google search data, by country</title>
      <dc:creator>Fast Data</dc:creator>
      <pubDate>Sat, 24 Oct 2026 11:46:52 +0000</pubDate>
      <link>https://dev.to/fast_data_asap/n8n-vs-zapier-vs-power-automate-vs-make-5-years-of-google-search-data-by-country-2b0m</link>
      <guid>https://dev.to/fast_data_asap/n8n-vs-zapier-vs-power-automate-vs-make-5-years-of-google-search-data-by-country-2b0m</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Written with AI assistance. Data, code and numbers were checked by me before publishing.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you're choosing a workflow automation tool, or building templates and plugins for one, it helps to know where the attention is. I pulled five years of Google search interest for the four big names on a single scale and broke it down by country.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclosure: I built the &lt;a href="https://apify.com/fast_data_asap/google-trends-scraper" rel="noopener noreferrer"&gt;Google Trends Scraper&lt;/a&gt; on Apify that I used for this pull. The same numbers are visible on trends.google.com if you compare the terms by hand.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The setup
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Terms:&lt;/strong&gt; &lt;code&gt;n8n&lt;/code&gt;, &lt;code&gt;zapier&lt;/code&gt;, &lt;code&gt;power automate&lt;/code&gt;, &lt;code&gt;make.com&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scope:&lt;/strong&gt; worldwide web search, weekly, last 5 years (October 2021 to October 2026)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pulled:&lt;/strong&gt; 24 October 2026&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scale:&lt;/strong&gt; all four on one 0–100 scale (100 = n8n's peak week, 10–16 August 2025)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One caveat first. "Make" is a common English word, so I used &lt;code&gt;make.com&lt;/code&gt;, which undercounts Make. Read Make's numbers as a lower bound.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. n8n went from almost nothing to the top in about a year
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4x4zxn6u2wwj1evqjnbk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4x4zxn6u2wwj1evqjnbk.png" alt="n8n vs Zapier vs Power Automate vs Make search interest" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Using sums over 4 weeks to smooth out the noise:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Period (4 weeks ending)&lt;/th&gt;
&lt;th&gt;n8n&lt;/th&gt;
&lt;th&gt;Power Automate&lt;/th&gt;
&lt;th&gt;Zapier&lt;/th&gt;
&lt;th&gt;Make (make.com)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;October 2024&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;57&lt;/td&gt;
&lt;td&gt;24&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;October 2025&lt;/td&gt;
&lt;td&gt;263&lt;/td&gt;
&lt;td&gt;85&lt;/td&gt;
&lt;td&gt;75&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;October 2026&lt;/td&gt;
&lt;td&gt;73&lt;/td&gt;
&lt;td&gt;49&lt;/td&gt;
&lt;td&gt;26&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;n8n stayed above Zapier every week from the end of January 2025, and above Power Automate from March 2025.&lt;/li&gt;
&lt;li&gt;Two years ago n8n had &lt;strong&gt;0.42x&lt;/strong&gt; Zapier's search interest. Today it has &lt;strong&gt;2.8x&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Against Power Automate it went from &lt;strong&gt;0.18x&lt;/strong&gt; to &lt;strong&gt;1.5x&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  2. Everyone fell since late 2025. Compare ratios, not levels
&lt;/h2&gt;

&lt;p&gt;The less obvious part: all four tools peaked between August 2025 and March 2026 and have dropped since. Compared with a year ago, the last four weeks are down 72% for n8n, 65% for Zapier, 65% for Make and 42% for Power Automate.&lt;/p&gt;

&lt;p&gt;When every term in a category drops at the same time, I wouldn't read that as "automation is dying". More likely, people now ask an AI assistant instead of Googling the tool name, or Google Trends' sampling has shifted. I can't tell which from this data. What the data does support is the &lt;strong&gt;relative&lt;/strong&gt; picture: n8n's lead over Zapier has narrowed slightly (3.5x a year ago, 2.8x now), and Power Automate has held up best.&lt;/p&gt;

&lt;p&gt;If you use Google Trends for any product decision, this is the main lesson. Always put a benchmark term next to the one you care about, and look at the ratio.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. By country: n8n leads in most places, Power Automate in the big English-speaking markets
&lt;/h2&gt;

&lt;p&gt;Google Trends gives each country's split between the four terms. Of the 71 countries with enough data, n8n has the largest share in 40 and Power Automate in 29. Two are ties: Germany (n8n and Power Automate both 41%) and Nigeria (n8n and Zapier both 39%). Zapier and Make don't lead anywhere.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Country&lt;/th&gt;
&lt;th&gt;n8n&lt;/th&gt;
&lt;th&gt;Power Automate&lt;/th&gt;
&lt;th&gt;Zapier&lt;/th&gt;
&lt;th&gt;Make&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;United States&lt;/td&gt;
&lt;td&gt;25%&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;42%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;29%&lt;/td&gt;
&lt;td&gt;4%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;United Kingdom&lt;/td&gt;
&lt;td&gt;20%&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;53%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;24%&lt;/td&gt;
&lt;td&gt;3%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Canada&lt;/td&gt;
&lt;td&gt;24%&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;48%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;25%&lt;/td&gt;
&lt;td&gt;3%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Japan&lt;/td&gt;
&lt;td&gt;13%&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;76%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;10%&lt;/td&gt;
&lt;td&gt;1%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;India&lt;/td&gt;
&lt;td&gt;38%&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;44%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;16%&lt;/td&gt;
&lt;td&gt;2%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Germany&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;41%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;41%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;14%&lt;/td&gt;
&lt;td&gt;4%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;France&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;44%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;31%&lt;/td&gt;
&lt;td&gt;21%&lt;/td&gt;
&lt;td&gt;4%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Brazil&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;57%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;30%&lt;/td&gt;
&lt;td&gt;12%&lt;/td&gt;
&lt;td&gt;1%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;China&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;60%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;24%&lt;/td&gt;
&lt;td&gt;15%&lt;/td&gt;
&lt;td&gt;1%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Power Automate leads in the US, UK, Canada, Australia, Japan, India and most of the Nordics and Benelux, which are markets where Microsoft 365 is standard in companies. n8n's strongest shares are in North Africa, South Asia and Latin America (Algeria 69%, Morocco 66%, Bangladesh 65%, Egypt 61%, Brazil 57%). That fits a self-hostable tool with a free tier being popular with freelancers and agencies.&lt;/p&gt;

&lt;p&gt;Zapier never leads, but it holds 24–29% in the US, UK and Canada. It's still a strong number two in the markets with the most spending.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. What people search next to each tool
&lt;/h2&gt;

&lt;p&gt;The rising related queries show what each audience is trying to do right now:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;n8n:&lt;/strong&gt; "n8n ai agent", "n8n whatsapp", "n8n mcp", "is n8n free", "n8n templates". The top related query overall is "n8n ai".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zapier:&lt;/strong&gt; "zapier chatgpt", "zapier ai actions", plus "claude" and "perplexity"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Make:&lt;/strong&gt; "make.com pricing", "make.com alternatives", "make.com vs n8n"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Power Automate:&lt;/strong&gt; "power automate copilot", "copilot studio". "n8n" also appears as a rising query.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Two things stand out. AI agents and MCP are now the main reason people look up n8n. And people searching for Make and Power Automate are comparing them with n8n, so content that compares them honestly is clearly in demand.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd take from this
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;If you build templates, nodes or courses:&lt;/strong&gt; n8n has the attention, and "AI agent", "MCP" and "WhatsApp" are the topics people search for alongside it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;If you sell to companies in the US, UK, Japan or India:&lt;/strong&gt; don't skip Power Automate. It leads search interest there and has held up best over the past year.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;If you track any tool with Google Trends:&lt;/strong&gt; compare against benchmark terms and use ratios. The whole category dropped together over the past year.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Reproduce it
&lt;/h2&gt;

&lt;p&gt;You can type the four terms into trends.google.com. To get the raw weekly numbers, the country split and related queries as JSON (or to track them on a schedule), the &lt;a href="https://apify.com/fast_data_asap/google-trends-scraper" rel="noopener noreferrer"&gt;Google Trends Scraper&lt;/a&gt; takes this input:&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;"searchTerms"&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;"n8n"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"zapier"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"make.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"power automate"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"timeRange"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"today 5-y"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"includeInterestOverTime"&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;"includeInterestByRegion"&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;"includeRelatedQueries"&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;That run returned 6 results, which costs about $0.02 at $3 per 1,000 results. Unlike the website, it can compare more than five terms on one scale.&lt;/p&gt;

&lt;p&gt;If you'd rather get a weekly report in Google Sheets automatically, there's a free n8n workflow for exactly that in my &lt;a href="https://github.com/ichkifokkus/data-recipes/tree/main/3-weekly-trend-report" rel="noopener noreferrer"&gt;data-recipes repo&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>n8n</category>
      <category>automation</category>
      <category>nocode</category>
      <category>datascience</category>
    </item>
    <item>
      <title>Germany's job board has a free JSON API: 821,306 open jobs and what its facets reveal</title>
      <dc:creator>Fast Data</dc:creator>
      <pubDate>Sat, 24 Oct 2026 06:47:24 +0000</pubDate>
      <link>https://dev.to/fast_data_asap/germanys-job-board-has-a-free-json-api-821306-open-jobs-and-what-its-facets-reveal-2116</link>
      <guid>https://dev.to/fast_data_asap/germanys-job-board-has-a-free-json-api-821306-open-jobs-and-what-its-facets-reveal-2116</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Written with AI assistance. Data, code and numbers were checked by me before publishing.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you build anything around jobs in Germany (a job alert, a recruiting dashboard, a labour-market chart), the largest source is the Bundesagentur für Arbeit (Federal Employment Agency) and its Jobsuche. What many people don't know is that the web app runs on a JSON API you can call directly, and that every response includes facet counts for the &lt;em&gt;whole&lt;/em&gt; result set.&lt;/p&gt;

&lt;p&gt;I queried it on 24 October 2026. Below: how to call it, and what the numbers say.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclosure: I built the &lt;a href="https://apify.com/fast_data_asap/job-search-api" rel="noopener noreferrer"&gt;Job Search API&lt;/a&gt; actor on Apify, which uses this endpoint among other sources. You don't need it for anything in this post; plain Python is enough.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The endpoint
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET https://rest.arbeitsagentur.de/jobboerse/jobsuche-service/pc/v6/jobs
Header: X-API-Key: jobboerse-jobsuche
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key is the public client ID that the official Jobsuche web app sends. It's not a secret, and the community project &lt;a href="https://github.com/bundesAPI/jobsuche-api" rel="noopener noreferrer"&gt;bundesAPI/jobsuche-api&lt;/a&gt; documents the parameters. Useful ones:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parameter&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;was&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Keyword, e.g. &lt;code&gt;Softwareentwickler&lt;/code&gt;, &lt;code&gt;Python&lt;/code&gt;, &lt;code&gt;Pflegefachkraft&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;wo&lt;/code&gt; / &lt;code&gt;umkreis&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Place and radius in km, e.g. &lt;code&gt;Berlin&lt;/code&gt; / &lt;code&gt;25&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;angebotsart&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Offer type. &lt;code&gt;1&lt;/code&gt; = regular jobs (other codes cover apprenticeships, internships and self-employment)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;veroeffentlichtseit&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Published in the last N days (max 100)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;page&lt;/code&gt; / &lt;code&gt;size&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Pagination, up to 100 results per page&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A minimal call:&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;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://rest.arbeitsagentur.de/jobboerse/jobsuche-service/pc/v6/jobs&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;HEADERS&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;X-API-Key&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;jobboerse-jobsuche&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;params&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;angebotsart&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;size&lt;/span&gt;&lt;span class="sh"&gt;"&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="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;HEADERS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;30&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="nf"&gt;raise_for_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;was&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Softwareentwickler&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;wo&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;München&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;umkreis&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;25&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;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;maxErgebnisse&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;                 &lt;span class="c1"&gt;# total matches
&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;job&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ergebnisliste&lt;/span&gt;&lt;span class="sh"&gt;"&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;job&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;stellenangebotsTitel&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;|&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;firma&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;p&gt;Each job has the title, employer, location with coordinates, publish date, a reference number (&lt;code&gt;referenznummer&lt;/code&gt;) and, when the employer states one, a salary range (&lt;code&gt;gehaltsspanneVon&lt;/code&gt; / &lt;code&gt;gehaltsspanneBis&lt;/code&gt;). The detail page is &lt;code&gt;https://www.arbeitsagentur.de/jobsuche/jobdetail/&amp;lt;referenznummer&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part most people skip: facets
&lt;/h2&gt;

&lt;p&gt;Next to the results, each response has a &lt;code&gt;facetten&lt;/code&gt; object with counts for the whole result set: employers, cities, occupations, working time, temp agency or not, salary stated or not. Ask for &lt;code&gt;size=1&lt;/code&gt; and you get labour-market statistics in one request.&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="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;facetten&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;f&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;zeitarbeit&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;counts&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;          &lt;span class="c1"&gt;# temp agency yes/no
&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;f&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;veroeffentlichtseit&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;counts&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="c1"&gt;# new in last 1/7/14/28 days
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's what that showed on 24 October 2026.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. 821,306 open jobs, about 17,000 new per day
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Published within&lt;/th&gt;
&lt;th&gt;Jobs&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;last day&lt;/td&gt;
&lt;td&gt;17,448&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;last 7 days&lt;/td&gt;
&lt;td&gt;86,127&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;last 28 days&lt;/td&gt;
&lt;td&gt;316,370&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;all open&lt;/td&gt;
&lt;td&gt;821,306&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;So roughly 2% of the stock is new each day. If you build an alert, polling once a day with &lt;code&gt;veroeffentlichtseit=1&lt;/code&gt; is enough and keeps requests small.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. One in four jobs is with a temp agency
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;zeitarbeit&lt;/code&gt; facet marks &lt;strong&gt;214,738&lt;/strong&gt; of 821,306 jobs (26%) as temporary agency work. The employer facet shows the same thing: the top employers by open postings are Netto (15,029), TimePartner (13,381), Randstad (9,620), Sanovetis (9,434) and PerZukunft (7,703). At least three of those five are staffing or placement firms.&lt;/p&gt;

&lt;p&gt;If you count "hiring companies" from this data, filter out agency postings first. Otherwise a few staffing firms swamp your numbers.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Only about a quarter state a salary
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;verguetung&lt;/code&gt; facet counts jobs with a stated pay figure: 152,586 hourly and 65,735 yearly, together &lt;strong&gt;218,321 (27%)&lt;/strong&gt;. It is similar within fields:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Softwareentwickler&lt;/code&gt;: 812 of 3,345 (24%) state pay, almost all as a yearly figure&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Pflegefachkraft&lt;/code&gt; (registered nurse): 5,245 of 23,263 (23%)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the salary fields are useful, but don't treat a missing value as "unpaid" or "low". Three quarters of postings simply don't say.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Software jobs: Munich beats Berlin within 25 km
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Search&lt;/th&gt;
&lt;th&gt;Open jobs&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;Softwareentwickler&lt;/code&gt;, all Germany&lt;/td&gt;
&lt;td&gt;3,345&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;… within 25 km of München&lt;/td&gt;
&lt;td&gt;241&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;… within 25 km of Berlin&lt;/td&gt;
&lt;td&gt;189&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;… published in the last 7 days&lt;/td&gt;
&lt;td&gt;444&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;Python&lt;/code&gt;, all Germany&lt;/td&gt;
&lt;td&gt;2,307&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;Pflegefachkraft&lt;/code&gt;, all Germany&lt;/td&gt;
&lt;td&gt;23,263&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Across all occupations Berlin has by far the most postings (35,344, against 15,854 for München). For software developers within a 25 km radius, Munich comes out ahead. Nursing has about seven times as many open postings as software development.&lt;/p&gt;

&lt;p&gt;In a sample of 100 &lt;code&gt;Python&lt;/code&gt; jobs, 20 had &lt;code&gt;homeofficemoeglich: true&lt;/code&gt;, and Baden-Württemberg (23) and Bayern (21) led the regions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gotchas
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Keyword search is broad.&lt;/strong&gt; &lt;code&gt;was=Python&lt;/code&gt; also matches jobs where Python is just one skill among many. Filter titles afterwards if you need precision.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Totals move during the day.&lt;/strong&gt; Postings are added and removed all the time, so store the timestamp with any number you publish.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deduplicate by &lt;code&gt;referenznummer&lt;/code&gt;&lt;/strong&gt;, not by title. The same title appears hundreds of times (Verkäufer/in alone has 37,138 postings).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The detail endpoint&lt;/strong&gt; (full description) is a separate call per job, so only fetch it for jobs you keep.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Be polite.&lt;/strong&gt; It's a public service. Cache results, use the facets instead of paging through everything, and don't hammer it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  When to use a ready-made actor instead
&lt;/h2&gt;

&lt;p&gt;For one script, the code above is all you need. My &lt;a href="https://apify.com/fast_data_asap/job-search-api" rel="noopener noreferrer"&gt;Job Search API&lt;/a&gt; is for when you want the Bundesagentur results &lt;strong&gt;merged&lt;/strong&gt; with other open sources in one deduplicated list: Arbeitnow (Germany and Europe), Remote OK and Himalayas (remote jobs), plus company career pages on Greenhouse, Lever, Ashby and SmartRecruiters. All results share one schema (title, company, location, remote, salary, posted date, URL, source), and it runs on a schedule without a server. It costs $2.50 per 1,000 jobs.&lt;/p&gt;

&lt;p&gt;If you're watching specific companies rather than keywords, the &lt;a href="https://apify.com/fast_data_asap/career-site-jobs-api" rel="noopener noreferrer"&gt;Career Site Jobs API&lt;/a&gt; can return only postings that are new since the last run.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Germany's biggest job board has a JSON API. One header, no signup.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;size=1&lt;/code&gt; and the &lt;code&gt;facetten&lt;/code&gt; object to get market statistics in a single request.&lt;/li&gt;
&lt;li&gt;Before drawing conclusions, account for temp agencies (26% of postings) and missing salaries (73%).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;All numbers are from queries on 24 October 2026, around 07:00 UTC.&lt;/p&gt;

</description>
      <category>api</category>
      <category>python</category>
      <category>datascience</category>
      <category>career</category>
    </item>
    <item>
      <title>Your AI news feed is missing half the story: one search, four Google News editions, 765 headlines</title>
      <dc:creator>Fast Data</dc:creator>
      <pubDate>Fri, 23 Oct 2026 16:45:58 +0000</pubDate>
      <link>https://dev.to/fast_data_asap/your-ai-news-feed-is-missing-half-the-story-one-search-four-google-news-editions-765-headlines-4n79</link>
      <guid>https://dev.to/fast_data_asap/your-ai-news-feed-is-missing-half-the-story-one-search-four-google-news-editions-765-headlines-4n79</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Written with AI assistance. Data, code and numbers were checked by me before publishing.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you monitor a topic through Google News, whether in an RSS reader, an n8n workflow or a script, you are almost certainly reading one country's edition. Usually that is the US one, because it is the default.&lt;/p&gt;

&lt;p&gt;I wanted to know how much that matters, so I ran the same searches in four English-language editions and compared the results. The short answer: a lot more than I expected.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclosure: I built the Apify actor used to collect this data (&lt;a href="https://apify.com/fast_data_asap/google-news-scraper" rel="noopener noreferrer"&gt;Google News Scraper&lt;/a&gt;). Everything below can also be reproduced with the free Google News RSS feed and a few lines of Python; I show how at the end.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The setup
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Queries:&lt;/strong&gt; &lt;code&gt;AI&lt;/code&gt;, &lt;code&gt;AI startup&lt;/code&gt;, &lt;code&gt;artificial intelligence&lt;/code&gt;, &lt;code&gt;AI model&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Editions:&lt;/strong&gt; US, UK, India, Australia (all English)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Time range:&lt;/strong&gt; last 7 days (15 to 22 October 2026)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pulled:&lt;/strong&gt; 22 October 2026, around 08:00 UTC&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Matching:&lt;/strong&gt; two results count as the same headline when the title is identical&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each edition returned between 365 and 376 unique headlines. Across all four editions there were &lt;strong&gt;765 different headlines&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Result: only 15% of headlines are shared by everyone
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh2afg3njorg29vje6giz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh2afg3njorg29vje6giz.png" alt="Same AI search in 4 Google News editions" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Edition&lt;/th&gt;
&lt;th&gt;Unique headlines&lt;/th&gt;
&lt;th&gt;Only in this edition&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;US&lt;/td&gt;
&lt;td&gt;370&lt;/td&gt;
&lt;td&gt;95&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UK&lt;/td&gt;
&lt;td&gt;365&lt;/td&gt;
&lt;td&gt;105&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;India&lt;/td&gt;
&lt;td&gt;376&lt;/td&gt;
&lt;td&gt;118&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Australia&lt;/td&gt;
&lt;td&gt;370&lt;/td&gt;
&lt;td&gt;82&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;All four&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;765 total&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;118 in all four (15%)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;So any single edition showed you less than half (about 48%) of what the four editions surfaced together. Between 22% (Australia) and 31% (India) of each feed appeared in no other edition.&lt;/p&gt;

&lt;p&gt;One caveat: matching on identical titles undercounts overlap a bit. When two outlets cover the same story with different headlines, that counts as two items here. The point still stands: each edition ranks and selects &lt;em&gt;different sources&lt;/em&gt;, not just different wording.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the local-only headlines look like
&lt;/h2&gt;

&lt;p&gt;The exclusive headlines are not noise. They come from each country's own business and public broadcasters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;US-only:&lt;/strong&gt; Reuters (7), WSJ (5), Axios (4), The New York Times (4). Examples: a Philadelphia-area software firm buying its third AI startup for $40 million; a WSJ piece on AI agents causing headaches for online sellers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UK-only:&lt;/strong&gt; BBC (8), Financial Times (8), The Guardian (7), Tech.eu (4). Lots of European funding rounds you won't see in the US feed, such as small seed rounds for Nordic and EU startups.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;India-only:&lt;/strong&gt; NDTV (8), Moneycontrol (5), Financial Times (5), The Economic Times (5). Indian startup funding (for example an AI hiring startup raising Rs 39.3 crore) and Indian policy coverage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Australia-only:&lt;/strong&gt; AFR (11), The Conversation (8), The Guardian (7), ABC (7). Bank job cuts linked to AI, AI training on Australian music, a police warning about AI and youth crime.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you work in sales, investing, policy or recruiting, these are exactly the stories you want, and a US-only feed hides them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Even the "big company" picture shifts
&lt;/h2&gt;

&lt;p&gt;I also counted how many headlines in each edition named the big AI companies (whole-word match on the name):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Edition&lt;/th&gt;
&lt;th&gt;Most-mentioned companies&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;US&lt;/td&gt;
&lt;td&gt;OpenAI 20, Meta 15, DeepSeek 14, Anthropic 11, Apple 11&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UK&lt;/td&gt;
&lt;td&gt;Nvidia 13, Anthropic 13, OpenAI 11, Meta 11, DeepSeek 10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;India&lt;/td&gt;
&lt;td&gt;OpenAI 17, Google 13, Nvidia 13, Meta 13, Amazon 11&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Australia&lt;/td&gt;
&lt;td&gt;OpenAI 16, Meta 12, Anthropic 11, DeepSeek 10, Apple 9&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;OpenAI leads in three editions, but in the UK feed Nvidia and Anthropic come out ahead. In India, Google and Amazon rank much higher than elsewhere. If you report share of voice from one edition, your ranking depends on which country you happened to pick.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix: one URL parameter
&lt;/h2&gt;

&lt;p&gt;Google News RSS URLs take three edition parameters. This is the India edition of a search:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://news.google.com/rss/search?q=AI+startup+when:7d&amp;amp;hl=en-IN&amp;amp;gl=IN&amp;amp;ceid=IN:en
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;gl&lt;/code&gt; is the country (&lt;code&gt;US&lt;/code&gt;, &lt;code&gt;GB&lt;/code&gt;, &lt;code&gt;IN&lt;/code&gt;, &lt;code&gt;AU&lt;/code&gt;, &lt;code&gt;DE&lt;/code&gt;, ...)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;hl&lt;/code&gt; is the interface language (&lt;code&gt;en-IN&lt;/code&gt;, &lt;code&gt;en-GB&lt;/code&gt;, &lt;code&gt;de&lt;/code&gt;, ...)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ceid&lt;/code&gt; is &lt;code&gt;country:language&lt;/code&gt; (&lt;code&gt;IN:en&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;when:7d&lt;/code&gt; inside the query limits results to the last 7 days&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is a minimal script that reproduces the overlap count with the free feed (standard library plus &lt;code&gt;feedparser&lt;/code&gt;):&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;import&lt;/span&gt; &lt;span class="n"&gt;feedparser&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;urllib.parse&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;quote_plus&lt;/span&gt;

&lt;span class="n"&gt;EDITIONS&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;US&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;en-US&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;GB&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;en-GB&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;IN&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;en-IN&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;AU&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;en-AU&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;QUERIES&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;AI&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;AI startup&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;artificial intelligence&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;AI model&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;titles&lt;/span&gt; &lt;span class="o"&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;gl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hl&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;EDITIONS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;seen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;set&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;q&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;QUERIES&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://news.google.com/rss/search?q=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;quote_plus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; when&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
               &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;amp;hl=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;hl&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;&amp;amp;gl=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;gl&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;&amp;amp;ceid=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;gl&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;:en&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;seen&lt;/span&gt; &lt;span class="o"&gt;|=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;feedparser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;titles&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;seen&lt;/span&gt;

&lt;span class="n"&gt;union&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;union&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;titles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;values&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="n"&gt;common&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;intersection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;titles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;values&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;union&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;headlines,&lt;/span&gt;&lt;span class="sh"&gt;"&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;common&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;in all editions&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;p&gt;Each RSS query returns up to about 100 items, so for broad topics you will want more specific queries rather than one big one.&lt;/p&gt;

&lt;h2&gt;
  
  
  When the free feed is enough, and when it isn't
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The free RSS feed is enough&lt;/strong&gt; if you only need titles, sources and dates for a handful of queries and you are happy to run the script yourself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It gets annoying&lt;/strong&gt; when you need the real article URL (the RSS links are Google redirect URLs, not the publisher's), the full article text for an LLM summary, or many queries across many editions on a schedule. That is the part my actor automates: it takes a list of queries, an edition (18 countries and languages) and a time range, resolves the original URLs and can optionally extract the full text. It costs $2.50 per 1,000 articles, plus $2.50 per 1,000 full texts if you turn that on. For this analysis I left full text off, which is the cheap mode.&lt;/p&gt;

&lt;p&gt;If you already use n8n, I published a free workflow that sends a daily competitor news digest to Slack: &lt;a href="https://github.com/ichkifokkus/data-recipes/tree/main/2-daily-news-alert" rel="noopener noreferrer"&gt;data-recipes / daily news alert&lt;/a&gt;. To cover more countries, run the same node once per edition and merge the results.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Google News is not one feed. Each country edition picks different sources, and here only 15% of headlines appeared in all four.&lt;/li&gt;
&lt;li&gt;Monitoring from one edition shows you under half the coverage and skews your share-of-voice numbers.&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;gl&lt;/code&gt;, &lt;code&gt;hl&lt;/code&gt; and &lt;code&gt;ceid&lt;/code&gt; to your RSS URLs and loop over the markets you care about. It is a two-minute change.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you run this for other topics or languages, I'd like to see whether the overlap is similar. My guess is that it is lower for non-English editions.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>news</category>
      <category>datascience</category>
      <category>python</category>
    </item>
    <item>
      <title>How to get YouTube transcripts in bulk (whole channels and playlists) as text or JSON, without the YouTube Data API</title>
      <dc:creator>Fast Data</dc:creator>
      <pubDate>Fri, 23 Oct 2026 11:46:54 +0000</pubDate>
      <link>https://dev.to/fast_data_asap/how-to-get-youtube-transcripts-in-bulk-whole-channels-and-playlists-as-text-or-json-without-the-309g</link>
      <guid>https://dev.to/fast_data_asap/how-to-get-youtube-transcripts-in-bulk-whole-channels-and-playlists-as-text-or-json-without-the-309g</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Written with AI assistance. Data, code and numbers were checked by me before publishing.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You want the transcripts of a whole YouTube channel: to feed a RAG pipeline, summarise talks with an LLM, or count how often a topic comes up. The obvious first stop is the official YouTube Data API. It won't get you there. This post covers why, what the free DIY route looks like, and how to do it in bulk with one API call.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Disclosure:&lt;/strong&gt; I built the &lt;a href="https://apify.com/fast_data_asap/youtube-transcript-scraper" rel="noopener noreferrer"&gt;YouTube Transcript &amp;amp; Subtitles Scraper&lt;/a&gt; on Apify that the last part uses. The numbers below come from real runs of it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the YouTube Data API doesn't work for this
&lt;/h2&gt;

&lt;p&gt;The Data API has a &lt;code&gt;captions&lt;/code&gt; resource, so it looks like the right tool. But the &lt;a href="https://developers.google.com/youtube/v3/docs/captions/download" rel="noopener noreferrer"&gt;captions.download reference&lt;/a&gt; says the method needs OAuth with the &lt;code&gt;youtube.force-ssl&lt;/code&gt; or &lt;code&gt;youtubepartner&lt;/code&gt; scope, and the user has to have permission to edit the video. If you call it on someone else's video, you get a &lt;code&gt;403 forbidden&lt;/code&gt; error: &lt;em&gt;"The permissions associated with the request are not sufficient to download the caption track."&lt;/em&gt; Each call also costs 200 quota units, out of a default budget of 10,000 units a day.&lt;/p&gt;

&lt;p&gt;So &lt;code&gt;captions.download&lt;/code&gt; works for &lt;strong&gt;your own&lt;/strong&gt; videos. Most transcript projects need other people's videos, and for those it doesn't work at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  The DIY route: youtube-transcript-api
&lt;/h2&gt;

&lt;p&gt;The Python library &lt;a href="https://github.com/jdepoix/youtube-transcript-api" rel="noopener noreferrer"&gt;youtube-transcript-api&lt;/a&gt; reads the same captions the YouTube web player shows. It needs no API key and no OAuth:&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="c1"&gt;# pip install youtube-transcript-api
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;youtube_transcript_api&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;YouTubeTranscriptApi&lt;/span&gt;

&lt;span class="n"&gt;ytt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;YouTubeTranscriptApi&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;fetched&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ytt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;zjkBMFhNj_g&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;languages&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;en&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;fetched&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;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;words&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;p&gt;Here are the pros and cons.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It's free and open source, and it's good. My actor uses it under the hood.&lt;/li&gt;
&lt;li&gt;It handles auto-generated vs. manual captions, language selection and YouTube's built-in translations.&lt;/li&gt;
&lt;li&gt;For a few videos on your own machine, it's all you need.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;IP blocks.&lt;/strong&gt; YouTube blocks many cloud IP ranges (AWS, GCP, Azure and others). Code that works on your laptop often fails on a server with &lt;code&gt;RequestBlocked&lt;/code&gt; / &lt;code&gt;IpBlocked&lt;/code&gt;. The library's README recommends rotating residential proxies to get around this, which means a proxy subscription and more code to maintain.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One video at a time.&lt;/strong&gt; It takes video IDs. To get a whole channel or playlist, you first have to list the video IDs with something like &lt;code&gt;yt-dlp&lt;/code&gt; or the Data API's &lt;code&gt;playlistItems&lt;/code&gt; endpoint, which uses up quota.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Upkeep.&lt;/strong&gt; Retries, rate limits, bookkeeping for videos without captions, and fixes whenever YouTube changes something are all on you.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you need ten videos from your laptop, stop here. If you need 500 videos from a cron job on a cloud server, read on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bulk: whole channels and playlists in one call
&lt;/h2&gt;

&lt;p&gt;The actor takes any mix of video URLs, Shorts, &lt;code&gt;@channel&lt;/code&gt; URLs, playlist URLs and search queries. It lists the videos with &lt;code&gt;yt-dlp&lt;/code&gt; (metadata only, nothing is downloaded), fetches the transcripts, and returns one JSON item per video. It starts with cheap datacenter proxies and retries blocked requests through residential proxies automatically.&lt;/p&gt;

&lt;p&gt;With curl (the call waits for the run to finish and returns the dataset):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"https://api.apify.com/v2/acts/fast_data_asap~youtube-transcript-scraper/run-sync-get-dataset-items?token=&lt;/span&gt;&lt;span class="nv"&gt;$APIFY_TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "startUrls": [
      "https://www.youtube.com/@aiDotEngineer/videos",
      "https://www.youtube.com/playlist?list=YOUR_PLAYLIST_ID"
    ],
    "maxVideosPerSource": 50,
    "languages": ["en"],
    "includeSegments": true,
    "chunkSizeChars": 2000
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With Python (&lt;code&gt;pip install apify-client&lt;/code&gt;):&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;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_APIFY_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;fast_data_asap/youtube-transcript-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;startUrls&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;https://www.youtube.com/@aiDotEngineer/videos&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;maxVideosPerSource&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;languages&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;en&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="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&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="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;if&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&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;ok&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;item&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;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;wordCount&lt;/span&gt;&lt;span class="sh"&gt;"&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;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;isGenerated&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;p&gt;Each item has these fields:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;videoId&lt;/code&gt;, &lt;code&gt;url&lt;/code&gt;, &lt;code&gt;title&lt;/code&gt;, &lt;code&gt;channel&lt;/code&gt;, &lt;code&gt;durationSeconds&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;status&lt;/code&gt;: &lt;code&gt;ok&lt;/code&gt;, &lt;code&gt;transcripts_disabled&lt;/code&gt;, &lt;code&gt;no_transcript_in_requested_languages&lt;/code&gt;, &lt;code&gt;video_unavailable&lt;/code&gt;, &lt;code&gt;age_restricted&lt;/code&gt; or &lt;code&gt;failed&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;language&lt;/code&gt;, &lt;code&gt;languageCode&lt;/code&gt;, &lt;code&gt;isGenerated&lt;/code&gt;, &lt;code&gt;isTranslated&lt;/code&gt;, &lt;code&gt;availableTranscripts&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;text&lt;/code&gt;: the full transcript as one string&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;segments&lt;/code&gt;: &lt;code&gt;[{start, duration, text}]&lt;/code&gt; with timestamps in seconds&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;chunks&lt;/code&gt;: optional &lt;code&gt;[{start, end, text}]&lt;/code&gt; blocks for embeddings&lt;/li&gt;
&lt;li&gt;&lt;code&gt;wordCount&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Real numbers.&lt;/strong&gt; Andrej Karpathy's "[1hr Talk] Intro to Large Language Models" runs 59:49. It came back as 12,151 words in 1,704 timestamped segments, for $0.002. A run on the AI Engineer channel (&lt;code&gt;maxVideosPerSource: 12&lt;/code&gt;) returned 12 transcripts, all &lt;code&gt;status: ok&lt;/code&gt;, 58,654 words in total, for $0.024.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pricing:&lt;/strong&gt; $2 per 1,000 transcripts, dropping to $1.70 and then $1.50 per 1,000 on higher Apify plans. You aren't charged for videos without a transcript; they still show up in the output with their status. Apify's $5 of free monthly credit covers about 2,500 transcripts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tip: auto-captions misspell product names
&lt;/h2&gt;

&lt;p&gt;Almost every transcript you'll get is auto-generated (&lt;code&gt;isGenerated: true&lt;/code&gt;). All 12 AI Engineer talks above were. Speech recognition is bad with proper nouns, which quietly breaks keyword counts and search. In those 12 talks:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Spoken&lt;/th&gt;
&lt;th&gt;Transcribed as&lt;/th&gt;
&lt;th&gt;Wrong&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Kimi&lt;/td&gt;
&lt;td&gt;"kimmy"&lt;/td&gt;
&lt;td&gt;19 of 19&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DeepSeek&lt;/td&gt;
&lt;td&gt;"deep seek"&lt;/td&gt;
&lt;td&gt;7 of 14&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Code&lt;/td&gt;
&lt;td&gt;"cloud code"&lt;/td&gt;
&lt;td&gt;3 of 7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ChatGPT&lt;/td&gt;
&lt;td&gt;"chat gpt" / "jgpt"&lt;/td&gt;
&lt;td&gt;2 of 7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Anthropic&lt;/td&gt;
&lt;td&gt;"entropic"&lt;/td&gt;
&lt;td&gt;1 of 15&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;(Small sample: n=12 talks from one channel, and Kimi and DeepSeek both come from a single workshop. The pattern is still real.)&lt;/p&gt;

&lt;p&gt;Normalise the text before you count or embed it:&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;import&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;

&lt;span class="n"&gt;ALIASES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;\bkimmy\b&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;Kimi&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;\bdeep seek\b&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;DeepSeek&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;\bcloud code\b&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;Claude Code&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;\bchat gpt\b|\bjgpt\b&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;ChatGPT&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;\bentropic\b&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;Anthropic&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fix_names&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&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;pattern&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;canonical&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ALIASES&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;canonical&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IGNORECASE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="c1"&gt;# items = the dataset items from the run above
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&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;ok&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;isGenerated&lt;/span&gt;&lt;span class="sh"&gt;"&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;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&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="nf"&gt;fix_names&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;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&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;p&gt;Only apply rules like &lt;code&gt;cloud code&lt;/code&gt; → &lt;code&gt;Claude Code&lt;/code&gt; to &lt;code&gt;isGenerated&lt;/code&gt; transcripts on a topic where that's what the speaker meant. In a talk about cloud infrastructure, "cloud code" may be correct. Build your own alias list from the names that matter in your niche.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Your own videos:&lt;/strong&gt; the YouTube Data API's &lt;code&gt;captions.download&lt;/code&gt; works with OAuth.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A handful of other people's videos, run locally:&lt;/strong&gt; use &lt;code&gt;youtube-transcript-api&lt;/code&gt;. It's free and solid.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Whole channels, playlists or searches, run on a server or schedule:&lt;/strong&gt; use a hosted scraper that handles listing videos, proxies and retries. &lt;a href="https://apify.com/fast_data_asap/youtube-transcript-scraper" rel="noopener noreferrer"&gt;Here's mine&lt;/a&gt;, at $2 per 1,000 transcripts, with no charge for videos that have none.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Whichever route you take, check &lt;code&gt;isGenerated&lt;/code&gt; and clean up proper nouns before you trust any counts. Transcripts belong to their creators, so make sure your use follows copyright law and YouTube's terms.&lt;/p&gt;

</description>
      <category>youtube</category>
      <category>python</category>
      <category>ai</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How to scrape Workday job postings (myworkdayjobs.com) as JSON: the 2,000-result limit and how to get around it</title>
      <dc:creator>Fast Data</dc:creator>
      <pubDate>Fri, 23 Oct 2026 06:45:46 +0000</pubDate>
      <link>https://dev.to/fast_data_asap/how-to-scrape-workday-job-postings-myworkdayjobscom-as-json-the-2000-result-limit-and-how-to-239i</link>
      <guid>https://dev.to/fast_data_asap/how-to-scrape-workday-job-postings-myworkdayjobscom-as-json-the-2000-result-limit-and-how-to-239i</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Written with AI assistance. Data, code and numbers were checked by me before publishing.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A large share of big employers (NVIDIA, Salesforce, Wells Fargo and many more) run their careers page on Workday. The URLs look like &lt;code&gt;https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite&lt;/code&gt;. If you want those jobs as clean JSON for a job board, a hiring-signal alert or a market analysis, most job APIs won't help you, because they don't cover Workday.&lt;/p&gt;

&lt;p&gt;This post covers the do-it-yourself route first: how the endpoint works, the 2,000-result limit, and the facet trick that gets around it. After that I show how to get the same data in one call.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclosure: I built the Apify actor shown at the end (&lt;a href="https://apify.com/fast_data_asap/career-site-jobs-api" rel="noopener noreferrer"&gt;Career Site Jobs API&lt;/a&gt;). The DIY route below is free and works fine for a handful of companies.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How a Workday career site loads its jobs
&lt;/h2&gt;

&lt;p&gt;Open any &lt;code&gt;*.myworkdayjobs.com&lt;/code&gt; site and watch the network tab. The page is a JavaScript app, and the job list comes from a public JSON endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;POST https://&amp;lt;tenant&amp;gt;.wd&amp;lt;N&amp;gt;.myworkdayjobs.com/wday/cxs/&amp;lt;tenant&amp;gt;/&amp;lt;site&amp;gt;/jobs
Content-Type: application/json

{"appliedFacets": {}, "limit": 20, "offset": 0, "searchText": ""}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For NVIDIA, &lt;code&gt;tenant&lt;/code&gt; is &lt;code&gt;nvidia&lt;/code&gt;, &lt;code&gt;N&lt;/code&gt; is &lt;code&gt;5&lt;/code&gt; and &lt;code&gt;site&lt;/code&gt; is &lt;code&gt;NVIDIAExternalCareerSite&lt;/code&gt;. All three come straight from the careers URL. You can't guess them from a company name, so you need the URL once.&lt;/p&gt;

&lt;p&gt;The response holds:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;total&lt;/code&gt;: the number of matching jobs&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;jobPostings&lt;/code&gt;: 20 jobs, each with &lt;code&gt;title&lt;/code&gt;, &lt;code&gt;externalPath&lt;/code&gt;, &lt;code&gt;locationsText&lt;/code&gt;, &lt;code&gt;postedOn&lt;/code&gt; ("Posted 3 Days Ago") and sometimes &lt;code&gt;remoteType&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;facets&lt;/code&gt;: the filters in the sidebar (job family, location, time type, ...), each with a &lt;code&gt;facetParameter&lt;/code&gt; and a list of &lt;code&gt;values&lt;/code&gt; with an &lt;code&gt;id&lt;/code&gt; and a &lt;code&gt;count&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pagination is plain &lt;code&gt;offset&lt;/code&gt; paging in steps of 20. The job ID is the last part of &lt;code&gt;externalPath&lt;/code&gt; (&lt;code&gt;..._JR2017966&lt;/code&gt;), and &lt;code&gt;GET /wday/cxs/&amp;lt;tenant&amp;gt;/&amp;lt;site&amp;gt;&amp;lt;externalPath&amp;gt;&lt;/code&gt; returns the full posting with description and all locations.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 2,000-result limit
&lt;/h2&gt;

&lt;p&gt;This is where most scrapers quietly fail. &lt;strong&gt;Workday serves at most 2,000 results per query.&lt;/strong&gt; On a site with 2,700 open jobs, &lt;code&gt;total&lt;/code&gt; stops at 2,000 and paging past offset 2,000 gets you nothing more. You don't get an error. You just get 26% fewer jobs than the site has.&lt;/p&gt;

&lt;p&gt;The fix is in the &lt;code&gt;facets&lt;/code&gt; block. Every facet value has its own &lt;code&gt;count&lt;/code&gt;, and a query filtered to one value has its own 2,000 window. So:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Fetch page 0 without filters and read &lt;code&gt;facets&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Pick a facet where &lt;strong&gt;every&lt;/strong&gt; value has fewer than 2,000 jobs (job family or category usually works).&lt;/li&gt;
&lt;li&gt;Page through each value separately via &lt;code&gt;appliedFacets: {"&amp;lt;facetParameter&amp;gt;": ["&amp;lt;value id&amp;gt;"]}&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;De-duplicate by &lt;code&gt;externalPath&lt;/code&gt;, because some facets let one job appear under two values.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The sum of the facet counts is also the best estimate of how many jobs are really open, which is how you notice you hit the limit in the first place.&lt;/p&gt;

&lt;h2&gt;
  
  
  A DIY sketch in Python
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;This is a sketch.&lt;/strong&gt; My dev sandbox can't reach &lt;code&gt;myworkdayjobs.com&lt;/code&gt;, so I haven't run this exact script. The request body, headers and response fields match what the actor below sends and parses, and that part worked in the run shown further down.&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;import&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;BASE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://nvidia.wd5.myworkdayjobs.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;API&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;BASE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/wday/cxs/nvidia/NVIDIAExternalCareerSite/jobs&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;HEADERS&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;Content-Type&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;application/json&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;Accept&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;application/json&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;Accept-Language&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;en-US&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;WINDOW&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;page&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;facets&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;body&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;appliedFacets&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;facets&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;limit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;offset&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;searchText&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="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;API&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;HEADERS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;30&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="nf"&gt;raise_for_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# be polite: one request at a time, small pause
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fetch_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;first&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;page&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="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;total&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;WINDOW&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;pages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;page&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;query&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;o&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;pages&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;jobPostings&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="n"&gt;first&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;page&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="n"&gt;jobs&lt;/span&gt; &lt;span class="o"&gt;=&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;first&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;total&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="n"&gt;WINDOW&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;fetch_all&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# split by a facet whose values all stay below the 2,000 window
&lt;/span&gt;    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;facets&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="n"&gt;vals&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;values&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="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;count&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;v&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;vals&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;count&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;v&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;vals&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;WINDOW&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;v&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;vals&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;fetch_all&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;facetParameter&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="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]]},&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;count&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;

&lt;span class="n"&gt;unique&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;externalPath&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;j&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="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;unique&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;jobs&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;p&gt;Things to know before you run it at scale:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Rate limits.&lt;/strong&gt; Workday tenants throttle aggressive clients. One request at a time with a short pause is plenty for a daily run. The actor never sends more than 5 Workday requests in parallel.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Some facets are nested&lt;/strong&gt; (e.g. location groups contain locations). The sketch skips values without a flat &lt;code&gt;id&lt;/code&gt;/&lt;code&gt;count&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dates are relative.&lt;/strong&gt; "Posted 30+ Days Ago" has no exact date. You need the detail request for that.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;locationsText&lt;/code&gt; can say "5 Locations".&lt;/strong&gt; The real list is only in the detail response.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Be a good citizen.&lt;/strong&gt; Check the site's terms and &lt;code&gt;robots.txt&lt;/code&gt;, use only the public endpoint the career site itself calls, and don't log in, rotate identities or work around a block. If a tenant refuses automated requests, skip it. Job postings are business data, but descriptions sometimes name a recruiter, so don't build person profiles from them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's a working base for one company. In practice the maintenance is in everything around it: tenants on &lt;code&gt;myworkdaysite.com&lt;/code&gt; with a different URL scheme, sites that need the site ID read from the HTML, 400s for renamed sites, de-duplication, and remembering which jobs you've already seen.&lt;/p&gt;

&lt;h2&gt;
  
  
  The same thing in one call
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://apify.com/fast_data_asap/career-site-jobs-api" rel="noopener noreferrer"&gt;Career Site Jobs API&lt;/a&gt; actor does exactly the above: same public endpoint, paging, automatic facet splitting above 2,000 (up to 10,000 jobs per company and run), with no login and no browser. It also covers Greenhouse, Lever, Ashby, SmartRecruiters, Workable, Teamtailor, Recruitee and Personio with the same output schema.&lt;/p&gt;

&lt;p&gt;I ran it on 23 October 2026 with a deliberately tiny limit:&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="s2"&gt;"https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"maxJobsPerCompany"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&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 finished in 5 seconds and returned 5 jobs. The &lt;code&gt;SUMMARY&lt;/code&gt; record reported &lt;strong&gt;&lt;code&gt;"jobsOpenOnSite": 2719&lt;/code&gt;&lt;/strong&gt; for NVIDIA, so this site is over the 2,000 window, and a plain offset loop would miss more than 700 jobs. One of the returned items:&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;"Senior Software Developer, Test and Automation"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"company"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Nvidia"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ats"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"workday"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"location"&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, CA, Santa Clara"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"remote"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"department"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"employmentType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"postedAt"&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-10-23T00:00: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;"url"&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://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite/job/US-CA-Santa-Clara/Senior-Software-Developer--Test-and-Automation_JR2026347"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"jobId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"JR2026347"&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 few honest notes. &lt;code&gt;department&lt;/code&gt; is always empty for Workday because the endpoint doesn't publish it. Two of the five items had &lt;code&gt;location: "5 Locations"&lt;/code&gt;/&lt;code&gt;"2 Locations"&lt;/code&gt;. Turn on &lt;code&gt;includeDescription&lt;/code&gt; to get the full location list, employment type, exact date and plain-text description, which costs one extra request per job and makes the run slower.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cost:&lt;/strong&gt; pricing is per job returned, at $2 per 1,000 jobs (higher Apify plans go down to $1.50 per 1,000). These 5 jobs cost $0.01. Companies that are not found or that block the request return a free &lt;code&gt;failed&lt;/code&gt;/&lt;code&gt;not_found&lt;/code&gt; item instead of a broken run. All ~2,700 NVIDIA jobs would cost about $5.40.&lt;/p&gt;

&lt;p&gt;From Python:&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;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;&amp;lt;APIFY_TOKEN&amp;gt;&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;fast_data_asap/career-site-jobs-api&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;companies&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;https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite&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;keywords&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;engineer&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;maxJobsPerCompany&lt;/span&gt;&lt;span class="sh"&gt;"&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="c1"&gt;# 0 = all open jobs
&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="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;list_items&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;For alerts you rarely want the full list every day. Set &lt;code&gt;"onlyNewJobs": true&lt;/code&gt; and schedule the actor. The first run stores the job IDs as a baseline, and later runs return (and charge for) only postings that weren't there before. If nothing is new, you get one free &lt;code&gt;no_results&lt;/code&gt; item.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which one should you use?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;One or two Workday sites, occasionally:&lt;/strong&gt; use the DIY sketch. It's free and teaches you how the endpoint works.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dozens of companies across several ATS, daily, with delta mode:&lt;/strong&gt; that's the maintenance the actor saves you.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Either way, remember the 2,000 window. If your Workday scraper always returns exactly 2,000 jobs, it isn't done.&lt;/p&gt;

</description>
      <category>python</category>
      <category>webscraping</category>
      <category>api</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>A PDF inbox that summarises itself: Google Drive, n8n and an LLM (free workflow)</title>
      <dc:creator>Fast Data</dc:creator>
      <pubDate>Thu, 22 Oct 2026 16:51:23 +0000</pubDate>
      <link>https://dev.to/fast_data_asap/a-pdf-inbox-that-summarises-itself-google-drive-n8n-and-an-llm-free-workflow-7lg</link>
      <guid>https://dev.to/fast_data_asap/a-pdf-inbox-that-summarises-itself-google-drive-n8n-and-an-llm-free-workflow-7lg</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Written with AI assistance. Data, code and numbers were checked by me before publishing.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Every team has a folder where PDFs go to be ignored: supplier invoices, signed offers, a 40-page report someone "will read later". The fix I wanted was boring and specific: drop a file into a Google Drive folder, and a minute later there is a Google Doc next to it that says what the document is, lists the key facts with page numbers, and says whether anyone has to do something.&lt;/p&gt;

&lt;p&gt;This post is that workflow for n8n, with the parts that were harder than expected.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Disclosure:&lt;/strong&gt; I built this actor: the PDF conversion step runs on &lt;a href="https://apify.com/fast_data_asap/document-to-markdown" rel="noopener noreferrer"&gt;Document to Markdown&lt;/a&gt; on Apify. Prices and the free alternatives are below.&lt;/p&gt;

&lt;h2&gt;
  
  
  The flow
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Google Drive Trigger (new file in "PDF inbox")   Webhook (POST {"url": ...})
                    └──────────────┬──────────────┘
                     Code: collect { url, name }
                                   ↓
                HTTP Request: Document to Markdown
                                   ↓
                         IF status = success
          ├ yes → LLM chain: summarise → Google Docs: create → Google Docs: insert text
          └ no  → Set: file + error (not charged)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two entry points feed the same pipeline. The Drive Trigger polls one folder every minute. The webhook takes &lt;code&gt;{"url": "...", "name": "..."}&lt;/code&gt; so anything that can make an HTTP call (a mail parser, a form, another workflow) can push documents in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why convert to Markdown before the LLM?
&lt;/h2&gt;

&lt;p&gt;You can paste raw PDF text into a model, and for a clean one-column letter it works. Invoices, price lists and reports are mostly tables, and plain text extraction flattens tables into a stream of numbers. The model then has to guess which amount belongs to which line, and that is exactly where summaries get wrong.&lt;/p&gt;

&lt;p&gt;Markdown keeps tables as rows and columns and puts a &lt;code&gt;&amp;lt;!-- Page N --&amp;gt;&lt;/code&gt; marker before every page. That second part makes the prompt instruction "add the page number for each fact" actually work.&lt;/p&gt;

&lt;h2&gt;
  
  
  A real run
&lt;/h2&gt;

&lt;p&gt;I tested the conversion step with a one-page sample AWS invoice from the open-source &lt;a href="https://github.com/invoice-x/invoice2data" rel="noopener noreferrer"&gt;invoice2data&lt;/a&gt; test files. The Actor input is exactly what the n8n node sends:&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;"documentUrls"&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://raw.githubusercontent.com/invoice-x/invoice2data/master/tests/compare/AmazonWebServices.pdf"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ocr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"auto"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"includePageBreaks"&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;Result metadata:&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;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"success"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"fileType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pdf"&lt;/span&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;"Amazon Web Services Invoice 296664039561"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"pageCount"&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="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"pagesProcessed"&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="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ocrPages"&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;"billedPages"&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="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;326&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 conversion took 0.9 seconds; the whole run, including container start, about 10 seconds. The totals block came back as a proper table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Summary&lt;/span&gt;

| AWS Service Charges    | $4.11 |
| ---------------------- | ----- |
| Charges                | $4.11 |
| Credits                | $0.00 |
| Tax &lt;span class="err"&gt;*&lt;/span&gt;                  | $0.00 |
| Total for this invoice | $4.11 |
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the per-service detail (EC2 $1.87, Glacier $2.22, S3 $0.01, data transfer $0.01) as rows of a second table. That is what you want an LLM to read.&lt;/p&gt;

&lt;p&gt;The honest part: the bill-to address lines came out as &lt;code&gt;##&lt;/code&gt; headings, because they are set in a larger font and the converter treats bigger text as headings. Harmless for a summary, but don't treat the headings of an invoice as a reliable outline.&lt;/p&gt;

&lt;h2&gt;
  
  
  The prompt
&lt;/h2&gt;

&lt;p&gt;The summary step is n8n's Basic LLM Chain with &lt;code&gt;gpt-5-mini&lt;/code&gt; (swap in any chat model node):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Write plain text, no Markdown:
1. One sentence: what kind of document this is and who it is from.
2. Key facts as 3-7 short lines starting with '- ': parties, dates,
   amounts, deadlines, decisions. Copy numbers exactly. Add the page
   number from the &amp;lt;!-- Page N --&amp;gt; markers, e.g. (p. 2).
3. 'Action needed:' followed by what someone has to do, or 'none'.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;"Copy numbers exactly" and the page references matter more than any clever wording. If a fact has a page number, you can check it in five seconds.&lt;/p&gt;

&lt;p&gt;The Google Doc then gets the summary, a source line (&lt;code&gt;Pages: 1 (OCR: 0), words: 326&lt;/code&gt;) and the full Markdown below it, so the doc is searchable in Drive even when the summary missed something.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part that took longest: Drive links
&lt;/h2&gt;

&lt;p&gt;The Actor runs in the cloud and downloads files by URL. It never sees your Google account. So the Code node turns each new Drive file into a share link (&lt;code&gt;drive.google.com/file/d/&amp;lt;id&amp;gt;/view&lt;/code&gt;, or the &lt;code&gt;docs.google.com/...&lt;/code&gt; link for native Google Docs, Slides and Sheets), and the Actor converts those into direct downloads.&lt;/p&gt;

&lt;p&gt;That only works if the folder is shared as &lt;em&gt;Anyone with the link: Viewer&lt;/em&gt;. Drive IDs are long and random, but this is still a real trade-off: for contracts, payroll or health data, don't use a link-shared folder. Use the webhook with expiring links (S3 pre-signed URLs, for example), or run a converter yourself.&lt;/p&gt;

&lt;h2&gt;
  
  
  DIY alternatives
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;MarkItDown&lt;/strong&gt; (Microsoft, MIT): great for DOCX/PPTX/XLSX, decent for digital PDFs, no OCR out of the box. If you self-host n8n, an Execute Command node calling it is a perfectly good replacement for the HTTP step.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;pdfplumber / pdftotext&lt;/strong&gt;: fast and free; you build tables and page markers yourself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Send the PDF straight to a multimodal model&lt;/strong&gt;: works for short files, but you pay vision tokens per page and get no reusable text for search.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The hosted route is worth it when you don't want to maintain OCR dependencies on your n8n box, or when documents arrive as URLs anyway.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cost
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;$1.50 per 1,000 pages&lt;/strong&gt; with a text layer, &lt;strong&gt;+$5 per 1,000&lt;/strong&gt; for pages that need OCR, up to 25 % less on higher Apify plans.&lt;/li&gt;
&lt;li&gt;Failed downloads and files without text are free.&lt;/li&gt;
&lt;li&gt;50 one- or two-page invoices a month: about $0.10. My test run: one page, $0.0015.&lt;/li&gt;
&lt;li&gt;Apify's free plan includes $5 a month, about 3,300 text pages.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Add &lt;code&gt;"pages": "1-5"&lt;/code&gt; to the input to convert (and pay for) only the first pages of long reports.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get it
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Workflow JSON and setup guide: &lt;a href="https://github.com/ichkifokkus/data-recipes/tree/main/6-pdf-inbox-to-markdown" rel="noopener noreferrer"&gt;https://github.com/ichkifokkus/data-recipes/tree/main/6-pdf-inbox-to-markdown&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Actor: &lt;a href="https://apify.com/fast_data_asap/document-to-markdown" rel="noopener noreferrer"&gt;https://apify.com/fast_data_asap/document-to-markdown&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Setup takes about ten minutes: an Apify token as a Header Auth credential, Google Drive and Docs, and an LLM credential. Drop one test PDF into the folder before you point a mail rule at it.&lt;/p&gt;

</description>
      <category>n8n</category>
      <category>automation</category>
      <category>ai</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
