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.

Adam Ben-Ayoun

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 setCalls per tileBusinesses reached
250 keywords25085.27%recommended
307 keywords30787.15%covers all top 500 categories
500 categories, exact50081.53%more calls, less coverage
every category~1,90097.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. 1.store
  2. 2.restaurant
  3. 3.contractor
  4. 4.clinic
  5. 5.insurance agency
  6. 6.salon
  7. 7.doctor
  8. 8.lodging
  9. 9.consultant
  10. 10.church
  11. 11.school
  12. 12.station
  13. 13.physician
  14. 14.agent
  15. 15.repair service
  16. 16.dentist
  17. 17.bar
  18. 18.attorney
  19. 19.therapist
  20. 20.auto repair shop
  21. 21.real estate agency
  22. 22.government office
  23. 23.atm
  24. 24.spa
  25. 25.surgeon
  26. 26.manufacturer
  27. 27.storage facility
  28. 28.park
  29. 29.internist
  30. 30.public
  31. 31.construction company
  32. 32.car dealer
  33. 33.cottage
  34. 34.remodeler
  35. 35.apartment building
  36. 36.counselor
  37. 37.farm
  38. 38.builder
  39. 39.coffee shop
  40. 40.bank
  41. 41.attraction
  42. 42.gift shop
  43. 43.practitioner
  44. 44.caterer
  45. 45.non-profit organization
  46. 46.barber shop
  47. 47.bakery
  48. 48.preparation
  49. 49.chiropractor
  50. 50.shipping and mailing service
  51. 51.psychologist
  52. 52.landscaper
  53. 53.financial planner
  54. 54.mental health service
  55. 55.wholesaler
  56. 56.pediatrician
  57. 57.apartment complex
  58. 58.maintenance
  59. 59.hotel
  60. 60.tire shop
  61. 61.pharmacy
  62. 62.lender
  63. 63.cafe
  64. 64.trainer
  65. 65.plumber
  66. 66.home health care service
  67. 67.electrician
  68. 68.gym
  69. 69.optometrist
  70. 70.sandwich shop
  71. 71.delivery
  72. 72.event venue
  73. 73.investment service
  74. 74.day care center
  75. 75.trucking company
  76. 76.gynecologist
  77. 77.photographer
  78. 78.marketing agency
  79. 79.wash
  80. 80.psychiatrist
  81. 81.preschool
  82. 82.accountant
  83. 83.cardiologist
  84. 84.community
  85. 85.money order service
  86. 86.auto body shop
  87. 87.law firm
  88. 88.lawn care service
  89. 89.psychotherapist
  90. 90.handyperson
  91. 91.oil change service
  92. 92.hospital
  93. 93.house cleaning service
  94. 94.ice cream shop
  95. 95.veterinarian
  96. 96.painter
  97. 97.printer
  98. 98.warehouse
  99. 99.painting
  100. 100.groomer
  101. 101.mechanic
  102. 102.wellness center
  103. 103.print shop
  104. 104.money transfer service
  105. 105.florist
  106. 106.fitness center
  107. 107.cemetery
  108. 108.legal services
  109. 109.brake shop
  110. 110.logistics service
  111. 111.social services organization
  112. 112.tobacco shop
  113. 113.property management company
  114. 114.apartment rental agency
  115. 115.employment agency
  116. 116.website designer
  117. 117.medical center
  118. 118.laboratory
  119. 119.transportation service
  120. 120.car detailing service
  121. 121.campground
  122. 122.loan agency
  123. 123.corporate office
  124. 124.sign shop
  125. 125.supermarket
  126. 126.tree service
  127. 127.gallery
  128. 128.waxing hair removal service
  129. 129.pressure washing service
  130. 130.association / organization
  131. 131.physical fitness program
  132. 132.shipping service
  133. 133.ophthalmologist
  134. 134.deli
  135. 135.development
  136. 136.maker
  137. 137.assisted living facility
  138. 138.towing service
  139. 139.event planner
  140. 140.graphic designer
  141. 141.boutique
  142. 142.machine shop
  143. 143.business to business service
  144. 144.truck dealer
  145. 145.architect
  146. 146.post office
  147. 147.van rental agency
  148. 148.pest control service
  149. 149.fabricator
  150. 150.truck rental agency
  151. 151.landscape designer
  152. 152.interior designer
  153. 153.hairdresser
  154. 154.internet marketing service
  155. 155.advertising agency
  156. 156.museum
  157. 157.podiatrist
  158. 158.truck repair shop
  159. 159.radiologist
  160. 160.carpet cleaning service
  161. 161.bookkeeping service
  162. 162.trailer rental service
  163. 163.educational institution
  164. 164.financial institution
  165. 165.propane supplier
  166. 166.dessert shop
  167. 167.mortgage broker
  168. 168.telecommunications service provider
  169. 169.eye care center
  170. 170.garden center
  171. 171.dermatologist
  172. 172.neurologist
  173. 173.waste management service
  174. 174.party equipment rental service
  175. 175.moving and storage service
  176. 176.gastroenterologist
  177. 177.vacation home rental agency
  178. 178.software company
  179. 179.periodontist
  180. 180.travel agency
  181. 181.oncologist
  182. 182.anesthesiologist
  183. 183.weight loss service
  184. 184.janitorial service
  185. 185.wedding venue
  186. 186.business center
  187. 187.welder
  188. 188.tattoo shop
  189. 189.industrial equipment supplier
  190. 190.orthodontist
  191. 191.rehabilitation center
  192. 192.mall
  193. 193.computer support and services
  194. 194.cleaners
  195. 195.wheel alignment service
  196. 196.water damage restoration service
  197. 197.emergency dental service
  198. 198.funeral home
  199. 199.internet service provider
  200. 200.equipment rental agency
  201. 201.building materials supplier
  202. 202.recycling center
  203. 203.accounting firm
  204. 204.transmission shop
  205. 205.religious organization
  206. 206.window installation service
  207. 207.insurance broker
  208. 208.safety equipment supplier
  209. 209.lab
  210. 210.yoga studio
  211. 211.developer
  212. 212.optician
  213. 213.motel
  214. 214.nursing home
  215. 215.nursery
  216. 216.teeth whitening service
  217. 217.laundromat
  218. 218.urologist
  219. 219.processor
  220. 220.produce market
  221. 221.union
  222. 222.cleaner
  223. 223.contact lenses supplier
  224. 224.inspector
  225. 225.tutoring service
  226. 226.car rental agency
  227. 227.tool rental service
  228. 228.surgical center
  229. 229.stock broker
  230. 230.photography studio
  231. 231.window cleaning service
  232. 232.lawyer
  233. 233.insurance company
  234. 234.pulmonologist
  235. 235.gun shop
  236. 236.finance broker
  237. 237.auto tune up service
  238. 238.urgent care center
  239. 239.child care agency
  240. 240.computer service
  241. 241.jeweler
  242. 242.video production service
  243. 243.worker
  244. 244.locksmith
  245. 245.photography service
  246. 246.auto electrical service
  247. 247.gutter cleaning service
  248. 248.medical office
  249. 249.landmark
  250. 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

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 LinkedIn

Start 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.

APIs by Category

Didn't find the API you are looking for? Request an API

© 2026 OpenWeb Ninja. All rights reserved.

G2 LogoTrustpilot LogoGitHub