n8n Google Places API: Auto-Save Businesses to Sheets

n8n Google Places API: Auto-Save Businesses to Sheets

Slug: n8n-google-places-search-workflow
Focus Keyphrase: n8n Google Places API


Hey everyone 👋

If you’ve ever needed to find local businesses automatically — say, “cafes in New York” or “law firms in London” — and save all that data neatly into Google Sheets, then this post is going to be your new favorite.

I recently built an n8n workflow that does exactly that:
👉 takes a location and an industry,
👉 queries the Google Places API,
👉 extracts the business info (like name, address, phone, rating, etc.), and
👉 saves everything automatically into a Google Sheet.

No manual copy-paste, no spreadsheets mess — just pure automation.

Let’s break it down step by step 👇


💡 What This Workflow Does

Here’s what happens behind the scenes when you trigger it:

  1. You fill out a simple form (Location + Industry).
  2. The form sends data to n8n.
  3. n8n makes a POST request to Google Places API with your query.
  4. It extracts details like name, address, rating, phone, website, etc.
  5. It loops through each result.
  6. And finally, it appends or updates the data into a Google Sheet.

That’s it — within seconds, you’ve got a structured business list ready to go.


🧩 The n8n Workflow Overview

Here’s what the workflow looks like inside n8n:

Nodes used:

  • 📝 Form Trigger → To collect user input (Location + Industry)
  • 🌍 HTTP Request (Google Places API) → To search for businesses
  • 🔁 Split in Batches / Loop → To handle multiple results
  • ⚙️ Function Node → To extract and format the data
  • 📄 Google Sheets Node → To save it neatly into your spreadsheet

Let’s walk through each one briefly.


🧭 1. Form Trigger — The Starting Point

I used n8n’s Form Trigger node to collect two fields:

  • Location
  • Industry

So when someone enters something like “Restaurants” in “New York”, it passes that data to the next node.


🌐 2. Google Places API Request

This node sends a POST request to the Google Places API endpoint:

https://places.googleapis.com/v1/places:searchText

It includes two important headers:

X-Goog-Api-Key: YOUR_API_KEY
X-Goog-FieldMask: places.id,places.displayName,places.formattedAddress,places.internationalPhoneNumber,places.rating,places.websiteUri

And the body:

{
  "textQuery": "{{ $json.Industry }} in {{ $json.Location }}"
}

So if someone entered “Cafes” and “Los Angeles”, it searches Cafes in Los Angeles.

Tip: Make sure you have the Google Places API enabled in your Google Cloud console and a valid API key.


🔄 3. Split In Batches (Looping Through Results)

Once the API returns multiple businesses, n8n loops through each one so that we can process them individually.
This ensures the workflow doesn’t choke on large datasets — it handles one at a time gracefully.


🧮 4. Extract Business Data (Function Node)

Here’s where the magic happens 👇

This small JavaScript snippet extracts and formats the API response:

const places = items[0].json.places || [];

return places.map(place => ({
  json: {
    id: place.id || null,
    name: place.displayName?.text || null,
    address: place.formattedAddress || null,
    phone: place.internationalPhoneNumber || null,
    website: place.websiteUri || null,
    rating: place.rating || null
  }
}));

Basically, it picks the key details we care about — ID, name, address, phone, website, and rating — and prepares them to be sent to Google Sheets.


📊 5. Save to Google Sheets

Finally, the Google Sheets node appends or updates each business in your sheet.

Here’s how the column mapping looks:

Sheet Columnn8n Field
ID{{$json.id}}
Name{{$json.name}}
Address{{$json.address}}
Phone{{$json.phone}}
Website{{$json.website}}
Rating{{$json.rating}}

You can use “Append or Update” so it won’t duplicate existing entries.

This workflow automatically syncs data — it’s like your own mini CRM for leads, businesses, or prospect research.

