OpenStreetMap Scraper - OSM POI Data, No-Website Leads avatar

OpenStreetMap Scraper - OSM POI Data, No-Website Leads

Pricing

$2.60 / 1,000 leads

Go to Apify Store
OpenStreetMap Scraper - OSM POI Data, No-Website 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

Snow Leo Data

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

7 hours ago

Last modified

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.

MeasurementAustin, TexasManchester, UK
Named businesses found in the whole city7,6735,905
With no website at all3,9324,261
Overpass queries needed11
Wall-clock time for the sweep80.5 s13.4 s
Records carrying a phone number3,192943
Phone numbers normalised to E.1643,192 (100%)943 (100%)
Records publishing opening hours3,0491,059
Opening hours parsed into a weekly schedule2,781937
Chain outlets identified and flagged2,6071,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:

  • Identityname, name_international, brand, operator, is_chain
  • Categorycategory, category_group, plus the raw osm_key and osm_value so nothing is hidden behind our own naming
  • The website questionhas_website, website, and website_tag, which names the exact tag the address was found in
  • Contacthas_phone, phone in E.164, phone_raw as the map stores it, phone_tag, phone_all when a business lists several numbers, email, facebook, instagram, and social_only for businesses that have a Facebook or Instagram page but no site of their own
  • Address, split into partsaddress, street, house_number, unit, postcode, neighbourhood, city, county, state, country_code, latitude, longitude, distance_from_centre_km
  • Opening hours, machine-readableopening_hours as published, opening_hours_weekly as a Monday-to-Sunday object of time ranges, opening_hours_parsed, open_24_7, open_days_per_week
  • Extra detail where the map has itcuisine, wheelchair, internet_access, payment_methods, service_options, description
  • Lead qualificationlead_score, lead_tier, lead_reasons
  • Provenanceosm_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:

FieldBusinesses with no website that carry it
address, latitude, longitude, categoryevery row
opening_hours327 (20.6%)
phone133 (8.4%)
email5 (0.3%)
facebook / instagram8 (0.5%)

Reproduce it against the live Overpass API:

python3 - <<'PY'
from src import source, categories
rules = 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:

CheckPointsWhy it matters
no_website40There is something to sell
reachable_by_phone25You can start the conversation today
full_street_address15A real premises, not a dropped pin
independent_not_chain10Somebody local can say yes
opening_hours_published5The record is maintained
recently_edited5Someone 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:

  1. A bounding boxsouth,west,north,east in decimal degrees. Exact, repeatable, and the right tool for splitting a large metro across several runs.
  2. A radius — kilometres or miles from the centre of the place you named.
  3. 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.

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.