<?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: Chris</title>
    <description>The latest articles on DEV Community by Chris (@chris_0_714d9b).</description>
    <link>https://dev.to/chris_0_714d9b</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%2F4163858%2Fdcccd86b-cabc-4056-bf62-69002a1fe003.png</url>
      <title>DEV Community: Chris</title>
      <link>https://dev.to/chris_0_714d9b</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/chris_0_714d9b"/>
    <language>en</language>
    <item>
      <title>Build a UK restaurant list by postcode with n8n and Google Sheets</title>
      <dc:creator>Chris</dc:creator>
      <pubDate>Mon, 05 Oct 2026 12:09:45 +0000</pubDate>
      <link>https://dev.to/chris_0_714d9b/build-a-uk-restaurant-list-by-postcode-with-n8n-and-google-sheets-5152</link>
      <guid>https://dev.to/chris_0_714d9b/build-a-uk-restaurant-list-by-postcode-with-n8n-and-google-sheets-5152</guid>
      <description>&lt;p&gt;A coffee roaster covering Manchester does not need a nationwide business database. A useful starting point is a list of restaurants and cafés in the postcode districts its drivers already serve, with a way to check for published changes later.&lt;/p&gt;

&lt;p&gt;This tutorial uses official Food Standards Agency records, an Apify Actor and n8n to maintain that list in Google Sheets. The example covers Manchester M1.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Disclosure:&lt;/strong&gt; I maintain the paid &lt;a href="https://apify.com/dig_stays/uk-hospitality-territory-monitor?utm_source=devto&amp;amp;utm_medium=tutorial&amp;amp;utm_campaign=hospitality_postcode_oct2026" rel="noopener noreferrer"&gt;UK Restaurant &amp;amp; Hospitality Territory Monitor&lt;/a&gt; used here. The underlying FSA data is publicly available; you can build directly against the &lt;a href="https://api.ratings.food.gov.uk/help" rel="noopener noreferrer"&gt;FSA API&lt;/a&gt; instead. The Actor packages filtering, pagination and comparison between scans.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the workflow produces
&lt;/h2&gt;

&lt;p&gt;It uses two tabs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Businesses:&lt;/strong&gt; one row per FSA business ID, containing the name, address, postcode, business type, hygiene rating, observation time and official source link.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Runs:&lt;/strong&gt; one row per Apify run, recording counts and delivery status. A successful scan with no changes still gets a log entry.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It does not collect email addresses or phone numbers. A newly seen record is not proof that a business has just opened. Missing records are not treated as confirmed closures, and this template does not delete rows.&lt;/p&gt;

&lt;h2&gt;
  
  
  Download the template
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://api.apify.com/v2/key-value-stores/Cfl7RG4BuTVimQA8y/records/hospitality-google-sheets.n8n.json?signature=1p4Ak11ep9v2RKlx7m5y3" rel="noopener noreferrer"&gt;Download the n8n workflow JSON&lt;/a&gt; and &lt;a href="https://api.apify.com/v2/key-value-stores/Cfl7RG4BuTVimQA8y/records/sheet-headers.txt?signature=1octY9nbiwROIY84q4x5H" rel="noopener noreferrer"&gt;the sheet headers&lt;/a&gt;. If your browser displays the JSON, save it as a .json file.&lt;/p&gt;

&lt;p&gt;The template contains no credentials and imports inactive. You need an n8n instance, an Apify account with an API token and sufficient credit, and a Google account that can edit the destination spreadsheet.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Prepare Google Sheets
&lt;/h2&gt;

&lt;p&gt;Create a spreadsheet with tabs named exactly &lt;strong&gt;Businesses&lt;/strong&gt; and &lt;strong&gt;Runs&lt;/strong&gt;. Copy each tab-separated header line from the header download into cell A1 of the corresponding tab. The names must match the template.&lt;/p&gt;

&lt;p&gt;Keep this as one territory per spreadsheet. Your own research notes can go in additional columns; the workflow maps only its specified columns.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Import and configure n8n
&lt;/h2&gt;

&lt;p&gt;Import the JSON into a new workflow. Open &lt;strong&gt;Configuration&lt;/strong&gt; and replace &lt;code&gt;REPLACE_WITH_YOUR_GOOGLE_SHEET_ID&lt;/code&gt; with the ID between &lt;code&gt;/d/&lt;/code&gt; and &lt;code&gt;/edit&lt;/code&gt; in your spreadsheet URL.&lt;/p&gt;

&lt;p&gt;The starting territory is:&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;"outputMode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"changes"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"localAuthorities"&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;"Manchester"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"postcodeDistricts"&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;"M1"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"businessTypeIds"&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="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;"maxRecords"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"resetBaseline"&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;Business type 1 is the FSA category Restaurant/Cafe/Canteen. The postcode filter uses the outward district: M1 is separate from M10.&lt;/p&gt;

&lt;p&gt;Leave &lt;code&gt;resumeRunId&lt;/code&gt; empty for normal runs. The template sets a maximum Actor charge of &lt;strong&gt;$4 per run&lt;/strong&gt; through &lt;code&gt;maxChargeUsd&lt;/code&gt;. This is a ceiling, not the expected cost. n8n hosting or subscription costs are separate.&lt;/p&gt;

&lt;p&gt;In a live Actor test on 5 October 2026, the Manchester M1 restaurant/café export returned &lt;strong&gt;322 records&lt;/strong&gt;. With the lower pricing introduced on 13 October 2026, the standard rate is &lt;strong&gt;$3 per 1,000 business records&lt;/strong&gt;, so that export would cost approximately &lt;strong&gt;$0.97&lt;/strong&gt;. Counts and pricing can change; check the Actor's Pricing tab before running. A limit too small for all matching records causes a safe failure rather than an incomplete monitoring baseline.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Connect credentials
&lt;/h2&gt;

