<?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>Bulk-upscale product photos from a Google Sheet with n8n and Real-ESRGAN</title>
      <dc:creator>Fast Data</dc:creator>
      <pubDate>Wed, 21 Oct 2026 11:44:13 +0000</pubDate>
      <link>https://dev.to/fast_data_asap/bulk-upscale-product-photos-from-a-google-sheet-with-n8n-and-real-esrgan-3fe5</link>
      <guid>https://dev.to/fast_data_asap/bulk-upscale-product-photos-from-a-google-sheet-with-n8n-and-real-esrgan-3fe5</guid>
      <description>&lt;p&gt;If you have ever imported products from a supplier, you know the problem: half of the images are 400 pixels wide. Marketplaces want 1,000+ px, zoom features want more, and nobody wants to open 300 files one by one in an upscaling web app.&lt;/p&gt;

&lt;p&gt;This n8n workflow does it from a Google Sheet. You paste image URLs into a column, click &lt;em&gt;Execute workflow&lt;/em&gt;, and every image is upscaled 4x with Real-ESRGAN, saved to Google Drive, and the Drive link, new size and status are written back into the same row. Run it again later and it only processes the new rows.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Disclosure:&lt;/strong&gt; the upscaling runs on the AI Image Upscaler Actor on Apify, which I built. Prices are below.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it looks
&lt;/h2&gt;

&lt;p&gt;Your sheet has a tab &lt;code&gt;Images&lt;/code&gt; with four columns (illustrative rows):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;image_url&lt;/th&gt;
&lt;th&gt;upscaled_url&lt;/th&gt;
&lt;th&gt;size&lt;/th&gt;
&lt;th&gt;status&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;https://…/sku-1001.jpg&lt;/td&gt;
&lt;td&gt;&lt;a href="https://drive.google.com/file/d/%E2%80%A6/view" rel="noopener noreferrer"&gt;https://drive.google.com/file/d/…/view&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;3200x3200&lt;/td&gt;
&lt;td&gt;done&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;https://…/sku-1002.jpg&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;failed: HTTPStatusError: Client error '404 Not Found' …&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;https://…/sku-1003.jpg&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;You fill &lt;code&gt;image_url&lt;/code&gt;; the workflow fills the rest. In my test run a 179x179 px JPG came back as a 716x716 px file (92 kB) after about a second of processing, and a broken link was written back as &lt;code&gt;failed&lt;/code&gt;, free of charge.&lt;/p&gt;

&lt;h2&gt;
  
  
  The workflow
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Manual Trigger
  → Google Sheets: read "Images"
  → Filter: has image_url, no upscaled_url
  → Code: batch URLs (5 per batch)
  → HTTP Request: AI Image Upscaler
  → IF status = ok
       ├ yes → HTTP Request: download → Google Drive: upload → Set row → Google Sheets: update
       └ no  → Set row (failed + reason) ──────────────────────────────→ Google Sheets: update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Batching
&lt;/h3&gt;

&lt;p&gt;Apify's synchronous endpoint (&lt;code&gt;run-sync-get-dataset-items&lt;/code&gt;) is the simplest way to call an Actor from n8n: one request in, results out. It waits at most 300 seconds, though. Instead of sending 300 images in one run, a Code node groups the URLs into batches of five:&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;BATCH_SIZE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&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="p"&gt;[...&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&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;i&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;image_url&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Boolean&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;batches&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="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;urls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;BATCH_SIZE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;batches&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;json&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;imageUrls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;urls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;BATCH_SIZE&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="nx"&gt;batches&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The HTTP Request node runs once per incoming item, so each batch becomes one Actor run, and the body is built from the item:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight handlebars"&gt;&lt;code&gt;=&lt;span class="k"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;imageUrls&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;imageUrls&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;scale&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'4'&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;outputFormat&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'jpg'&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;quality&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;92&lt;/span&gt; &lt;span class="err"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Actor returns one result per image with &lt;code&gt;status&lt;/code&gt;, &lt;code&gt;outputUrl&lt;/code&gt;, &lt;code&gt;outputWidth&lt;/code&gt;, &lt;code&gt;outputHeight&lt;/code&gt;, or an &lt;code&gt;error&lt;/code&gt; for images it could not process.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why copy to Google Drive?
&lt;/h3&gt;

&lt;p&gt;The upscaled files live in the Apify run's key-value store. The links work right away, but unnamed Apify storage is deleted after your plan's retention period (7 days on the free plan at the time of writing). For product images you want something permanent, so the workflow downloads each file (same Apify credential) and uploads it to a Drive folder. Swap that node for S3, Cloudinary or your shop's media upload if you prefer.&lt;/p&gt;

&lt;h3&gt;
  
  
  Writing back to the right row
&lt;/h3&gt;

