DEV Community

Ben Barazandeh
Ben Barazandeh

Posted on

Local Business Leads Without Scraping Google Maps: An OpenStreetMap + Overpass Guide

Disclosure: I'm Ben, and I built the Apify Actor mentioned later in this post. I charge per result for it. Everything in the tutorial part works with free tools too.

If you sell to local businesses (web design, SEO, payments, insurance, supplies), you need lists like "every roofer in Denver with a phone number". Most people reach for a Google Maps scraper. That works, but Google's terms forbid scraping, and you aren't allowed to store or resell what you collect. That's a shaky foundation for a CRM you plan to keep for years.

There's an open alternative a lot of people overlook: OpenStreetMap.

Why OpenStreetMap?

OpenStreetMap (OSM) is the community-built world map behind thousands of apps. Its data is licensed under the Open Database License (ODbL). You can store it, reuse it and even resell it, as long as you:

  1. credit "© OpenStreetMap contributors" when you use it publicly, and
  2. keep any database you publish from it under the ODbL too (share-alike).

OSM is full of businesses: shops, restaurants, clinics, offices and tradespeople, tagged with things like amenity=dentist, craft=plumber or shop=bakery. Many have phone, website, opening_hours and sometimes email (or contact:* variants).

Here are the trade-offs up front:

OpenStreetMap Google Maps scrapers
Licence ODbL: store and reuse with attribution Google ToS forbid scraping and storing
Ratings and reviews Not in OSM Yes
Coverage Very good in Europe and big cities, uneven for trades in small towns Most complete

If you need star ratings, OSM can't help. If you need a contactable list you can actually keep, it's worth a look.

Step 1: Query OSM yourself with Overpass

The Overpass API is a read-only query engine over OSM data. The basic pattern for a lead list is:

  1. find the city's administrative boundary and turn it into an area
  2. ask for every node, way and relation (nwr) with your tag inside that area
  3. get back tags plus a centre point

A minimal Overpass QL query looks like this:

[out:json][timeout:60];
area["boundary"="administrative"]["name"="Denver"]->.city;
nwr["craft"="plumber"](area.city);
out center tags;
Enter fullscreen mode Exit fullscreen mode

Paste it into overpass-turbo.eu to see results on a map. In a real pipeline you'll quickly hit the annoying parts:

  • Ambiguous names. Lots of places are called "Denver" or "Manchester", so you need to pin down the right boundary (country, state, admin_level).
  • Busy public servers. Public Overpass instances often reply "try again later". You need retries with backoff, and ideally failover between servers.
  • Usage policies. Public instances are run by volunteers and companies, each with its own policy. overpass-api.de, for example, asks commercial users to self-host or pay. Read the policy before you point a business workload at a server.
  • Messy tags. phone vs contact:phone, several values separated by ;, websites without https://, and addresses split across addr:* tags.
  • Deduplication when you combine several categories or cities.

None of this is hard, but it adds up to a weekend of work.

Step 2: Or use the packaged version

I packaged all of that into an Apify Actor, Local Business Leads. The input is plain English:

{
  "categories": ["plumber", "electrician", "hvac", "roofer"],
  "locations": ["Denver, CO"],
  "enrichWebsites": true,
  "maxItems": 100
}
Enter fullscreen mode Exit fullscreen mode

What it does:

  • 95 built-in categories (dentist, hvac, hair salon, lawyer, accountant, gym, veterinarian, hotel…), plus any raw OSM tag such as shop=bakery, craft=* or cuisine~pizza
  • Resolves "Denver, CO" to Denver's official city boundary in OSM, so it doesn't spill into the next town. It can also search a radius around a lat/lng.
  • Resolves locations with Overpass itself, without calling the public Nominatim geocoder
  • Uses only public Overpass instances whose published policy allows use in any project: VK Maps and Private.coffee. It doesn't use overpass-api.de. Queries run one at a time, with exponential backoff and failover. For big or frequent jobs you can plug in your own Overpass endpoint.
  • Puts contact-rich leads first, and can filter to businesses that have a phone, website, email or any contact

