Tutorial
Updated August 2026 · 11 min read
How to Scrape All Google Maps Businesses in a Certain Area
Not all the pizzerias, all the businesses. That means sweeping a keyword set across a tiled area. Here is the 250-keyword set we measured at 85% coverage of 17.1 million US listings, and the code to run it.

Author
Adam Ben-Ayoun
CTO · OpenWeb Ninja
Google Maps · Local Business · Python · Node.js
Key Takeaways
- One query never returns everything. Split on geography (tiles) and category (keywords) at the same time.
- 250 consolidated keywords reach 85.27% of businesses. The 500 most common categories searched exactly reach only 81.53%, using twice the calls.
- 98.78% is the hard ceiling. 1.22% of listings carry no category, so no keyword set reaches them.
- Deduplicate by business_id. Overlapping tiles are the point, duplicates are the cost.
Two axes, not one
Our earlier guide on getting all businesses of a type in an area solves one axis: tile a bounding box so no single query has to return more results than Google is willing to give. That works perfectly when you already know the category.
“All businesses” adds the second axis. There is no wildcard category on Google Maps, and an empty query does not enumerate a neighbourhood. You have to ask for something, which means the completeness of your dataset is bounded by the completeness of your keyword list. Tiles fix truncation; keywords fix category blindness. You need both.
What each keyword set actually covers
We aggregated the categories on 17,112,704 US business listings and measured, for each candidate keyword set, how many distinct businesses hold at least one category the set would match. A business with several categories counts once.
| Keyword set | Calls per tile | Businesses reached | |
|---|---|---|---|
| 250 keywords | 250 | 85.27% | recommended |
| 307 keywords | 307 | 87.15% | covers all top 500 categories |
| 500 categories, exact | 500 | 81.53% | more calls, less coverage |
| every category | ~1,900 | 97.9% | diminishing returns |
The interesting row is the last-but-one. Searching the 500 most common categories exactly is strictly worse than searching 250 consolidated keywords: more calls, less coverage. The reason is that Google Maps matches a keyword against related categories, so dentist pulls in Cosmetic dentist, Pediatric dentist and Dental implants provider, while the exact category Dentist pulls in only itself. Consolidating up the tree is free coverage.
The 250 keywords
Built from the 500 most common Google Maps categories, consolidated so each keyword subsumes as many related categories as possible, then ordered greedily by how many new businesses it adds. That ordering is the useful part: each keyword is the one that added the most businesses not already reached by the keywords above it, so a prefix of the list is always the best set of that size, and you can run a short prefix first to size a job before paying for the full sweep.
- 1.store
- 2.restaurant
- 3.contractor
- 4.clinic
- 5.insurance agency
- 6.salon
- 7.doctor
- 8.lodging
- 9.consultant
- 10.church
- 11.school
- 12.station
- 13.physician
- 14.agent
- 15.repair service
- 16.dentist
- 17.bar
- 18.attorney
- 19.therapist
- 20.auto repair shop
- 21.real estate agency
- 22.government office
- 23.atm
- 24.spa
- 25.surgeon
- 26.manufacturer
- 27.storage facility
- 28.park
- 29.internist
- 30.public
- 31.construction company
- 32.car dealer
- 33.cottage
- 34.remodeler
- 35.apartment building
- 36.counselor
- 37.farm
- 38.builder
- 39.coffee shop
- 40.bank
- 41.attraction
- 42.gift shop
- 43.practitioner
- 44.caterer
- 45.non-profit organization
- 46.barber shop
- 47.bakery
- 48.preparation
- 49.chiropractor
- 50.shipping and mailing service
- 51.psychologist
- 52.landscaper
- 53.financial planner
- 54.mental health service
- 55.wholesaler
- 56.pediatrician
- 57.apartment complex
- 58.maintenance
- 59.hotel
- 60.tire shop
- 61.pharmacy
- 62.lender
- 63.cafe
- 64.trainer
- 65.plumber
- 66.home health care service
- 67.electrician
- 68.gym
- 69.optometrist
- 70.sandwich shop
- 71.delivery
- 72.event venue
- 73.investment service
- 74.day care center
- 75.trucking company
- 76.gynecologist
- 77.photographer
- 78.marketing agency
- 79.wash
- 80.psychiatrist
- 81.preschool
- 82.accountant
- 83.cardiologist
- 84.community
- 85.money order service
- 86.auto body shop
- 87.law firm
- 88.lawn care service
- 89.psychotherapist
- 90.handyperson
- 91.oil change service
- 92.hospital
- 93.house cleaning service
- 94.ice cream shop
- 95.veterinarian
- 96.painter
- 97.printer
- 98.warehouse
- 99.painting
- 100.groomer
- 101.mechanic
- 102.wellness center
- 103.print shop
- 104.money transfer service
- 105.florist
- 106.fitness center
- 107.cemetery
- 108.legal services
- 109.brake shop
- 110.logistics service
- 111.social services organization
- 112.tobacco shop
- 113.property management company
- 114.apartment rental agency
- 115.employment agency
- 116.website designer
- 117.medical center
- 118.laboratory
- 119.transportation service
- 120.car detailing service
- 121.campground
- 122.loan agency
- 123.corporate office
- 124.sign shop
- 125.supermarket
- 126.tree service
- 127.gallery
- 128.waxing hair removal service
- 129.pressure washing service
- 130.association / organization
- 131.physical fitness program
- 132.shipping service
- 133.ophthalmologist
- 134.deli
- 135.development
- 136.maker
- 137.assisted living facility
- 138.towing service
- 139.event planner
- 140.graphic designer
- 141.boutique
- 142.machine shop
- 143.business to business service
- 144.truck dealer
- 145.architect
- 146.post office
- 147.van rental agency
- 148.pest control service
- 149.fabricator
- 150.truck rental agency
- 151.landscape designer
- 152.interior designer
- 153.hairdresser
- 154.internet marketing service
- 155.advertising agency
- 156.museum
- 157.podiatrist
- 158.truck repair shop
- 159.radiologist
- 160.carpet cleaning service
- 161.bookkeeping service
- 162.trailer rental service
- 163.educational institution
- 164.financial institution
- 165.propane supplier
- 166.dessert shop
- 167.mortgage broker
- 168.telecommunications service provider
- 169.eye care center
- 170.garden center
- 171.dermatologist
- 172.neurologist
- 173.waste management service
- 174.party equipment rental service
- 175.moving and storage service
- 176.gastroenterologist
- 177.vacation home rental agency
- 178.software company
- 179.periodontist
- 180.travel agency
- 181.oncologist
- 182.anesthesiologist
- 183.weight loss service
- 184.janitorial service
- 185.wedding venue
- 186.business center
- 187.welder
- 188.tattoo shop
- 189.industrial equipment supplier
- 190.orthodontist
- 191.rehabilitation center
- 192.mall
- 193.computer support and services
- 194.cleaners
- 195.wheel alignment service
- 196.water damage restoration service
- 197.emergency dental service
- 198.funeral home
- 199.internet service provider
- 200.equipment rental agency
- 201.building materials supplier
- 202.recycling center
- 203.accounting firm
- 204.transmission shop
- 205.religious organization
- 206.window installation service
- 207.insurance broker
- 208.safety equipment supplier
- 209.lab
- 210.yoga studio
- 211.developer
- 212.optician
- 213.motel
- 214.nursing home
- 215.nursery
- 216.teeth whitening service
- 217.laundromat
- 218.urologist
- 219.processor
- 220.produce market
- 221.union
- 222.cleaner
- 223.contact lenses supplier
- 224.inspector
- 225.tutoring service
- 226.car rental agency
- 227.tool rental service
- 228.surgical center
- 229.stock broker
- 230.photography studio
- 231.window cleaning service
- 232.lawyer
- 233.insurance company
- 234.pulmonologist
- 235.gun shop
- 236.finance broker
- 237.auto tune up service
- 238.urgent care center
- 239.child care agency
- 240.computer service
- 241.jeweler
- 242.video production service
- 243.worker
- 244.locksmith
- 245.photography service
- 246.auto electrical service
- 247.gutter cleaning service
- 248.medical office
- 249.landmark
- 250.otolaryngologist
Sweeping keywords across tiles
The pattern is a nested loop: for every tile, for every keyword, page through results and deduplicate by business_id. This uses /area-search-by-bounding-box on the Local Business Data API, which works in every country because it takes coordinates rather than postal codes.
# sweep_area.py -- every business in a bounding box
import os, requests, itertools, json
API_KEY = os.environ["OWN_API_KEY"]
URL = "https://api.openwebninja.com/local-business-data/area-search-by-bounding-box"
# load the 250 keywords (one per line)
KEYWORDS = [l.strip() for l in open("keywords.txt") if l.strip()]
def tiles(bottom_left, top_right, steps):
"""Split a bounding box into steps x steps sub-boxes."""
(lat1, lng1), (lat2, lng2) = bottom_left, top_right
dlat = (lat2 - lat1) / steps
dlng = (lng2 - lng1) / steps
for i, j in itertools.product(range(steps), range(steps)):
yield ((lat1 + i * dlat, lng1 + j * dlng),
(lat1 + (i + 1) * dlat, lng1 + (j + 1) * dlng))
def search(keyword, bl, tr, limit=20):
r = requests.get(URL, headers={"X-API-Key": API_KEY}, timeout=60, params={
"query": keyword,
"bottom_left": f"{bl[0]},{bl[1]}",
"top_right": f"{tr[0]},{tr[1]}",
"limit": limit,
})
if r.status_code != 200:
print(f" {keyword}: HTTP {r.status_code}")
return []
return r.json().get("data", [])
# Austin, TX
BL, TR = (30.10, -97.94), (30.52, -97.56)
businesses = {}
for n, (bl, tr) in enumerate(tiles(BL, TR, steps=4), 1):
print(f"tile {n}: {bl} -> {tr}")
for keyword in KEYWORDS:
for b in search(keyword, bl, tr):
# dedupe: adjacent tiles and related keywords both return overlaps
businesses.setdefault(b["business_id"], b)
print(f" running total: {len(businesses):,}")
with open("austin_businesses.json", "w") as f:
json.dump(list(businesses.values()), f, indent=2)
print(f"done: {len(businesses):,} unique businesses")Two things to tune before running the whole set. Raise steps until a single tile stops returning a full page for your densest keywords, which is the signal that truncation is still costing you results. And run the first 40 keywords only, to get a real businesses-per-call number for your area before committing to 250.
// sweep-area.mjs (Node 18+, native fetch)
import { readFileSync, writeFileSync } from 'node:fs'
const API_KEY = process.env.OWN_API_KEY
const URL = 'https://api.openwebninja.com/local-business-data/area-search-by-bounding-box'
const KEYWORDS = readFileSync('keywords.txt', 'utf8').split('\n').map(s => s.trim()).filter(Boolean)
function* tiles([lat1, lng1], [lat2, lng2], steps) {
const dLat = (lat2 - lat1) / steps
const dLng = (lng2 - lng1) / steps
for (let i = 0; i < steps; i++)
for (let j = 0; j < steps; j++)
yield [[lat1 + i * dLat, lng1 + j * dLng], [lat1 + (i + 1) * dLat, lng1 + (j + 1) * dLng]]
}
async function search(query, bl, tr, limit = 20) {
const params = new URLSearchParams({
query,
bottom_left: `${bl[0]},${bl[1]}`,
top_right: `${tr[0]},${tr[1]}`,
limit: String(limit),
})
const res = await fetch(`${URL}?${params}`, { headers: { 'X-API-Key': API_KEY } })
if (!res.ok) { console.log(` ${query}: HTTP ${res.status}`); return [] }
return (await res.json()).data ?? []
}
const businesses = new Map()
let tile = 0
for (const [bl, tr] of tiles([30.10, -97.94], [30.52, -97.56], 4)) {
console.log(`tile ${++tile}`)
for (const keyword of KEYWORDS) {
for (const b of await search(keyword, bl, tr)) {
if (!businesses.has(b.business_id)) businesses.set(b.business_id, b)
}
}
console.log(` running total: ${businesses.size}`)
}
writeFileSync('austin_businesses.json', JSON.stringify([...businesses.values()], null, 2))
console.log(`done: ${businesses.size} unique businesses`)Budgeting the sweep
Cost is keywords multiplied by tiles, so the tile count is the lever that matters. A 4x4 grid over a mid-sized city with the full 250 keywords is 4,000 calls. The same city at 8x8 is 16,000. Both return nearly the same businesses if the city is not dense, which is why it pays to find the tile size empirically rather than splitting uniformly and hoping.
If you need to trim, trim keywords rather than tiles. The list is ordered by marginal yield, so the last 100 keywords contribute far less than the first 40, while dropping tiles loses whole neighbourhoods.
FAQ
About the author

Adam Ben-Ayoun
CTO @ OpenWeb Ninja
Adam leads engineering at OpenWeb Ninja, building the APIs and infrastructure that make public web data accessible to developers and AI agents.
Connect on LinkedInStart building your business dataset
The Local Business Data API searches Google Maps by keyword, bounding box, or zip code and returns names, addresses, phones, ratings, and more. Free plan includes 100 requests/month.
Didn't find the API you are looking for? Request an API