&lt;p&gt;The Google Sheets &lt;em&gt;Update&lt;/em&gt; operation matches on the &lt;code&gt;image_url&lt;/code&gt; column. That only works if the URL that comes back is exactly the one in the sheet, which is why the batching code trims whitespace and the Actor echoes the original URL as &lt;code&gt;sourceUrl&lt;/code&gt;. The Drive file name is derived from it too: &lt;code&gt;sku-1001.jpg&lt;/code&gt; becomes &lt;code&gt;sku-1001-4x.jpg&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quality and limits
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Real-ESRGAN (general x4 v3) is good at sharpening edges, text and product shapes. It does not invent detail that isn't there; a blurry face stays a slightly cleaner blurry face.&lt;/li&gt;
&lt;li&gt;Inputs are capped at 1 megapixel by default (larger images are scaled down first), so a 4x result is up to about 16 MP. That's plenty for shops and print catalogues and keeps processing fast.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;"scale": "2"&lt;/code&gt; is often enough for web shops and gives smaller files.&lt;/li&gt;
&lt;li&gt;PNG output keeps transparency for cut-out product shots and logos.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;&lt;strong&gt;$0.015 per upscaled image&lt;/strong&gt;, $15 per 1,000. Failed images are free. 100 product photos = $1.50. Apify's free plan includes $5 per month, roughly 330 images.&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/5-bulk-image-upscale" rel="noopener noreferrer"&gt;https://github.com/ichkifokkus/data-recipes/tree/main/5-bulk-image-upscale&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Actor: &lt;a href="https://apify.com/fast_data_asap/ai-image-upscaler" rel="noopener noreferrer"&gt;https://apify.com/fast_data_asap/ai-image-upscaler&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Setup takes about ten minutes: one Apify credential, Google Sheets and Google Drive. Start with two or three rows to check the output before you throw your whole catalogue at it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Only upscale images you have the rights to use. The model is BSD-licensed; the images are not.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>n8n</category>
      <category>ecommerce</category>
      <category>ai</category>
      <category>automation</category>
    </item>
    <item>
      <title>Hiring signals: get a Slack alert when target companies post new jobs (n8n)</title>
      <dc:creator>Fast Data</dc:creator>
      <pubDate>Wed, 21 Oct 2026 06:45:13 +0000</pubDate>
      <link>https://dev.to/fast_data_asap/hiring-signals-get-a-slack-alert-when-target-companies-post-new-jobs-n8n-4e32</link>
      <guid>https://dev.to/fast_data_asap/hiring-signals-get-a-slack-alert-when-target-companies-post-new-jobs-n8n-4e32</guid>
      <description>&lt;p&gt;Job postings are one of the most honest signals a company sends. A startup that opens five account executive roles in a new country is about to sell there. A first "Head of Data" means a data stack is about to be bought. A burst of support roles often follows a launch.&lt;/p&gt;

&lt;p&gt;Sales teams call these hiring signals, investors use them to track growth, and job seekers just want to be early. In all three cases the job is the same: watch a list of career pages and tell me what is new. This n8n workflow does exactly that, once a day, for about a dollar a month.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Disclosure:&lt;/strong&gt; the job data comes from the Job Search API Actor on Apify, which I built. Pricing and limits are below.&lt;/p&gt;

&lt;h2&gt;
  
  
  The result
&lt;/h2&gt;

&lt;p&gt;From a test run watching Stripe, Ramp and Palantir (postings from the last two days):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Hiring signals: 13 new role(s) at 3 company(ies)

Stripe: 9 new (Sales 3, Engineering 3, Leadership 1, Marketing 1, Customer 1)
- Account Executive, Velocity Platforms (Hunter) - Chicago, IL; New York, NY; ...
- Backend / API Engineer, Radar
- Country Manager, Israel - Israel
- Engineering Manager, Money as a Service (MaaS) - Singapore
...and 4 more (see the sheet)

Ramp: 2 new (Recruiting &amp;amp; People 2)
- Technical Sourcer - Engineering (Contract) - New York, NY (HQ)
- Lead Recruiter - Executive - New York, NY (HQ)

Palantir: 2 new (Engineering 2)
- Software Engineer, New Grad - Query Engines - London, United Kingdom
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;"Country Manager, Israel" is precisely the kind of line a sales or partnerships team wants to see on the day it appears. Every posting is also appended to a Google Sheet (company, team, title, location, remote, posted date, URL), so you can count over months.&lt;/p&gt;

&lt;h2&gt;
  
  
  The workflow
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Schedule (daily 8:00)
  → HTTP Request: Job Search API (company career pages)
  → Remove Duplicates (previous executions, by job URL)
  → Code: tag team &amp;amp; pick columns
      ├→ Google Sheets (append)
      └→ Code: build digest → Slack
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Reading career pages without scraping HTML
&lt;/h3&gt;

