OpenStreetMap Scraper - OSM POI Data, No-Website Leads
Pricing
$2.60 / 1,000 leads
OpenStreetMap Scraper - OSM POI Data, No-Website Leads
Local business leads with no website of their own, from OpenStreetMap: 3932 of 7673 named Austin businesses in one query, 80.5 seconds. Phones normalised to E.164, 2607 chain outlets removed before billing, any city worldwide. Businesses without websites finder, OSM points of interest.
Pricing
$2.60 / 1,000 leads
Rating
0.0
(0)
Developer
Snow Leo Data
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
7 hours ago
Last modified
Categories
Share
Local Business Leads With No Website — from OpenStreetMap
Find local businesses that have no website — the single easiest pitch in web design, SEO, and digital marketing. This Actor reads OpenStreetMap, the open map database that powers Apple Maps, Strava, Craigslist and thousands of other products, through its public Overpass API. No proxies. No headless browser. No API key. One request sweeps an entire city.
A missing website here is a fact in the data, not something a model guessed: the record simply has no website tag on it. The same goes for a missing phone number, a missing address and missing opening hours. Every row links back to the exact map object it came from, so any claim in this dataset can be checked at the source in one click.
Data source: OpenStreetMap, © OpenStreetMap contributors, licensed ODbL 1.0.
What this Actor measured before it was published
Every number below came from tools/measure.py on 2026-09-12. Nothing here is
an estimate.
| Measurement | Austin, Texas | Manchester, UK |
|---|---|---|
| Named businesses found in the whole city | 7,673 | 5,905 |
| With no website at all | 3,932 | 4,261 |
| Overpass queries needed | 1 | 1 |
| Wall-clock time for the sweep | 80.5 s | 13.4 s |
| Records carrying a phone number | 3,192 | 943 |
| Phone numbers normalised to E.164 | 3,192 (100%) | 943 (100%) |
| Records publishing opening hours | 3,049 | 1,059 |
| Opening hours parsed into a weekly schedule | 2,781 | 937 |
| Chain outlets identified and flagged | 2,607 | 1,205 |
Two of those rows deserve a second look.
3,741 Austin businesses have a website, but only 3,500 of them keep it in
the plain website tag. The other 241 keep it in contact:website, url,
brand:website or operator:website. A scraper that checks one tag would
have handed you 241 businesses in one city that already have a website — and
charged you for every one of them. This Actor checks six website tags and six
phone tags before it decides that something is missing.
2,607 of 7,673 Austin records are chain outlets. A branch manager cannot buy a website from you; head office already did. One checkbox removes all of them before you are billed.
How it works
What exactly does this Actor return?
Every row is one business, with 59 fields in the default mode:
- Identity —
name,name_international,brand,operator,is_chain - Category —
category,category_group, plus the rawosm_keyandosm_valueso nothing is hidden behind our own naming - The website question —
has_website,website, andwebsite_tag, which names the exact tag the address was found in - Contact —
has_phone,phonein E.164,phone_rawas the map stores it,phone_tag,phone_allwhen a business lists several numbers,email,facebook,instagram, andsocial_onlyfor businesses that have a Facebook or Instagram page but no site of their own - Address, split into parts —
address,street,house_number,unit,postcode,neighbourhood,city,county,state,country_code,latitude,longitude,distance_from_centre_km - Opening hours, machine-readable —
opening_hoursas published,opening_hours_weeklyas a Monday-to-Sunday object of time ranges,opening_hours_parsed,open_24_7,open_days_per_week - Extra detail where the map has it —
cuisine,wheelchair,internet_access,payment_methods,service_options,description - Lead qualification —
lead_score,lead_tier,lead_reasons - Provenance —
osm_type,osm_id,osm_url,last_edited,months_since_edit,search_area,change_type,data_source,data_licence,attribution,scraped_at
An empty field always means one thing: the map does not hold that value. It is never filled in by inference, pattern-matching or a language model.
How often is each contact field actually there?
The map is volunteered data, so a contact field is present only when a mapper typed it in. Measured on 16 September 2026 over one Austin, Texas sweep — 3 000 named businesses across every category this Actor covers, of which 1 586 had no website of their own:
| Field | Businesses with no website that carry it |
|---|---|
address, latitude, longitude, category | every row |
opening_hours | 327 (20.6%) |
phone | 133 (8.4%) |
email | 5 (0.3%) |
facebook / instagram | 8 (0.5%) |
Reproduce it against the live Overpass API:
python3 - <<'PY'from src import source, categoriesrules = categories.selectors([g[0] for g in categories.GROUPS], [])els = source.run_query(source.build_query(('bbox', 30.20, -97.80, 30.35, -97.68), rules, limit=3000, with_meta=False))rows = [e.get('tags') or {} for e in els if (e.get('tags') or {}).get('name')]no_site = [t for t in rows if not (t.get('website') or t.get('contact:website'))]print(len(rows), len(no_site),sum(1 for t in no_site if t.get('phone') or t.get('contact:phone')))PY
This is why requirePhone exists: switch it on and the run keeps only the
businesses you can call, and you are not charged for the rest. What every row
always carries is the name, the address, the coordinates and the category —
enough to walk in or post a letter, which is how this list is normally worked.
How is the lead score calculated?
Six checks, each one a plain fact about the record, and each one listed by name
in lead_reasons so you can audit the number instead of trusting it:
| Check | Points | Why it matters |
|---|---|---|
no_website | 40 | There is something to sell |
reachable_by_phone | 25 | You can start the conversation today |
full_street_address | 15 | A real premises, not a dropped pin |
independent_not_chain | 10 | Somebody local can say yes |
opening_hours_published | 5 | The record is maintained |
recently_edited | 5 | Someone saw this place in the last two years |
The total is rescaled to 0-100. lead_tier is HIGH at 70 and above, MEDIUM
from 45, LOW below that. There is no hidden model and no black box: if you
disagree with the weighting, filter on the underlying fields instead.
How do I choose the area to sweep?
Three ways, in order of precedence:
- A bounding box —
south,west,north,eastin decimal degrees. Exact, repeatable, and the right tool for splitting a large metro across several runs. - A radius — kilometres or miles from the centre of the place you named.
- The administrative boundary — the default. Name a city and you get the real city limits, not a circle that clips half the suburbs and spills into farmland on the other side.
The place name is resolved through Nominatim, OpenStreetMap's own geocoder, which prefers a genuine administrative boundary over a same-named point of interest. Add a country code when a name is ambiguous.
Which business categories can I ask for?
Fourteen groups, each of which maps onto real OpenStreetMap tags. The mapping is printed in the run report, so you always know exactly what was asked for: restaurants and cafes; hair, beauty and nails; dentists, doctors and vets; plumbers, electricians and builders; automotive; offices; lawyers and accountants; estate agents; every kind of shop; hotels and guest houses; gyms and leisure; schools and training; pet services; photographers and florists.
Leave the list empty and all fourteen are swept at once — that is how the whole-city numbers at the top of this page were produced.
For anything the list does not cover, customTags takes raw OpenStreetMap
pairs such as shop=florist, craft=roofer or office=architect, or a bare
key such as club to take every value of it.
How does monitor mode avoid charging me twice?
Turn on onlyNew and the Actor keeps a record of every place it has returned,
in a named key-value store that survives between runs. On the next run it
compares each place against what it remembers and assigns a change_type:
- NEW — this place was not in the previous runs
- UPDATED — its name, website, phone, address, opening hours or category changed
- UNCHANGED — nothing you care about moved
Unchanged records are not written to the dataset, so you are not billed for
them. Set emitUnchanged if you want the full picture anyway.
The fingerprint deliberately ignores coordinates and edit timestamps. Mappers nudge a pin by a metre and fix a neighbouring tag constantly; if those counted, every run would declare the whole city updated and every run would cost you full price.
Where do the results go besides the dataset?
Four delivery channels, all optional, all plain HTTP:
- Slack — incoming webhook URL
- Discord — incoming webhook URL
- Telegram — bot token plus chat ID
- Your own webhook — a JSON POST with the run summary and up to 200 leads, with custom headers if your endpoint needs authentication
notifyOnlyChanges narrows the message to new and updated places.
notificationLimit controls how many leads are listed inside one message; the
full set always lands in the dataset either way.
A broken notification channel never fails the run. The leads are already delivered and already paid for; an expired Telegram token must not turn a successful run into a failed one. Every channel's outcome is written to the run report instead.
What happens when the source struggles?
Overpass is a shared public service with a real query budget. When a sweep is too heavy for one request, this Actor splits the area into quadrants and retries each one, up to four levels deep, deduplicating the places that fall into two cells at once. It also knows three Overpass mirrors and moves to the next one after a 429 or a 504.
A naive client does none of this, receives a gateway timeout on a large city, and reports that the source is down.
What does a run cost me in results?
Every filter in the input runs before anything is written to the dataset.
Rows removed by websiteFilter, requirePhone, requireAddress,
excludeChains, minLeadScore and the rest never reach the dataset and cost
you nothing. The run report breaks down exactly what each filter removed, so an
empty result is always explained rather than mysterious.
The Actor also respects your per-run spending limit itself. Apify stops charging when you hit the limit but does not stop the run; this Actor reads the remaining budget at start-up and stops at it.
Honest limitations
Rule of this repository: anything a competing Actor does that this one does not is named here rather than quietly omitted.
No ratings and no review counts. OpenStreetMap does not have a rating
system, so minRating and minReviews style filters are not available here
and never will be. Scrapers built on Google Maps can offer them. If ranking by
star rating is essential to your workflow, this is not your tool.
Coverage is volunteer-made and varies by city. OpenStreetMap is edited by people, not by a company with a street-view fleet. In some cities its business coverage is excellent; in others it is thinner than a commercial map. We measured 7,673 named businesses inside the Austin city limits and 5,905 in Manchester. Before committing to a market, run a small sweep and look at the count — it costs almost nothing and it is the only honest way to know.
Opening hours are not always parsable. OpenStreetMap's opening-hours syntax
supports seasonal rules, public holidays and sunrise-relative times. This Actor
parses the common weekday-and-time subset — 2,781 of the 3,049 Austin records
that publish hours — and honestly reports opening_hours_parsed: false for the
rest, leaving the original string untouched in opening_hours. It never
pretends to have understood a schedule it did not.
A phone number is only converted when it can be converted. If the map
stores a local number and the country is unknown, phone stays empty rather
than guessing a dialling code. A wrong number costs you a call to a stranger;
an empty field costs you nothing.
Fewer delivery channels than the biggest competitor. Delivery here is Slack, Discord, Telegram and a generic webhook. There is no WhatsApp Cloud API channel and no Notion or issue-tracker connector; a competing Actor has both. If your pipeline ends in one of those, send the generic webhook into your automation tool instead.
Descriptions come in one format. OpenStreetMap stores a plain-text
description tag and nothing else, so there is no HTML or Markdown variant of
it to choose between. Competitors that scrape listing pages can offer that
choice; there is simply no second format in this source to offer.
No Google Maps place IDs, CIDs or Google URLs. The identifiers here are OpenStreetMap identifiers. They are stable, public and free to look up, but they are not the same namespace.
Input
Leave everything empty and press Start: you get up to 200 website-less businesses in Austin, Texas, which is enough to see the shape of the data before deciding anything.
{"location": "Manchester, United Kingdom","categories": ["beauty_wellness", "food_drink"],"websiteFilter": "no_website","requirePhone": true,"excludeChains": true,"maxResults": 500}
A monitored city, checked daily, billing only for movement:
{"location": "Austin, Texas, United States","onlyNew": true,"requirePhone": true,"minLeadScore": 70,"slackWebhookUrl": "https://hooks.slack.com/services/...","notifyOnlyChanges": true,"maxResults": 1000}
An exact territory, for splitting a metro between runs:
{"boundingBox": "30.15,-97.95,30.45,-97.60","categories": ["home_trades"],"requireAddress": true,"maxResults": 1000}
Output
One example row, trimmed to the interesting fields:
{"name": "Bird's Barber Shop","category": "hairdresser","category_group": "beauty_wellness","has_website": false,"website": "","has_phone": true,"phone": "+15123409288","phone_raw": "+1 512 340 9288","address": "4815 W Braker Ln, Unit 530, Austin, TX, 78759","street": "W Braker Ln","house_number": "4815","unit": "530","postcode": "78759","city": "Austin","state": "TX","country_code": "US","latitude": 30.3999206,"longitude": -97.7481906,"opening_hours": "Mo-Su 09:00-19:00","opening_hours_weekly": {"monday": ["09:00-19:00"],"sunday": ["09:00-19:00"]},"open_days_per_week": 7,"is_chain": false,"lead_score": 100,"lead_tier": "HIGH","lead_reasons": ["no_website","reachable_by_phone","full_street_address","independent_not_chain","opening_hours_published","recently_edited"],"osm_url": "https://www.openstreetmap.org/node/1249568015","last_edited": "2025-11-23T16:08:37Z","months_since_edit": 10,"change_type": "NEW","data_licence": "ODbL 1.0","attribution": "© OpenStreetMap contributors, ODbL 1.0 (openstreetmap.org/copyright)"}
Set compactOutput to get 14 core fields plus the attribution instead — a
smaller payload for LLM pipelines and agent tools. Set excludeEmptyFields to
drop nulls and empty lists from every record.
FAQ
Who actually buys this data?
Web design studios, SEO consultants, digital marketing agencies, SaaS tools sold to local businesses, franchise development teams, and market researchers mapping a category across a city. The common thread is an offer that only makes sense to a business that does not already have what you sell.
Is scraping OpenStreetMap legal?
OpenStreetMap data is open data, published deliberately for reuse under the Open Database Licence 1.0. The Overpass API is the project's own public query endpoint. This Actor honours the usage policy: a descriptive User-Agent, one request at a time, a pause between requests, and no attempt to work around any protection — because there is none to work around.
The licence has one requirement you must carry forward: attribution. Every
row carries an attribution field with the required credit, and it survives
both compact mode and empty-field trimming. If you publish or redistribute
anything derived from this data, credit © OpenStreetMap contributors.
Does this collect personal data?
No. It returns business records from a public map: trading name, business address, business phone number, business category and opening hours. It does not collect names of individuals, personal email addresses, personal phone numbers or anything behind a login. Records that are not businesses at all — named benches, parks, car parks — are filtered out before billing.
Why OpenStreetMap instead of Google Maps?
Three practical reasons. It needs no proxies and no headless browser, so the run costs a fraction of a scraped equivalent and there is no monthly proxy bill baked into the price. It covers every country equally, because there is one global database rather than a per-market product. And the data is licensed for reuse, which is a different legal position from data scraped out of a consumer product's interface.
The trade-off is coverage and the absence of ratings, both named in the limitations above.
How fresh is the data?
Overpass serves the live OpenStreetMap database, usually within a minute or two
of the latest edit. Freshness of an individual record is a different question,
and this Actor answers it directly: last_edited is the timestamp of the last
edit to that map object and months_since_edit is the age in months. A record
last touched eight years ago deserves a verification call before a sales call,
and now you can sort by that.
Can I sweep a whole country?
Technically the bounding box will accept one, but Overpass will refuse a query that large, and the Actor will spend a long time splitting it into cells. The practical unit is a city or a metropolitan area. To cover a country, run one task per city on a schedule — that also keeps monitor mode meaningful, because each task remembers its own area.
What happens on the very first run of monitor mode?
Everything is NEW, because the memory starts empty. That first run is your baseline and it is priced like any other run. From the second run onwards you pay only for movement. If you want to seed the memory cheaply, do the first run with a tight filter.
Why do some records have no address?
Because nobody has mapped one yet. In Austin, 5,200 of 7,673 businesses have a
full street address in the map; the rest have a name, a category and a
position but no postal address. Use requireAddress if a postal address is
non-negotiable for your campaign — it filters before billing, so the ones
without cost you nothing.
Can I get businesses that DO have a website?
Yes. Set websiteFilter to has_website to invert the filter, or to any to
turn it off entirely and take the full business directory of a city. The same
59 fields come back either way, which makes this a reasonable local-business
directory scraper even when the no-website angle is not what you are after.
How do I verify a lead before calling it?
Open its osm_url. It goes straight to the map object this row was built from,
with the full tag list and the complete edit history. Every claim in every row
can be checked at the source, which is not something a scraped screenshot of a
consumer map can offer.
Data: © OpenStreetMap contributors, ODbL 1.0 — https://www.openstreetmap.org/copyright
What else is this called?
Names buyers use for this job: OpenStreetMap business data, OSM POI scraper, OSM amenity export, Overpass API businesses, businesses without a website, no-website leads. One Overpass query per area returns the amenities with their addresses, phones and website tags.
Other names for what a row carries: wifi internet access venues (internet_access), international name transliteration (name_international), service options for delivery and takeaway (service_options), lead tier segmentation (lead_tier), independent versus chain business and chain detection by brand or operator (is_chain, brand, operator). Telegram bot notifications are supported through the telegramToken input.