{
  "nodes": [
    {
      "parameters": {
        "method": "POST",
        "url": "https://places.googleapis.com/v1/places:searchText",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "X-Goog-Api-Key",
              "value": "<API_KEY>"
            },
            {
              "name": "X-Goog-FieldMask",
              "value": "places.id,places.displayName,places.formattedAddress,places.internationalPhoneNumber,places.rating,places.websiteUri"
            }
          ]
        },
        "sendBody": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "textQuery",
              "value": "={{ $json.Industry }} in {{ $json.Location }}"
            }
          ]
        },
        "options": {}
      },
      "id": "3c9a9767-e78a-49f4-8151-8429e99a2824",
      "name": "Google Places Search",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 3,
      "position": [
        -688,
        64
      ]
    },
    {
      "parameters": {
        "functionCode": "// Extract business data from Google Places API response (new format)\nconst places = items[0].json.places || [];\n\nreturn places.map(place => ({\n  json: {\n    id: place.id || null,\n    name: place.displayName?.text || null,\n    address: place.formattedAddress || null,\n    phone: place.internationalPhoneNumber || null,\n    website: place.websiteUri || null,\n    rating: place.rating || null\n  },\n  Language: place.languageCode || null\n}));\n"
      },
      "id": "ca63a61f-8fa5-484f-aa87-e9c2efd3d5b8",
      "name": "Extract Business Data",
      "type": "n8n-nodes-base.function",
      "typeVersion": 1,
      "position": [
        -48,
        48
      ]
    },
    {
      "parameters": {
        "batchSize": 1,
        "options": {}
      },
      "id": "26bcf278-e991-4022-8c2b-f78ca7824381",
      "name": "Split In Batches",
      "type": "n8n-nodes-base.splitInBatches",
      "typeVersion": 1,
      "position": [
        -240,
        48
      ]
    },
    {
      "parameters": {
        "operation": "appendOrUpdate",
        "documentId": {
          "__rl": true,
          "value": "<Goole_Sheet_ID_URL>",
          "mode": "url"
        },
        "sheetName": {
          "__rl": true,
          "value": "gid=0",
          "mode": "list",
          "cachedResultName": "Sheet1",
          "cachedResultUrl": "<Goole_Sheet_ID_URL>"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "ID": "={{ $json.id }}",
            "name": "={{ $json.name }}",
            "address": "={{ $json.address }}",
            "phone": "={{ $json.phone }}",
            "website": "={{ $json.website }}",
            "Rating": "={{ $json.rating }}"
          },
          "matchingColumns": [
            "ID"
          ],
          "schema": [
            {
              "id": "ID",
              "displayName": "ID",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "name",
              "displayName": "name",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "address",
              "displayName": "address",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "phone",
              "displayName": "phone",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "website",
              "displayName": "website",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Rating",
              "displayName": "Rating",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {}
      },
      "id": "85d4dce5-a46c-4913-8c79-ed683c246ea6",
      "name": "Google Sheets (Save Data)",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4,
      "position": [
        160,
        48
      ],
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "kgn6beRnGDwnBhfk",
          "name": "Google Sheets account"
        }
      }
    },
    {
      "parameters": {
        "options": {}
      },
      "type": "n8n-nodes-base.splitInBatches",
      "typeVersion": 3,
      "position": [
        -448,
        64
      ],
      "id": "a4d4a1b5-8377-42b0-b401-e8662ef662bb",
      "name": "Loop Over Items"
    },
    {
      "parameters": {},
      "type": "n8n-nodes-base.noOp",
      "name": "Replace Me",
      "typeVersion": 1,
      "position": [
        -320,
        224
      ],
      "id": "420e2fe4-368a-4759-9830-c48ffa567b66"
    },
    {
      "parameters": {
        "formTitle": "Search",
        "formFields": {
          "values": [
            {
              "fieldLabel": "Location"
            },
            {
              "fieldLabel": "Industry"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.formTrigger",
      "typeVersion": 2.3,
      "position": [
        -896,
        64
      ],
      "id": "e1c0e351-5d12-4d6b-8a35-d81f8e3369bd",
      "name": "On form submission",
      "webhookId": "fbb1894c-9fd4-4cf3-a1d4-f2da62bf44ba"
    }
  ],
  "connections": {
    "Google Places Search": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Business Data": {
      "main": [
        [
          {
            "node": "Google Sheets (Save Data)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split In Batches": {
      "main": [
        [
          {
            "node": "Extract Business Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop Over Items": {
      "main": [
        [
          {
            "node": "Split In Batches",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Replace Me",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Replace Me": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "On form submission": {
      "main": [
        [
          {
            "node": "Google Places Search",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "pinData": {},
  "meta": {
    "instanceId": "9f9ed819420789edfdfcb51a110cde7a611f70c85f4eb1924df805fc9bc5ff04"
  }
}

🧠 Real Use Cases

Here are a few cool ways you could use this:

  • 📍 Build a local business directory
  • 🏢 Generate leads for your marketing or sales team
  • 🍕 Create “Top X in City” lists for your website
  • 🧰 Enrich datasets with public business info
  • 🧾 Combine with AI (like OpenAI node) to summarize or categorize the results

⚙️ Things to Keep in Mind

  • You’ll need a Google Cloud API key with Places API enabled.
  • Add rate limits if you plan to do large queries.
  • Secure your webhook if the form trigger is public.
  • You can easily host this on your self-hosted n8n or n8n Cloud — both work great.

🚀 Final Thoughts

This little workflow shows how powerful n8n + Google APIs can be together.
With just a few nodes, you can go from user input → API → formatted data → Google Sheet — no coding required (well, except a small function node 😄).

It’s fast, flexible, and you own the entire process.

Now I’m curious…
👉 Have you built something similar in n8n?
👉 Do you prefer using n8n for data workflows, or do you still rely on Make.com or Zapier?

Drop your thoughts in the comments below — I’d love to feature some of your creative use cases on DevToolHub!

You Might Also Like

🛠️ Recommended Tools for Developers & Tech Pros

Save time, boost productivity, and work smarter with these AI-powered tools I personally use and recommend:

1️⃣ CopyOwl.ai – Research & Write Smarter
Write fully referenced reports, essays, or blogs in one click.
✅ 97% satisfaction • ✅ 10+ hrs saved/week • ✅ Academic citations

2️⃣ LoopCV.pro – Build a Job-Winning Resume
Create beautiful, ATS-friendly resumes in seconds — perfect for tech roles.
✅ One-click templates • ✅ PDF/DOCX export • ✅ Interview-boosting design

3️⃣ Speechify – Listen to Any Text
Turn articles, docs, or PDFs into natural-sounding audio — even while coding.
✅ 1,000+ voices • ✅ Works on all platforms • ✅ Used by 50M+ people

4️⃣ Jobright.ai – Automate Your Job Search
An AI job-search agent that curates roles, tailors resumes, finds referrers, and can apply for jobs—get interviews faster.
✅ AI agent, not just autofill – ✅ Referral insights – ✅ Faster, personalized matching

1 thought on “n8n Google Places API: Auto-Save Businesses to Sheets

  1. I must say this article is extremely well written, insightful, and packed with valuable knowledge that shows the author’s deep expertise on the subject, and I truly appreciate the time and effort that has gone into creating such high-quality content because it is not only helpful but also inspiring for readers like me who are always looking for trustworthy resources online. Keep up the good work and write more. i am a follower.

Comments are closed.