&lt;p&gt;Most tech companies host their careers page on an applicant tracking system: Greenhouse, Lever, Ashby or SmartRecruiters. All four have public JSON endpoints for open positions. The Actor reads those and returns one unified format, so you can mix them freely:&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;"greenhouse: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;"ashby:ramp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lever:palantir"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"postedWithinDays"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"maxJobsPerSource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"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;false&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also pass careers URLs like &lt;code&gt;https://jobs.lever.co/palantir&lt;/code&gt;. Since no &lt;code&gt;keywords&lt;/code&gt; are set, only the company pages are read and every role is returned. (With keywords, the Actor also searches public job boards: Remote OK, Himalayas, Arbeitnow and the German Federal Employment Agency. That's handy for a job-seeker version.)&lt;/p&gt;

&lt;p&gt;The HTTP Request node calls &lt;code&gt;https://api.apify.com/v2/acts/fast_data_asap~job-search-api/run-sync-get-dataset-items&lt;/code&gt; with a Header Auth credential (&lt;code&gt;Authorization: Bearer &amp;lt;token&amp;gt;&lt;/code&gt;). For three companies, the run took about six seconds.&lt;/p&gt;

&lt;h3&gt;
  
  
  "New since the last run"
&lt;/h3&gt;

&lt;p&gt;This is the part people usually build with a database. n8n has it built in: &lt;strong&gt;Remove Duplicates&lt;/strong&gt; with "Remove items processed in previous executions", keyed on &lt;code&gt;{{ $json.url }}&lt;/code&gt;. It stores every URL it has seen and only passes on new ones. I tested it by running the workflow twice in a row: the first run let 13 postings through, the second discarded all 13.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;postedWithinDays: 2&lt;/code&gt; (not 1) is deliberate. If a run fails or n8n is down for a day, the next run still covers the gap, and deduplication takes care of the overlap.&lt;/p&gt;

&lt;h3&gt;
  
  
  Guessing the team
&lt;/h3&gt;