Here's one real lead from a Denver roofers run (trimmed):

{
  "name": "Assurance Contractors",
  "category": "roofer",
  "address": "1660 South Albion Street Suite 339, Denver, CO 80222",
  "phone": "+1-720-837-1630",
  "email": "service@assurancecontractors.com",
  "website": "https://assurancecontractors.com/roofing-denver-co/",
  "openingHours": "Mo-Fr 09:00-17:00; Sa-Su closed",
  "latitude": 39.6863292,
  "longitude": -104.9391307,
  "osmId": "node/9053239725",
  "osmUrl": "https://www.openstreetmap.org/node/9053239725",
  "lastEditedAt": "2023-09-20T05:33:48Z",
  "websiteEmails": ["info@assurancecontractors.com"],
  "websitePagesFetched": 3,
  "attribution": "© OpenStreetMap contributors, ODbL 1.0 – https://www.openstreetmap.org/copyright"
}
Enter fullscreen mode Exit fullscreen mode

Each row also has address parts, socials (Facebook, Instagram, LinkedIn, X, YouTube, TikTok), brand, operator, cuisine, wheelchair access and a Wikidata ID where they're mapped. Every row carries an attribution field, an OSM link and a last-edited date, so you can see how fresh the entry is.

Step 3: Be honest about coverage

This is the part most lead tools skip. Here are real fill rates from my live test runs on 17 Oct 2026:

Search Leads Phone Website Email
Restaurants + cafés, Manchester UK (only with phone or website) 200 55% 87% 17%
Plumbers/electricians/HVAC/roofers, Denver CO + enrichment 32 46% 43% 25%
Dentists, Austin TX + enrichment (capped at $0.05) 10 100% 100% 60%
shop=bakery within 2 km of central Paris 25 80% 88% 24%

The pattern is clear. Food, shops, healthcare and hotels in big cities are well mapped. Trades in US cities are thin. 32 trade businesses across four categories in Denver is far fewer than Google would show. There are also no ratings or reviews. Use OSM where it's strong, and don't expect it to replace Google for every niche.

Step 4: Fill in emails from each business's own website

Email is the field OSM most often lacks. The fix is to visit each business's own website and read the contact details there.

With enrichWebsites: true, the Actor reads the homepage plus up to 4 contact/about/impressum pages. It stays on the business's own domain, respects robots.txt, sends at most 1 request per second per site and uses an identifiable User-Agent. In the Denver run, enrichment roughly doubled email coverage, from 12% (OSM tags alone) to 25%.

If you're building this yourself, the same rules apply: be polite, stay on the business's own domain, and honour robots.txt.

Export and automate

Results land in an Apify dataset that you can download as CSV, Excel or JSON, or push to Google Sheets, HubSpot, Make, Zapier or n8n. To keep a list up to date, schedule a monthly run per city and dedupe on osmId.

What it costs

  • $0.003 per lead ($3 per 1,000)
  • $0.002 per website enriched ($2 per 1,000), charged only when enrichment is on and the site was reached

You pay only for leads returned, and runs stop cleanly at your "maximum cost per run". The Austin dentist test stopped at 10 leads + 9 enrichments = $0.048. The free route (Overpass plus your own crawler) costs nothing but time.

Play fair

  • Credit "© OpenStreetMap contributors" when you use the data publicly, and remember the share-alike rule if you publish a derived database.
  • Contact data comes with obligations. GDPR, CAN-SPAM, CASL and PECR rules apply to cold email. Honour opt-outs.
  • Go easy on the public Overpass servers. Self-host or pay for an endpoint if you run big jobs.
  • If a business's details in OSM are wrong, fix them on openstreetmap.org. The map gets better for everyone.

Did a city come back thin, or do you have thoughts on the Overpass usage? Tell me in the comments. I'm especially keen to hear from OSM folks.

Map data © OpenStreetMap contributors, ODbL 1.0.

Top comments (0)