Maps scraper
The Ultron Maps scraper turns a list of search terms and a location into a structured set of places: name, address, phone, website, categories, rating, review count, hours, photo count. It runs on a managed worker platform with residential proxies, Playwright under Crawlee, and a webhook-back contract to Ultron when each run completes.
Overview
- Worker
ultron-gmaps- Runtime
- Node 20, Crawlee PlaywrightCrawler
- Source
apify-actors/gmaps/- Proxy
- Residential, on by default
- Default page size
- 100 results per search
- Max page size
- 500 results per search
- Cost
- ~0.65 USD per 1,000 places without reviews
The Maps scraper is the workhorse of the local-business workflows. The Specter persona's gmaps-leads skill fires it the moment a user asks for businesses in a given category and location; downstream skills enrich and contact the resulting leads. It is the only scraper in the worker set that defaults to residential proxy because Google Maps treats datacentre IPs aggressively.
Input schema
Drive everything from one input object.
Crawl flow
Five steps. Each step is one file under src/.
The crawler is deliberately patient. Each feed scroll waits for the result counter to stabilise before reading the cards, otherwise the actor races Maps' lazy load and undercounts results. Each detail panel click waits for the phone or website field to appear before reading, otherwise the row goes out half-populated.
Output shape
One row per place. All fields are nullable except name + scraped_at.
Proxy and throttling
Residential by default. Jitter between requests.
The crawler ships with a small jitter helper (jitterMs) that adds a random delay between requests inside the same crawl, sized to avoid burst-detection patterns on the maps domain. The proxy pool rotates per request; sticky sessions are not used because the maps frontend re-authenticates the visitor on every navigation anyway.
Fast vs full mode
Fast harvests cards from the feed. Full clicks each one for the detail panel.
| Mode | Triggered by | Returns | Speed |
|---|---|---|---|
| Fast | include_details: false | name, address, categories, rating, review_count, photos_count | Fast (one feed scroll per search) |
| Full | include_details: true (default) | All fast fields plus phone, website, place_id, lat, lng, hours | Slower (one detail click per result) |
Selector strategy
Selectors are defined in helpers.js with multiple fallbacks per field.
Google rewrites maps' DOM regularly. Each extractor reads from a list of selector candidates per field, picks the first that returns a non-empty value, and logs the picked variant so a future class-name change is visible in run logs before users start seeing missing fields. The actor is clean-room: it uses only public DOM patterns (aria-labels, role attributes), no proprietary reverse-engineered selectors.
Cost
Pricing scales with results, not searches. Reviews are the expensive bit.
| Mode | Approx cost per 1,000 results |
|---|---|
| Fast (no details, no reviews) | 0.30 USD |
| Full (details, no reviews) | 0.65 USD |
| Full + reviews (5 per place) | 2.00 USD |
How to trigger it
From a skill, through the Apify HTTP API.
gmaps-leads capability call which the platform translates into the right Apify run with the user's workspace id and conversation id attached as metadata. Webhooks come back to the platform, not to the skill.