&lt;p&gt;In n8n, create a &lt;strong&gt;Header Auth&lt;/strong&gt; credential with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Name: &lt;code&gt;Authorization&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Value: &lt;code&gt;Bearer YOUR_APIFY_TOKEN&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Select it on all four HTTP Request nodes: &lt;strong&gt;Start Apify run&lt;/strong&gt;, &lt;strong&gt;Get run status&lt;/strong&gt;, &lt;strong&gt;Read scan summary&lt;/strong&gt; and &lt;strong&gt;Download run records&lt;/strong&gt;. Use a token that can run this Actor and read its resulting run and storage records.&lt;/p&gt;

&lt;p&gt;Connect your Google Sheets OAuth credential to &lt;strong&gt;Update Businesses sheet&lt;/strong&gt; and &lt;strong&gt;Update Runs sheet&lt;/strong&gt;. Confirm that the account has edit access to your spreadsheet.&lt;/p&gt;

&lt;p&gt;Keep the token in credentials, not in the exported workflow or a public URL.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Run once and inspect the results
&lt;/h2&gt;

&lt;p&gt;Choose the manual trigger and execute the workflow. It starts an Actor run, waits for completion, checks the scan summary and downloads the completed dataset. It verifies the number of downloaded records before writing rows.&lt;/p&gt;

&lt;p&gt;For a new monitoring scope, the first successful run returns baseline records. Later runs with identical filters return newly seen or changed records. If you already ran that scope in the same Apify account, there may already be a baseline, so the first n8n execution can legitimately return zero changes.&lt;/p&gt;

&lt;p&gt;To fill a new sheet from an existing monitor, temporarily set &lt;code&gt;outputMode&lt;/code&gt; to &lt;code&gt;snapshot&lt;/code&gt;. That exports the current list without resetting saved monitoring history. Switch back to &lt;code&gt;changes&lt;/code&gt; afterwards. Snapshot mode charges for the full returned list on every run.&lt;/p&gt;

&lt;p&gt;The Businesses node uses &lt;strong&gt;Append or Update Row&lt;/strong&gt;, matching on &lt;code&gt;fhrsId&lt;/code&gt;. The Runs node matches on &lt;code&gt;runId&lt;/code&gt;. Values are written in RAW mode so source strings are not interpreted as spreadsheet formulas.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Recover a failed sheet write
&lt;/h2&gt;

&lt;p&gt;Apify can finish a scan and update its baseline before Google Sheets accepts the rows. Starting another scan immediately could therefore return no changes while the earlier results still need exporting.&lt;/p&gt;

&lt;p&gt;If a downstream step fails:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Pause the schedule and find the completed Apify run ID in the n8n execution.&lt;/li&gt;
&lt;li&gt;Put that ID into &lt;code&gt;resumeRunId&lt;/code&gt; in Configuration.&lt;/li&gt;
&lt;li&gt;Execute again. The workflow reads that saved run instead of starting a billable new scan.&lt;/li&gt;
&lt;li&gt;Confirm both tabs, clear &lt;code&gt;resumeRunId&lt;/code&gt;, then resume scheduling.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The matching keys allow a partially written run to be replayed without intentionally appending duplicate rows. Replay before newer runs; otherwise older values can overwrite newer ones. Keep one active monitor per filter scope and avoid overlapping executions. The Actor baseline is shared by runs with the same filters within that account, so another task checking the same scope can consume changes first.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Enable weekly checks
&lt;/h2&gt;

&lt;p&gt;After a successful manual test, publish the workflow to enable the included Monday 09:00 schedule. Its timezone is &lt;strong&gt;Europe/London&lt;/strong&gt;. Check n8n execution failures regularly or connect your existing error-notification workflow.&lt;/p&gt;

&lt;p&gt;Changing the territory creates a different comparison scope. Use a new sheet or deliberately rebuild your list when changing filters; old rows are not automatically removed.&lt;/p&gt;

&lt;h2&gt;
  
  
  What has been tested
&lt;/h2&gt;

&lt;p&gt;The Actor returned 322 records in the live M1 export. The template imported into &lt;strong&gt;n8n 2.41.7&lt;/strong&gt; and passed runtime scenarios for non-empty output, successful zero-change output, a failed Actor run and polling a running Actor. Nine additional checks covered mapping, incomplete downloads, duplicate IDs and recovery keys.&lt;/p&gt;

&lt;p&gt;Those workflow runtime tests used recorded Actor data and mocked Apify responses, with capture nodes replacing Google Sheets writes. &lt;strong&gt;Live Google OAuth and spreadsheet writes have not been verified in this template.&lt;/strong&gt; Run a manual check with your credentials before enabling its schedule.&lt;/p&gt;

&lt;p&gt;The workflow is intended for a bounded territory of up to 10,000 matching records, not an entire-country bulk export. Results retain FSA source links and Open Government Licence attribution. Treat the list as research material and verify a business before acting on it.&lt;/p&gt;

&lt;p&gt;If you try it, I would be interested in which part needs less setup: choosing the territory, connecting Sheets or understanding the first baseline.&lt;/p&gt;

&lt;p&gt;References: &lt;a href="https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.httprequest/" rel="noopener noreferrer"&gt;n8n HTTP Request&lt;/a&gt;, &lt;a href="https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.googlesheets/sheet-operations/" rel="noopener noreferrer"&gt;Google Sheets operations&lt;/a&gt; and &lt;a href="https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.scheduletrigger/" rel="noopener noreferrer"&gt;Schedule Trigger&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>automation</category>
      <category>tutorial</category>
      <category>api</category>
    </item>
  </channel>
</rss>