&lt;p&gt;Job titles are messy, but a dozen regexes get you surprisingly far:&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;TEAMS&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Leadership&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\b(&lt;/span&gt;&lt;span class="sr"&gt;head of|director|vp|vice president|chief|country manager|general manager&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;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Recruiting &amp;amp; People&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sr"&gt;/recruit|sourcer|talent|people partner|&lt;/span&gt;&lt;span class="se"&gt;\b&lt;/span&gt;&lt;span class="sr"&gt;hr&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;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Sales&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sr"&gt;/sales|account executive|account manager|business development|&lt;/span&gt;&lt;span class="se"&gt;\b[&lt;/span&gt;&lt;span class="sr"&gt;bs&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;dr&lt;/span&gt;&lt;span class="se"&gt;\b&lt;/span&gt;&lt;span class="sr"&gt;|partnership/i&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="c1"&gt;// ... Marketing, Customer, Data &amp;amp; AI, Engineering, Product &amp;amp; Design&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Order matters. My first version put Engineering first, and "Account Executive, Velocity &lt;strong&gt;Platforms&lt;/strong&gt;" was filed as Engineering because of &lt;code&gt;platform&lt;/code&gt;. Now leadership and go-to-market patterns are checked first, and Engineering no longer matches on "platform".&lt;/p&gt;

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

&lt;p&gt;The Actor charges &lt;strong&gt;$0.0025 per job returned&lt;/strong&gt;. The test run above returned 13 jobs = $0.03; a daily run for three fast-growing companies comes to about &lt;strong&gt;$1 per month&lt;/strong&gt;. Because of the one-day overlap, some postings are returned (and paid) twice; for very large companies, &lt;code&gt;postedWithinDays: 1&lt;/code&gt; halves the cost.&lt;/p&gt;

&lt;p&gt;Apify's free plan includes $5 per month.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ideas
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Filter to what you sell into&lt;/strong&gt;: add a Filter node, e.g. &lt;code&gt;team = Sales&lt;/code&gt; or title contains "Data".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tech-stack research&lt;/strong&gt;: &lt;code&gt;includeDescription: true&lt;/code&gt; plus an LLM step "Which tools does this posting mention?"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CRM task instead of Slack&lt;/strong&gt;: create a HubSpot or Pipedrive task "Company X is hiring 5 AEs in DACH".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Job-seeker mode&lt;/strong&gt;: add &lt;code&gt;"keywords": "python developer"&lt;/code&gt; and &lt;code&gt;"remoteOnly": true&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Workday employers&lt;/strong&gt;: the Job Search API doesn't read Workday career sites. For those I built a second Actor, 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;: nine ATS including Workday, Workable, Teamtailor, Recruitee and Personio, plus an optional "only new jobs" mode that remembers what it has already returned. $2 per 1,000 jobs (down to $1.50 on higher Apify plans).&lt;/li&gt;
&lt;/ul&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/4-hiring-signal-alert" rel="noopener noreferrer"&gt;https://github.com/ichkifokkus/data-recipes/tree/main/4-hiring-signal-alert&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Actor: &lt;a href="https://apify.com/fast_data_asap/job-search-api" rel="noopener noreferrer"&gt;https://apify.com/fast_data_asap/job-search-api&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Job ads can contain recruiter names and e-mail addresses. Store and use them in line with data protection rules.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>n8n</category>
      <category>automation</category>
      <category>sales</category>
      <category>jobs</category>
    </item>
    <item>
      <title>Build a weekly Google Trends report in Google Sheets with n8n (no pytrends)</title>
      <dc:creator>Fast Data</dc:creator>
      <pubDate>Tue, 20 Oct 2026 16:44:30 +0000</pubDate>
      <link>https://dev.to/fast_data_asap/build-a-weekly-google-trends-report-in-google-sheets-with-n8n-no-pytrends-453j</link>
      <guid>https://dev.to/fast_data_asap/build-a-weekly-google-trends-report-in-google-sheets-with-n8n-no-pytrends-453j</guid>
      <description>&lt;p&gt;Google Trends is one of the most useful free datasets in marketing and one of the most annoying to automate. There is no official API, the website compares at most five keywords, and &lt;code&gt;pytrends&lt;/code&gt; breaks whenever Google changes something. So most people check it by hand once in a while and forget about it.&lt;/p&gt;

&lt;p&gt;Here is a small n8n workflow that turns it into a habit: every Monday it pulls the last 90 days for your keywords and appends one row per keyword to a Google Sheet, with this week vs. last week, change in %, the peak day and the top rising related searches. After a month you have a trend history that's easy to chart.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Disclosure:&lt;/strong&gt; the data comes from the Google Trends Scraper on Apify, which I built. Costs are in the post.&lt;/p&gt;

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

&lt;p&gt;Real numbers from a test run (US, past 90 days, three note-taking apps):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;keyword&lt;/th&gt;
&lt;th&gt;this_week&lt;/th&gt;
&lt;th&gt;last_week&lt;/th&gt;
&lt;th&gt;change_pct&lt;/th&gt;
&lt;th&gt;period_avg&lt;/th&gt;
&lt;th&gt;peak&lt;/th&gt;
&lt;th&gt;rising_queries&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;notion&lt;/td&gt;
&lt;td&gt;52.9&lt;/td&gt;
&lt;td&gt;53.4&lt;/td&gt;
&lt;td&gt;-1.1&lt;/td&gt;
&lt;td&gt;61.1&lt;/td&gt;
&lt;td&gt;Aug 24, 2026&lt;/td&gt;
&lt;td&gt;notion ai workspace (+200%), ...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;obsidian&lt;/td&gt;
&lt;td&gt;74.1&lt;/td&gt;
&lt;td&gt;75.1&lt;/td&gt;
&lt;td&gt;-1.3&lt;/td&gt;
&lt;td&gt;75.6&lt;/td&gt;
&lt;td&gt;Jul 26, 2026&lt;/td&gt;
&lt;td&gt;air jordan 12 obsidian 2027 (Breakout), ...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;evernote&lt;/td&gt;
&lt;td&gt;2.9&lt;/td&gt;
&lt;td&gt;3.7&lt;/td&gt;
&lt;td&gt;-23.1&lt;/td&gt;
&lt;td&gt;3.4&lt;/td&gt;
&lt;td&gt;Aug 13, 2026&lt;/td&gt;
&lt;td&gt;how to delete evernote account (Breakout), ...&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Two things jump out, and they are exactly why I like having the rising queries next to the numbers:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;"obsidian" scores higher than "notion", but the rising queries are about sneakers, phones and Minecraft. The keyword is ambiguous, so its number tells you little about the app.&lt;/li&gt;
&lt;li&gt;Evernote is small and dropping, and the top rising query is "how to delete evernote account".&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The numbers are Google's &lt;strong&gt;relative interest (0-100)&lt;/strong&gt; on one shared scale, not search volumes. They are good for comparing terms and periods, not for "how many people searched".&lt;/p&gt;

&lt;h2&gt;
  
  
  The workflow
&lt;/h2&gt;

&lt;p&gt;Four nodes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Schedule (Monday 8:00) → HTTP Request (Google Trends Scraper) → Code → Google Sheets (append)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Fetching the data
&lt;/h3&gt;

&lt;p&gt;The HTTP Request node calls Apify's &lt;code&gt;run-sync-get-dataset-items&lt;/code&gt; endpoint for the Actor &lt;code&gt;fast_data_asap~google-trends-scraper&lt;/code&gt; (Header Auth credential with &lt;code&gt;Authorization: Bearer &amp;lt;token&amp;gt;&lt;/code&gt;) with this body:&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;"notion"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"obsidian"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"evernote"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"geo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"US"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"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 3-m"&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;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;"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;The response contains one item of type &lt;code&gt;interestOverTime&lt;/code&gt; (a timeline with values for every keyword) and one &lt;code&gt;relatedQueries&lt;/code&gt; item per keyword, with &lt;code&gt;top&lt;/code&gt; and &lt;code&gt;rising&lt;/code&gt; lists.&lt;/p&gt;

&lt;p&gt;Unlike the Trends website you can pass &lt;strong&gt;more than five keywords&lt;/strong&gt;. The Actor splits them into batches that share an anchor keyword and rescales everything onto one 0-100 scale, so twenty competitors are still directly comparable.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Code node
&lt;/h3&gt;

&lt;p&gt;The interesting part is turning a timeline into "this week vs. last week":&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;points&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timeline&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[]).&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&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;isPartial&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;stepDays&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;points&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;points&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="nx"&gt;time&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;points&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="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;86400&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;7&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;win&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max&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="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;stepDays&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// 7 for daily data, 1 for weekly data&lt;/span&gt;
&lt;span class="k"&gt;for &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;kw&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keywords&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;series&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;points&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Number&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;values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;kw&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;0&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;thisWeek&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;avg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;series&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;win&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;lastWeek&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;avg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;series&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;win&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;win&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="c1"&gt;// ... change_pct, peak, top 5 rising queries&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two details:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Drop the partial point.&lt;/strong&gt; The most recent data point is usually incomplete (&lt;code&gt;isPartial: true&lt;/code&gt;) and always looks like a drop. Comparing it would give you a "-40%" panic every Monday.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Detect the resolution.&lt;/strong&gt; Google returns daily points for 90 days and weekly points for 12 months. Computing the step from the timestamps means you can change &lt;code&gt;timeRange&lt;/code&gt; without touching the code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The result is one item per keyword, and the Google Sheets node appends them using automatic column mapping, so the header row just needs matching names.&lt;/p&gt;

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

&lt;p&gt;The Actor charges &lt;strong&gt;$0.003 per result&lt;/strong&gt;. A result is one timeline, one regional breakdown or one related-queries list. Three keywords with related queries = 4 results = $0.012 per run, about &lt;strong&gt;$0.05 a month&lt;/strong&gt; for a weekly report. With related queries switched off, a run costs $0.003 no matter how many keywords you track.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ideas
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Spike alert&lt;/strong&gt;: add an IF node &lt;code&gt;change_pct &amp;gt; 30&lt;/code&gt; → Slack.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Seasonality&lt;/strong&gt;: a one-off run with &lt;code&gt;"timeRange": "today 5-y"&lt;/code&gt; before you plan campaigns or stock.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Where to advertise&lt;/strong&gt;: &lt;code&gt;"includeInterestByRegion": true&lt;/code&gt; with &lt;code&gt;"regionResolution": "REGION"&lt;/code&gt; or &lt;code&gt;"CITY"&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;YouTube or Shopping interest&lt;/strong&gt;: &lt;code&gt;"searchProperty": "youtube"&lt;/code&gt; / &lt;code&gt;"shopping"&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Brand tracking&lt;/strong&gt;: your brand plus competitors on one scale, with a steady &lt;code&gt;anchorTerm&lt;/code&gt; when you track more than five.&lt;/li&gt;
&lt;/ul&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/3-weekly-trend-report" rel="noopener noreferrer"&gt;https://github.com/ichkifokkus/data-recipes/tree/main/3-weekly-trend-report&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Actor: &lt;a href="https://apify.com/fast_data_asap/google-trends-scraper" rel="noopener noreferrer"&gt;https://apify.com/fast_data_asap/google-trends-scraper&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you track something interesting with it, tell me in the comments. I'm curious which keywords people watch.&lt;/p&gt;

</description>
      <category>n8n</category>
      <category>seo</category>
      <category>googletrends</category>
      <category>automation</category>
    </item>
    <item>
      <title>A daily competitor news digest in Slack for ~$1.50 a month (n8n + Google News)</title>
      <dc:creator>Fast Data</dc:creator>
      <pubDate>Tue, 20 Oct 2026 11:44:14 +0000</pubDate>
      <link>https://dev.to/fast_data_asap/a-daily-competitor-news-digest-in-slack-for-150-a-month-n8n-google-news-405e</link>
      <guid>https://dev.to/fast_data_asap/a-daily-competitor-news-digest-in-slack-for-150-a-month-n8n-google-news-405e</guid>
      <description>&lt;p&gt;Google Alerts still exists, but it has been unreliable for years: late, incomplete, and stuck in your inbox. What I actually wanted was simpler: every morning, one Slack message with what was published about our brand and our competitors in the last 24 hours, with links to the original articles, and nothing I have already seen.&lt;/p&gt;

&lt;p&gt;This is the n8n workflow that does it. It has five nodes, takes about five minutes to set up and costs around $1.50 a month.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Disclosure:&lt;/strong&gt; the news data comes from the Google News Scraper on Apify, which I built. Prices and limits are below.&lt;/p&gt;

&lt;h2&gt;
  
  
  The result
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;News digest 2026-10-20: 19 new article(s)

OpenAI (10)
- OpenAI races to raise $30 billion at $1.4 trillion valuation ahead of Anthropic IPO (CTech)
- EXCLUSIVE: OpenAI seeks to accelerate fundraising ahead of Anthropic IPO, sources say (Reuters)
- Lawsuit alleges OpenAI 'stole' children's user data (Mashable)
...

Anthropic (9)
- Former Google CEO Schmidt owns 1% stake in Anthropic, Bloomberg reports (Reuters)
- Anthropic is AI trade's last big hope for a major IPO this year (CNBC)
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is real output from a test run, with "OpenAI" and "Anthropic" as example queries. In Slack every headline is a link to the publisher's page, not to a Google redirect.&lt;/p&gt;

&lt;h2&gt;
  
  
  The workflow
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Schedule (daily 7:30)
  → HTTP Request: Google News Scraper
  → Remove Duplicates (previous executions, by URL)
  → Code: build digest
  → Slack (+ optional e-mail)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  1. Search Google News
&lt;/h3&gt;

&lt;p&gt;An HTTP Request node calls the Actor through Apify's synchronous endpoint (&lt;code&gt;run-sync-get-dataset-items&lt;/code&gt;), which runs it and returns the results in one request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"queries"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;OpenAI&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Anthropic&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"edition"&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:en"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"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;"1d"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"maxArticlesPerSource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"includeFullText"&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="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 details that matter for monitoring:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Quotes&lt;/strong&gt; make a query an exact phrase. Without them, &lt;code&gt;Acme Corp&lt;/code&gt; also matches every article that mentions "acme" and "corp" separately.&lt;/li&gt;
&lt;li&gt;Google News &lt;strong&gt;operators&lt;/strong&gt; work: &lt;code&gt;"Acme" -jobs&lt;/code&gt;, &lt;code&gt;intitle:acme&lt;/code&gt;, &lt;code&gt;site:techcrunch.com&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;edition&lt;/code&gt; sets country and language; 18 editions are supported (US, UK, Germany, France, Japan...).&lt;/li&gt;
&lt;li&gt;Every article comes back with &lt;code&gt;title&lt;/code&gt;, &lt;code&gt;source&lt;/code&gt;, &lt;code&gt;publishedAt&lt;/code&gt; and the &lt;strong&gt;original publisher URL&lt;/strong&gt;. Google News RSS only gives you &lt;code&gt;news.google.com/rss/articles/...&lt;/code&gt; redirect links; the Actor decodes them. That matters for the next step.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Authentication is an n8n Header Auth credential: &lt;code&gt;Authorization: Bearer &amp;lt;Apify token&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Only new articles
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;1d&lt;/code&gt; window overlaps from day to day, and big stories stay in the results for a while. The &lt;strong&gt;Remove Duplicates&lt;/strong&gt; node in "Remove items processed in previous executions" mode, keyed on &lt;code&gt;{{ $json.url }}&lt;/code&gt;, remembers every URL it has passed through and drops repeats. Because the URLs are the real publisher URLs, the key is stable.&lt;/p&gt;

&lt;p&gt;Two caveats:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The first run has an empty history, so it posts everything.&lt;/li&gt;
&lt;li&gt;The node keeps a limited history (I set 50,000 keys). If it ever fills up, n8n stops with "exceeds the maximum history size"; raise the limit or clear the history.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Build one message
&lt;/h3&gt;

&lt;p&gt;I don't want 20 Slack notifications. A Code node groups the articles by query, sorts each group newest first, caps it at 10 and builds Slack mrkdwn plus an HTML version for e-mail:&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;groups&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="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;articles&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;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;topic&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Other&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/"/g&lt;/span&gt;&lt;span class="p"&gt;,&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="nx"&gt;groups&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;groups&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&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;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// ... `- &amp;lt;${a.url}|${title}&amp;gt; (${source})` per article&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If there are no new articles the node returns an empty array, so no message is sent. Quiet days stay quiet.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Slack and e-mail
&lt;/h3&gt;

&lt;p&gt;A standard Slack node posts &lt;code&gt;{{ $json.text }}&lt;/code&gt; to &lt;code&gt;#news-alerts&lt;/code&gt;. There is also a disabled Send Email node wired to the same output; enable it and add SMTP credentials to get the digest by mail as well.&lt;/p&gt;

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

&lt;p&gt;The Actor charges &lt;strong&gt;$0.0025 per article&lt;/strong&gt; (plus $0.0025 per article if you turn on full-text extraction and text was found). With two queries and a cap of 10 articles each, the worst case is 20 articles a day, about &lt;strong&gt;$1.50 a month&lt;/strong&gt;. Quiet brands cost less. Note that duplicates are dropped in n8n &lt;em&gt;after&lt;/em&gt; the run, so they are still paid for; set &lt;code&gt;maxArticlesPerSource&lt;/code&gt; near the number of articles you realistically expect per day.&lt;/p&gt;

&lt;p&gt;Apify's free plan includes $5 per month, enough for this and a few more workflows.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ideas
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Summaries and sentiment&lt;/strong&gt;: set &lt;code&gt;includeFullText: true&lt;/code&gt; and add an LLM step: "Summarise in two sentences and rate the sentiment towards our brand."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tiered alerts&lt;/strong&gt;: a Filter node that pings &lt;code&gt;@channel&lt;/code&gt; only when &lt;code&gt;source&lt;/code&gt; is Reuters, Bloomberg or the FT.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Industry radar&lt;/strong&gt;: add &lt;code&gt;"topics": ["TECHNOLOGY"]&lt;/code&gt; to get the top headlines of whole sections.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Several markets&lt;/strong&gt;: duplicate the HTTP node with &lt;code&gt;"edition": "DE:de"&lt;/code&gt; and merge.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Archive&lt;/strong&gt;: append every new article to Google Sheets for monthly reporting.&lt;/li&gt;
&lt;/ul&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/2-daily-news-alert" rel="noopener noreferrer"&gt;https://github.com/ichkifokkus/data-recipes/tree/main/2-daily-news-alert&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Actor: &lt;a href="https://apify.com/fast_data_asap/google-news-scraper" rel="noopener noreferrer"&gt;https://apify.com/fast_data_asap/google-news-scraper&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Questions or ideas for the next recipe? Leave a comment.&lt;/p&gt;

</description>
      <category>n8n</category>
      <category>automation</category>
      <category>slack</category>
      <category>news</category>
    </item>
    <item>
      <title>Summarise any YouTube channel into Google Sheets with n8n (free workflow)</title>
      <dc:creator>Fast Data</dc:creator>
      <pubDate>Tue, 20 Oct 2026 09:40:30 +0000</pubDate>
      <link>https://dev.to/fast_data_asap/summarise-any-youtube-channel-into-google-sheets-with-n8n-free-workflow-20j6</link>
      <guid>https://dev.to/fast_data_asap/summarise-any-youtube-channel-into-google-sheets-with-n8n-free-workflow-20j6</guid>
      <description>&lt;p&gt;I follow more YouTube channels than I have time to watch. Some of them I only need to &lt;em&gt;know about&lt;/em&gt;: what did a competitor announce, which tools did a creator recommend, what topic is a channel pushing this month. So I built a small n8n workflow that does the watching for me: every Monday it takes the newest videos of a channel, summarises them with an LLM and adds one row per video to a Google Sheet.&lt;/p&gt;

&lt;p&gt;This post walks through the workflow. You can import it as-is; the JSON is linked at the end.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Disclosure:&lt;/strong&gt; the transcripts come from the YouTube Transcript Scraper on Apify, which I built. I'll be clear about what it costs and where its limits are.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you end up with
&lt;/h2&gt;

&lt;p&gt;A sheet with one row per new video:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;date&lt;/th&gt;
&lt;th&gt;channel&lt;/th&gt;
&lt;th&gt;title&lt;/th&gt;
&lt;th&gt;url&lt;/th&gt;
&lt;th&gt;minutes&lt;/th&gt;
&lt;th&gt;words&lt;/th&gt;
&lt;th&gt;summary&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2026-10-20&lt;/td&gt;
&lt;td&gt;@Fireship&lt;/td&gt;
&lt;td&gt;The runtime wars are over... and Node won&lt;/td&gt;
&lt;td&gt;youtube.com/watch?v=l3L_lETiSzc&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;1155&lt;/td&gt;
&lt;td&gt;TL;DR + 3-5 key points + a quote&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;After a few weeks you have a searchable archive of everything the channel said, which is far easier to skim than a list of video titles.&lt;/p&gt;

&lt;h2&gt;
  
  
  The workflow
&lt;/h2&gt;

&lt;p&gt;Seven nodes, all built in:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Schedule Trigger&lt;/strong&gt;: every Monday, 7:00.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTTP Request&lt;/strong&gt;: runs the transcript Actor and waits for the result.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Filter&lt;/strong&gt;: keeps videos that actually have a transcript.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Remove Duplicates&lt;/strong&gt;: mode "remove items processed in previous executions", keyed on &lt;code&gt;videoId&lt;/code&gt;. This is the piece that makes the workflow idempotent: a video is summarised once, even if it is still among the newest videos next week.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Basic LLM Chain&lt;/strong&gt; with an &lt;strong&gt;OpenAI Chat Model&lt;/strong&gt; (swap in Anthropic, Gemini or Ollama if you prefer).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Edit Fields&lt;/strong&gt;: picks the columns.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google Sheets&lt;/strong&gt;: append row.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Getting the transcripts
&lt;/h3&gt;

&lt;p&gt;The HTTP Request node calls Apify's synchronous run 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://api.apify.com/v2/acts/fast_data_asap~youtube-transcript-scraper/run-sync-get-dataset-items
Authorization: Bearer &amp;lt;your Apify token&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;with this body:&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;"startUrls"&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://www.youtube.com/@Fireship/videos"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"maxVideosPerSource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"languages"&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;"en"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"includeSegments"&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="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 endpoint starts the Actor, waits until it is done and returns the dataset as a JSON array. n8n turns every element into an item, so the rest of the workflow simply works on "one item per video".&lt;/p&gt;

&lt;p&gt;A channel URL is enough: the Actor resolves the channel's video list, takes the newest &lt;code&gt;maxVideosPerSource&lt;/code&gt; videos and returns for each one &lt;code&gt;title&lt;/code&gt;, &lt;code&gt;url&lt;/code&gt;, &lt;code&gt;durationSeconds&lt;/code&gt;, &lt;code&gt;wordCount&lt;/code&gt;, a &lt;code&gt;status&lt;/code&gt; and the full &lt;code&gt;text&lt;/code&gt;. I switch off timestamped segments here because the summary only needs plain text.&lt;/p&gt;

&lt;p&gt;For the token, I use an n8n &lt;strong&gt;Header Auth&lt;/strong&gt; credential (&lt;code&gt;Authorization&lt;/code&gt; / &lt;code&gt;Bearer &amp;lt;token&amp;gt;&lt;/code&gt;) instead of putting the token in the URL. Credentials are encrypted in n8n; the URL ends up in execution logs.&lt;/p&gt;

&lt;p&gt;Why the HTTP Request node and not the Apify node? There is an Apify integration for n8n and it works. I went with HTTP Request because it runs on every n8n instance without installing anything, and you can see exactly what is being sent.&lt;/p&gt;

&lt;h3&gt;
  
  
  The prompt
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Summarize this YouTube video for a busy reader.

Title: {{ $json.title }}

Write:
1. A one-sentence TL;DR.
2. Three to five key points as a bulleted list.
3. One notable quote from the transcript, if there is one.

Plain text, no headings. Transcript:
{{ $json.text.slice(0, 60000) }}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;slice(0, 60000)&lt;/code&gt; is a safety net: about an hour of speech. Without it, a three-hour podcast episode would quietly cost ten times more than the rest of the week combined.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it costs
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Transcripts: &lt;strong&gt;$0.002 per video&lt;/strong&gt; that has a transcript. Videos without subtitles come back with a status like &lt;code&gt;transcripts_disabled&lt;/code&gt; and are not charged.&lt;/li&gt;
&lt;li&gt;As shipped (3 newest videos per week): about $0.006 per week on the Apify side.&lt;/li&gt;
&lt;li&gt;Backfilling a whole channel once, say 100 videos: $0.20.&lt;/li&gt;
&lt;li&gt;LLM: a 10-minute video is roughly 2,000 input tokens, so with a small model you are looking at fractions of a cent per video.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Apify's free plan includes $5 of usage per month, which is 2,500 transcripts, so you can run this for a long time without paying anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  Things I learned building it
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Deduplicate after filtering, not before.&lt;/strong&gt; If a video has no transcript yet (auto-captions sometimes appear a few hours after upload), you want next week's run to try again. Filtering first means only videos that were really summarised are remembered.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep runs small.&lt;/strong&gt; The synchronous endpoint waits up to 300 seconds. For a weekly job that is plenty; for a one-time backfill of hundreds of videos, run the Actor directly in Apify and import the dataset.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Log the source.&lt;/strong&gt; For channel inputs the workflow writes the channel URL you put in (&lt;code&gt;source&lt;/code&gt;) into the &lt;code&gt;channel&lt;/code&gt; column, so rows from several channels stay easy to filter.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Variations
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Several channels&lt;/strong&gt;: add more URLs to &lt;code&gt;startUrls&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Topic monitoring&lt;/strong&gt;: replace &lt;code&gt;startUrls&lt;/code&gt; with &lt;code&gt;"searchQueries": ["n8n tutorial"]&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Notion instead of Sheets&lt;/strong&gt;: replace the last node with Notion → create database page.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RAG&lt;/strong&gt;: set &lt;code&gt;"chunkSizeChars": 1500&lt;/code&gt;; every result then includes &lt;code&gt;chunks&lt;/code&gt; with start and end times, ready for a vector store node. Citations can link straight to the timestamp in the video.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repurposing&lt;/strong&gt;: change the prompt to "write a LinkedIn post in my voice" and you have a content pipeline.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Get the workflow
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Workflow JSON and step-by-step setup: &lt;a href="https://github.com/ichkifokkus/data-recipes/tree/main/1-youtube-channel-ai-summaries" rel="noopener noreferrer"&gt;https://github.com/ichkifokkus/data-recipes/tree/main/1-youtube-channel-ai-summaries&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The Actor: &lt;a href="https://apify.com/fast_data_asap/youtube-transcript-scraper" rel="noopener noreferrer"&gt;https://apify.com/fast_data_asap/youtube-transcript-scraper&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Import the JSON (Workflows → Import from File), add the three credentials (Apify, OpenAI, Google), change the channel URL and click &lt;em&gt;Execute workflow&lt;/em&gt;. If you build something on top of it, I'd love to hear about it in the comments.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Transcripts belong to their creators. Use the summaries for research and personal knowledge management, and respect copyright and YouTube's terms.&lt;/em&gt;&lt;/p&gt;

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