Local Business Data Extractor (NAP, hours, geo)
Under maintenancePricing
Pay per usage
Local Business Data Extractor (NAP, hours, geo)
Under maintenanceExtract normalized local-business data — name, type, phone, email, full address, lat/long, opening hours, price range, rating — from public pages via JSON-LD (LocalBusiness subtypes, Organization), microdata, and OpenGraph. HTML-only, fast, structured ok/error output.
Pricing
Pay per usage
Rating
0.0
(0)
Developer
Tommy G
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
6 days ago
Last modified
Categories
Share
Local Business Extractor (Apify Actor)
Give it any public business / venue page URL and get back clean, normalized
LocalBusiness data — name, business type, contact details, full address with geo
coordinates, opening hours, price range, and aggregate rating — pulled from schema.org
LocalBusiness (and subtypes like Restaurant / Store) in JSON-LD and microdata. HTML-only
(no headless browser) so it's fast and cheap. Ideal for local-business directories, lead
lists, and store-locator / map datasets.
What it extracts
For each page it returns one flat record with:
- name, legal_name, business_type, description
- telephone, email, url, image
- Address: street_address, locality, region, postal_code, country, address_full
- Geo: latitude, longitude
- opening_hours[], price_range, founding_date, same_as[] (linked social/profile URLs)
- Ratings: rating_value, rating_best, rating_count
Plus control keys present on every row (ok and error alike, for clean buyer tables):
status, requested_url, final_url, http_status, redirected, found, complete, page_type, source, render_required, fields_found, error, extracted_atInput
{ "startUrls": [{ "url": "https://example.com/locations/downtown" }], "maxConcurrency": 5, "maxPages": 100 }
maxPages capped at 200, maxConcurrency at 20 (cost guard).
Output — one STABLE record per URL (ok and error rows share the shape)
{"status": "ok","requested_url": "https://example.com/locations/downtown","final_url": "https://example.com/locations/downtown","http_status": 200,"found": true,"complete": true,"page_type": "localbusiness","source": "json-ld","name": "Acme Coffee Downtown","legal_name": "Acme Coffee LLC","business_type": "CafeOrCoffeeShop","description": "Specialty coffee in the heart of downtown.","telephone": "+1-212-555-0100","email": "hello@acmecoffee.example","url": "https://acmecoffee.example","image": "https://example.com/img/store.jpg","street_address": "123 Main St","locality": "New York","region": "NY","postal_code": "10001","country": "US","address_full": "123 Main St, New York, NY 10001, US","latitude": 40.7128,"longitude": -74.006,"opening_hours": ["Mo-Fr 07:00-19:00", "Sa-Su 08:00-17:00"],"price_range": "$$","rating_value": 4.6,"rating_best": 5,"rating_count": 312,"same_as": ["https://instagram.com/acmecoffee"],"fields_found": ["name", "telephone", "address_full", "rating_value"],"extracted_at": "2026-05-29T..."}
found:false means no LocalBusiness markup was present (e.g. a JS-rendered locator widget).
Failed fetches return the same keys with status:"error" + error.
Use cases
- Local-business directories — assemble structured listings (name, type, contact, hours, rating).
- Lead lists — pull public phone/email/address for businesses across a set of pages.
- Store-locator / map datasets — get geo coordinates + opening hours for mapping.
Notes / safety
- Reads only public schema.org business markup — facts-only, only contact details the page itself publicly publishes; no raw page bodies stored.
- SSRF-guarded (scheme + private/metadata IP block + redirect re-check), robots-respecting,
rate-limited, cost-capped — all via the shared
src/lib/actor_runner.js. - HTML-only: client-rendered pages that inject business JSON via JS return
found:false(no server-side markup to read). Core logic insrc/extract.js(pure, unit-tested).
Run locally / test
npm installnpm test # unit tests on the pure extractor (node:test)
Publish to Apify (account-holder's step)
$npm install -g apify-cli && apify login && apify push
Keep it free initially; enable pricing later via the adult account-holder once it shows repeat organic usage and clears a margin gate.