Keen Digital
← Back to Lab
7 July 2026

Lead gen: Places API billing fix

Manual prospecting works like this: search "[trade] [suburb]" on Google Maps, click through to the website, spend about a minute deciding if it's genuinely bad or missing — aim for a list of 50–100 qualified businesses before starting outreach. It works, but it doesn't scale past a suburb or two an evening.

The n8n workflow (leads-workflow-v10) automates exactly that checklist against the Google Places API. It's worth writing up because the Places API bills per call and per field, and the shape of the workflow is really a cost-control exercise wearing an automation costume.

The shape of it

  • Config sets the business type, region, and a comma-separated list of suburbs.
  • Generate Search Terms turns that into one search query per suburb (driving instructor Burleigh Heads, Gold Coast, QLD, Australia).
  • Places Text Search hits place/textsearch/json for each query and returns a page of results.
  • Filter by Review Count drops anything with 1000+ reviews — that's a franchise or national chain, not the owner-operated target.
  • Place Details looks up the survivors by place_id.
  • Skip If Exists loads the existing leads sheet and drops anything already logged, by business name, before doing anything expensive.
  • Businesses with a website get fetched and regex-scanned for a contact email; businesses without one skip straight through.
  • Everything lands in a Google Sheet with Status: To Contact.

Where the billing actually lives

Places Text Search is comparatively cheap. Place Details is the expensive call, and it's billed per field mask — the more fields you ask for, the higher the SKU tier you're charged at. Calling it with the default full response on every single text-search result, before any filtering, means paying the Details rate for businesses you're about to throw away anyway.

The fix is in the connection order, not the code: Filter by Review Count sits before Place Details in the graph, so the expensive call only ever runs on places that already passed the free filter. And the Details request itself asks for exactly six fields:

fields=name,website,formatted_phone_number,formatted_address,rating,user_ratings_total

— which pins it to the cheapest field-mask tier that still has everything the sheet needs. No fields you're not using, no upgrade to a pricier SKU by accident.

The other free filter: don't ask twice

Skip If Exists runs before the website check and email scrape, comparing the current batch against every business name already in the sheet (case-insensitive, deduped within the batch too). Run the workflow again next week for a new batch of suburbs and it never re-spends effort re-qualifying a business it already has — the only API cost that repeats is Text Search for the new suburbs themselves.

The pattern

With a metered API, the win isn't a smarter query — it's ordering the pipeline so the free, local filters (review count, "have I seen this business before") run first, and the metered call only ever fires on something that's already earned it.