# Google Maps Scraper (`vortex_data/google-maps`) Actor

Stop wasting your budget on slow, resource-heavy browser-based scrapers. This is the fastest, most cost-effective, and data-rich Google Maps scraper on Apify, designed for high-scale lead generation and market research.

- **URL**: https://apify.com/vortex\_data/google-maps.md
- **Developed by:** [VortexData](https://apify.com/vortex_data) (community)
- **Categories:** Lead generation, Automation, Developer tools
- **Stats:** 233 total users, 80 monthly users, 99.3% runs succeeded, 8 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

from $1.00 / 1,000 place scrapeds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp.md).

If your project is in a different language, use the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).


# README

## 🗺️ Google Maps Scraper

Extract structured Google Maps place data for lead generation, local market research, review monitoring, and competitor analysis.

This Actor searches Google Maps by keyword and location, returns one dataset row per unique place, and can optionally enrich each place with website contacts, social profiles, and reviews.

✅ No Google Maps API key is required.

### 📦 What You Can Extract

| Data group | Example fields |
| --- | --- |
| 🏷️ Place identity | `title`, `categoryName`, `categories`, `description`, `subTitle` |
| 📍 Address and location | `address`, `street`, `city`, `state`, `postalCode`, `countryCode`, `location` |
| ☎️ Contact data | `phone`, `phoneUnformatted`, `website`, `websiteDisplay` |
| ✉️ Website enrichment | `emails`, `additionalPhones`, `facebooks`, `instagrams`, `linkedIns`, `twitters`, `youtubes`, `tiktoks`, `pinterests`, `whatsapps` |
| ⭐ Ratings and status | `totalScore`, `reviewsCount`, `currentStatus`, `openingHoursToday`, `permanentlyClosed`, `temporarilyClosed` |
| 💬 Reviews | `reviews`, `reviewsScrapedCount`, review text, author metadata, dates, photos when available |
| 🔗 Google identifiers | `placeId`, `fid`, `cid`, `kgmid`, canonical Google Maps `url` |
| 🏨 Hotel fields | `hotelStars`, `hotelPrice`, `hotelAmenities`, `hotelCheckInDate`, `hotelCheckOutDate` |

The dataset includes ready-made Apify views for **Overview**, **Lead generation**, **Reviews**, and **Hotels**. Apify also provides the built-in **All fields** view.

### 🎯 Best For

- Building local B2B lead lists with phone numbers, websites, emails, and social profiles.
- Finding competitors in a city, region, ZIP code, or custom area.
- Collecting Google Maps reviews for reputation analysis.
- Monitoring business categories, ratings, opening status, and addresses.
- Enriching CRM, SEO, sales, or local-market datasets.

### 🚀 Quick Start

For most runs, fill in only these fields:

1. `searchStringsArray` - what to search for, one term per line.
2. `locationQueries` - one or more cities, regions, neighborhoods, or countries, one per line.
3. `maxCrawledPlacesPerSearch` - how many places to keep for each search term and location.

Example input:

```json
{
  "searchStringsArray": ["coffee shop"],
  "locationQueries": ["Brooklyn, New York, United States"],
  "maxCrawledPlacesPerSearch": 200
}
````

This returns up to 200 coffee shops around Brooklyn with core Google Maps fields such as name, category, address, phone, website, rating, coordinates, Place ID, and Maps URL.

### 🧪 Input Examples

#### ✉️ Lead Generation With Emails

Use website enrichment when you need emails and social profiles from business websites.

```json
{
  "searchStringsArray": ["dental clinic"],
  "locationQueries": ["Austin, Texas, United States"],
  "maxCrawledPlacesPerSearch": 500,
  "extractContactsFromWebsite": true
}
```

The Actor saves all scraped places and fills `emails`, social profiles, and
extra phones when they are found. After the run, filter the dataset by
`emails.length > 0` if you need an email-only export.

#### ⭐ Reviews

Set `maxReviewsPerPlace` to collect reviews for each scraped place.

```json
{
  "searchStringsArray": ["pizza"],
  "locationQueries": ["Brooklyn, New York, United States"],
  "maxCrawledPlacesPerSearch": 100,
  "maxReviewsPerPlace": 100,
  "reviewsSort": "newest"
}
```

The Actor can fetch up to 1000 reviews per place. Higher values take longer and use more requests.

#### 🔎 Multiple Search Terms

```json
{
  "searchStringsArray": [
    "restaurant",
    "coffee shop",
    "bakery"
  ],
  "locationQueries": ["Helsinki, Finland"],
  "maxCrawledPlacesPerSearch": 300,
  "language": "fi",
  "additionalLanguages": ["en"]
}
```

Each search term has its own result limit. Extra languages run additional passes and can improve coverage in multilingual regions.

#### 📍 Multiple Locations

Use multiple entries in `locationQueries` when you want to run the same search
terms across several independent areas in one run. Keep one complete location
per line; do not write two cities in one line.

```json
{
  "searchStringsArray": ["plumber", "electrician"],
  "locationQueries": [
    "Seddon, Victoria, Australia",
    "Footscray, Victoria, Australia"
  ],
  "maxCrawledPlacesPerSearch": 100
}
```

`maxCrawledPlacesPerSearch` applies per search term per location. The example
above can save up to about 400 places before deduplication: 2 terms × 2
locations × 100.

#### 🔗 Google Maps URLs Or Place IDs

Use this when you already know exact places, or when you copied a Google Maps
search result page with its visible map viewport.

```json
{
  "startUrls": [
    {
      "url": "https://www.google.com/maps/search/?api=1&query=Example+Business&query_place_id=ChIJreV9aqYWdkgROM_boL6YbwA",
      "userData": { "externalId": "crm-123" }
    }
  ],
  "maxReviewsPerPlace": 50
}
```

Direct URLs and raw Google Place IDs do not require `searchStringsArray` or
`locationQueries`. Raw Place IDs such as `ChIJreV9aqYWdkgROM_boL6YbwA` are
resolved through Google's exact `query_place_id` place endpoint and accepted
only when the returned `placeId` matches the input. Short `/g/...` identifiers,
CIDs, and fids are different Google identifiers; pass a full Google Maps URL
for those.

Copied search result URLs such as
`https://www.google.com/maps/search/coffee+shop/@30.2711286,-97.7436995,13z`
are treated as searches in the visible map area from the URL. The Actor parses
the search text and `@lat,lng,zoom` viewport. Google may also copy URLs with a
meter-scale viewport such as `@lat,lng,7094m`; those are accepted too. The Actor
does not guess one exact place. Search URLs without `@lat,lng,zoom` or
`@lat,lng,meters` are ignored because no search area is available.

How to choose the right direct input:

- Best option: open the place in Google Maps, click **Share**, click **Copy link**,
  and paste the full link into `startUrls`.
- Use `placeIds` only for raw Google Place IDs from Google Places API, Google's
  Place ID Finder, or a Maps URL parameter named `query_place_id`.
- Paste `/maps/search/.../@lat,lng,zoom` or `/maps/search/.../@lat,lng,meters`
  URLs when you want to scrape the
  visible Google Maps search area from an existing map page.
- Do not paste `/g/...` values, short IDs such as `11lmncvyn7`, fids such as
  `0x317067ffdca15d1b:0x367befc8c87c6fef`, numeric CIDs, or full URLs into
  `placeIds`. If you have any of those, paste the full Google Maps URL into
  `startUrls`.

#### 🧭 Custom Search Area

Use `customGeolocation` when a city boundary is too small, too large, or not accurate enough.

```json
{
  "searchStringsArray": ["plumber"],
  "maxCrawledPlacesPerSearch": 1000,
  "customGeolocation": {
    "type": "Point",
    "coordinates": [-74.006, 40.7128],
    "radiusKm": 20
  }
}
```

Supported custom areas include GeoJSON `Point` with `radiusKm`, `Polygon`, and `MultiPolygon`.
For normal keyword + location searches, the Actor adds the resolved location
text to Google Maps queries so broad categories stay focused on the requested
city or region before geographic filtering is applied.
By default, the Actor keeps all places Google returns for the search area. Set
`geoStrictMatch=true` when you need the final dataset to include only places
whose coordinates are inside the resolved location or custom GeoJSON area.
GeoJSON `Polygon` and `MultiPolygon` inputs are checked against the exact
polygon. Small geocoded areas such as suburbs and neighborhoods use the exact
OpenStreetMap boundary when available; broader geocoded areas and GeoJSON
`Point` + `radiusKm` areas use the resolved bounding box.
Strict mode requires a real area boundary. If the location resolves only to a
point, the Actor skips keyword search with a warning instead of returning rows
that cannot be checked against the selected area.
📍 When strict matching is off, rows include `insideSearchArea` so you can filter
nearby or out-of-area Google results without losing the scraped row.

### ⚙️ Configuration Reference

| Input field | What it does | Recommended use |
| --- | --- | --- |
| `searchStringsArray` | Google Maps search terms. | Add one business category or keyword per line. |
| `locationQueries` | One or more independent search locations. | Add one complete city/suburb/region per line. One line works for normal single-location runs; multiple lines run the same search terms across several areas. |
| `maxCrawledPlacesPerSearch` | Maximum kept places per search term per location, or per direct target. | Use 50 for tests, 500+ for city-scale lists. |
| `language` | Primary Google Maps language. | Use the local language for local categories when needed. |
| `additionalLanguages` | Extra language passes. | Useful in multilingual countries, but increases requests. |
| `extractContactsFromWebsite` | Visits business websites and extracts emails, phones, and social links. | Enable for lead generation. |
| `contactsFetchContactPage` | If the homepage has no email, checks up to five likely contact/about/location/press/booking pages. | Keep enabled for better email yield. |
| `contactsProxyFallback` | Retries only failed website/contact-page fetches through residential proxy after a direct attempt. | Keep enabled for better contact coverage; turn off only to minimize proxy bandwidth. |
| `contactsFilterEmailsByWebsiteDomain` | Keeps only emails that look related to the business website. | Enabled by default for cleaner leads; turn off to save every valid-looking public email found on the visited homepage/contact pages, including parent-company, agency, vendor, or platform domains. |
| `maxReviewsPerPlace` | Number of reviews to attach to each place. | Use 0 for fastest runs, higher values for review analysis. |
| `reviewsSort` | Requested review order. Google can ignore it for some places. | `newest` is best-effort, not a guarantee. |
| `skipClosedPlaces` | Removes temporarily or permanently closed places. | Enable for active-business lead lists. |
| `geoStrictMatch` | 📍 Drops places outside the resolved location or custom GeoJSON area. | Leave off for broad discovery; enable for strict city, neighborhood, or exact polygon exports. |
| `enableSubdivision` | Splits dense map areas into smaller viewports. | Keep enabled for full city coverage. |
| `maxSubdivisionDepth` | Maximum depth for automatic area splitting. | Increase for dense cities; decrease for faster runs. |
| `multiZoomDelta` | Runs nearby zoom levels around the resolved location. | Use only when you need extra coverage; it increases requests. |
| `concurrency` | Parallel scraping workers. | Higher is faster but can use more proxy bandwidth. |

Legacy post-fetch filters such as `categoryFilterWords`, `placeMinimumStars`,
and `searchMatching` are no longer applied. They could remove valid rows only
after Google Maps data had already been fetched, which made some runs expensive
with little or no output. Use the dataset fields (`categories`, `totalScore`,
`title`, `searchString`) to filter after the run instead.

### 📤 Output

The main result is the default dataset. Each item is one unique Google Maps place.

Example output item:

```json
{
  "searchString": "coffee shop",
  "title": "Bird & Branch Coffee Roasters",
  "categoryName": "Coffee shop",
  "categories": ["Coffee shop", "Cafe"],
  "address": "359 W 45th St, New York, NY 10036, United States",
  "city": "New York",
  "state": "New York",
  "countryCode": "US",
  "location": { "lat": 40.7602998, "lng": -73.9907758 },
  "insideSearchArea": true,
  "phone": "+1 917-265-8444",
  "website": "https://www.birdandbranch.com/",
  "emails": ["hello@example.com"],
  "instagrams": ["https://www.instagram.com/example"],
  "totalScore": 4.6,
  "currentStatus": "Closed",
  "placeId": "ChIJTVhsxFNYwokRXgPwYnY0vgI",
  "url": "https://www.google.com/maps/search/?api=1&query=Bird+%26+Branch+Coffee+Roasters&query_place_id=ChIJTVhsxFNYwokRXgPwYnY0vgI",
  "reviewsScrapedCount": 5,
  "reviews": [
    {
      "author": "Alex Smith",
      "text": "Great coffee and friendly service.",
      "publishedAt": "3 months ago",
      "publishedAtTimestamp": "2026-02-04T14:22:09Z"
    }
  ],
  "scrapedAt": "2026-05-29T10:30:00Z"
}
```

Website enrichment fields such as `emails` and social profiles are present only when `extractContactsFromWebsite` is enabled. Review fields are present only when `maxReviewsPerPlace` is greater than 0.

### 🩺 Run Summary And Diagnostics

At the end of each run, the Actor writes an `OUTPUT` record to the default key-value store. It contains:

- `status` - `SUCCEEDED`, `PARTIAL`, or `INPUT_NEEDS_ATTENTION`.
- `inputWarning` - human-readable explanation when the run needs input changes.
- `diagnosticHints` - concrete reasons for low or zero output.
- `totalPlaces` - number of pushed dataset rows.
- `searchStats` - raw counters for returned, filtered, out-of-area, and dropped places.
- `searchStartUrlsAccepted` - number of Google Maps search URLs accepted from `startUrls`.
- `unsupportedStartUrls` - number of unsupported URL entries ignored from `startUrls`.
- `stoppedByChargeLimit` - true when the run stopped because Apify's max total charge limit was reached.
- `chargeLimitEvent` - last pricing event reported when the charge limit was reached.
- `datasetUrl` - direct API link to the dataset.

The same important diagnostics are also printed in the Apify log as `Run diagnosis`.
Dataset rows also include `insideSearchArea` when a search-area boundary and
place coordinates were available.

Example:

```json
{
  "status": "INPUT_NEEDS_ATTENTION",
  "inputWarning": "Google Maps returned 80 raw place(s), but no rows matched the final criteria: 📍 80 outside the resolved search area.",
  "totalPlaces": 0,
  "diagnosticHints": [
    "Google returned only out-of-area results for 'restaurant'. Check Location spelling or use customGeolocation for an exact area."
  ]
}
```

### 🛠️ Troubleshooting

#### 🔍 The run succeeded but returned 0 places

Open the `OUTPUT` record or the Apify log and look for `Run diagnosis`.

Common causes:

- The location was misspelled or resolved to the wrong area.
- Google returned places outside the requested area and `geoStrictMatch=true` removed them.
- `skipClosedPlaces=true` removed places that Google marks as temporarily or permanently closed.
- The search term is too narrow for the selected location.

Try a broader search term, a clearer location such as `City, Country`, or disable strict/closed-place filtering if those options are too narrow for the run.

#### 📍 Location results look wrong

Use simple location text first:

```text
Los Angeles, California, United States
Helsinki, Finland
Brooklyn, New York, United States
```

If you need an exact area, use `customGeolocation` and enable `geoStrictMatch`.

#### 🧩 I need more than the visible Google Maps limit

Keep `enableSubdivision=true`. The Actor automatically splits dense areas into smaller viewports and deduplicates places by Google identifiers.

#### ✉️ I need emails only

Use:

```json
{
  "extractContactsFromWebsite": true
}
```

The Actor saves all scraped places. Filter the finished dataset by the `emails`
field to export only rows where website enrichment found an email.

### ⚡ Performance And Cost Tips

- Start with `maxCrawledPlacesPerSearch=50` for a quick test.
- Keep `maxReviewsPerPlace=0` unless reviews are needed.
- Enable contact enrichment only when you need emails or social profiles.
- Extra languages and extra zoom passes increase request volume.
- Higher concurrency is faster, but can use more proxy bandwidth.
- If you set a low Apify `maxTotalChargeUsd` run limit, the Actor stops gracefully when the limit is reached and writes a partial `OUTPUT` summary instead of continuing until the platform aborts the run.
- Check the Apify **Pricing** tab for current per-event pricing. You are charged according to the pricing events shown there.
- Pricing events are designed around delivered value: one base charge per saved place, one optional contact-details event when website enrichment finds emails or social profiles, and one optional review event per review actually attached.

### 🔌 Integrations

You can use the results through:

- Apify dataset exports: JSON, CSV, Excel, XML, RSS, and HTML.
- Apify API and webhooks.
- Scheduled runs.
- Make, Zapier, n8n, Google Sheets, Airtable, Slack, and other Apify integrations.
- Apify CLI or SDKs for automated workflows.

### ℹ️ Notes And Limitations

- Google Maps may return different rankings depending on language, location, and availability.
- Some businesses do not publish websites, emails, phone numbers, or reviews.
- Review metadata depends on what Google exposes for each place.
- Website enrichment extracts public contact details from the business website; it cannot create missing emails.
- This Actor is not affiliated with Google.

### 💬 Support

If a run does not return what you expected, check `OUTPUT.inputWarning`, `OUTPUT.diagnosticHints`, and the Apify log first. They usually explain whether the issue is location resolution, strict/closed-place filtering, out-of-area results, or an empty Google response.

For bugs or feature requests, use the Actor's Issues tab on Apify.

See [CHANGELOG.md](./CHANGELOG.md) for version history.

# Actor input Schema

## `searchStringsArray` (type: `array`):

Type what you would normally search for in Google Maps, for example <b>restaurant</b>, <b>recording studio</b>, <b>dentist</b>, or <b>taksi</b>. Add one search term per line.<br><br>💡 Use specific terms for cleaner results. Very similar terms may improve coverage, but they also make the run longer.<br><br>⚠️ Looking for a specific business name? Add a clear 📍 <b>Location</b> too, otherwise Google Maps may return the matching name from another city or country.

## `locationQueries` (type: `array`):

Define one or more search areas. Add one complete location per line, for example <b>Brooklyn, New York, United States</b>, <b>Seddon, Victoria, Australia</b>, or <b>Footscray, Victoria, Australia</b>.<br><br>Do not combine two cities in one line. Each location is geocoded separately, and <b>Number of places</b> applies per search term per location.<br><br>Simple formats usually work best: City + Country, or Neighborhood + City. Use 📡 <b>Geolocation parameters\*</b> only when this field is empty. Use 🛰 <b>Custom search area</b> with a GeoJSON MultiPolygon when you need exact custom boundaries.

## `geoStrictMatch` (type: `boolean`):

Turn this on to drop Google Maps results whose coordinates are outside the resolved 📍 <b>Location(s)</b>, 📡 <b>Geolocation parameters</b>, or 🛰 <b>Custom search area</b>.<br><br>For multiple locations, each result is checked against the specific location area that produced it. GeoJSON Polygon and MultiPolygon inputs are checked against the exact polygon. Small geocoded areas such as suburbs and neighborhoods use the exact OpenStreetMap boundary when available; broader geocoded areas and Point/radius inputs use the resolved bbox.<br><br>Strict mode requires each location to resolve to a real area boundary. If a location resolves only to a point, that location is skipped with a clear warning instead of returning unchecked rows.<br><br>Leave it off to keep all places Google returns for the query, including nearby results outside the area. The dataset will mark them with <code>insideSearchArea=false</code> when a boundary is available.

## `maxCrawledPlacesPerSearch` (type: `integer`):

Maximum number of places to keep for each search term in each location, or for each direct URL. Higher values take longer and may use more requests.<br><br>For quick tests, use <b>50</b>. For city-scale lead lists, use <b>500</b> or more.

## `language` (type: `string`):

Google Maps language used for names, categories, addresses, and labels.

## `skipClosedPlaces` (type: `boolean`):

Skip places that Google marks as temporarily or permanently closed. Useful when you only want currently active businesses.

## `extractContactsFromWebsite` (type: `boolean`):

Visit each business website and extract business emails, extra phone numbers, and social media links such as Facebook, Instagram, LinkedIn, X / Twitter, YouTube, TikTok, Pinterest, and WhatsApp. Places without websites are still saved; they simply cannot be website-enriched.

## `contactsFetchContactPage` (type: `boolean`):

If the homepage has no email, try up to five likely contact-style pages such as <code>/contact</code>, <code>/contact-us</code>, <code>/contacto</code>, <code>/sucursales</code>, <code>/ubicacion</code>, <code>/prensa</code>, <code>/booking</code>, <code>/kundservice</code>, or <code>/impressum</code>.

## `contactsProxyFallback` (type: `boolean`):

If a business website or contact page does not open directly, retry only that failed website request through Apify residential proxy. The Actor still tries direct website fetching first to keep enrichment fast, then uses proxy only as a recovery fallback for sites that block direct traffic. Turn this off if you want the lowest possible proxy bandwidth usage.

## `contactsFilterEmailsByWebsiteDomain` (type: `boolean`):

Enabled by default for cleaner leads: save emails that look related to the business website, including same-domain emails, close domain variants, and common inboxes such as Gmail/Yahoo when they are found on the business site.<br><br>Turn this off to save every valid-looking public email found on the visited homepage/contact pages, even if it belongs to a parent company, agency, vendor, platform, or another domain. Obvious fake/broken placeholders are still removed.

## `contactsSkipChains` (type: `boolean`):

Skip enrichment for large chains whose corporate websites rarely contain useful local branch emails.

## `contactsTimeoutSecs` (type: `integer`):

Maximum seconds to wait for each business website during contact enrichment.

## `maxReviewsPerPlace` (type: `integer`):

How many reviews to attach to each place. Set to <b>0</b> for the fastest run. Reviews are fetched from Google's paginated review feed; large values can take noticeably longer and use more requests.

## `reviewsSort` (type: `string`):

Requested review ordering for Google's paginated reviews endpoint. Google can still return its default ordering for some places.

## `additionalLanguages` (type: `array`):

Run the same search again in extra Google Maps languages, for example <code>fi</code>, <code>sv</code>, <code>es</code>, <code>de</code>. Each extra language adds another full pass and can uncover slightly different translated categories and rankings.

## `countryCode` (type: `string`):

Alternative way to define the search area. Used only when 📍 <b>Location</b> is empty. Use ISO country code, for example <code>US</code>, <code>FI</code>, <code>DE</code>.

## `city` (type: `string`):

City name used only when 📍 <b>Location</b> is empty.<br><br>⚠️ Do not include state or country here; use the separate fields.

## `state` (type: `string`):

State, province, or region used only when 📍 <b>Location</b> is empty. Mainly useful for countries such as the United States.

## `county` (type: `string`):

County or similar regional unit used only when 📍 <b>Location</b> is empty.

## `postalCode` (type: `string`):

ZIP / postal code used only when 📍 <b>Location</b> is empty.<br><br>⚠️ Use one postal code at a time.

## `customGeolocation` (type: `object`):

Define an exact custom search area with GeoJSON. Supported inputs: Polygon, MultiPolygon, or Point with <code>radiusKm</code>.<br><br>Use this when automatic city boundaries are too small, too large, or when you need a custom service area.

## `startUrls` (type: `array`):

Paste full Google Maps URLs for exact places or copied Google Maps search result pages.<br><br><b>Exact places:</b> open the place in Google Maps, click <b>Share</b>, then <b>Copy link</b>. Accepted: <code>google.com/maps/place/...</code>, <code>maps.app.goo.gl/...</code>, URLs with <code>query\_place\_id</code>, and URLs with <code>cid</code>.<br><br><b>Search pages:</b> copied <code>google.com/maps/search/.../@lat,lng,zoom</code> or <code>google.com/maps/search/.../@lat,lng,meters</code> URLs are scraped as searches in that visible map area. The Actor uses the search text and viewport from the URL; it does not guess one exact place.<br><br>If your link contains <code>/g/...</code>, <code>0x...:0x...</code>, or <code>cid=...</code>, keep it as a full URL here. Do not paste those identifiers into <b>Place IDs</b>.<br><br>Search URLs without <code>@lat,lng,zoom</code> or <code>@lat,lng,meters</code> are ignored because no search area is available.

## `placeIds` (type: `array`):

Paste only raw Google Place IDs, one per line.<br><br><b>Where to get them:</b> from Google Places API, Google's Place ID Finder, or from a Google Maps URL parameter named <code>query\_place\_id</code>.<br><br>Valid examples look like <code>ChIJreV9aqYWdkgROM\_boL6YbwA</code>, <code>GhIJQWDl0CIeQUARxks3icF8U8A</code>, or longer IDs returned by Places API.<br><br>Do <b>not</b> paste full URLs, short <code>/g/...</code> IDs such as <code>11lmncvyn7</code>, fids such as <code>0x317067ffdca15d1b:0x367befc8c87c6fef</code>, or numeric CIDs here. If you have any of those, paste the full Google Maps URL into <b>Google Maps URLs</b> instead.<br><br>The Actor accepts a Place ID only when Google returns the same exact <code>placeId</code>.

## `maxPlacesPerViewport` (type: `integer`):

Maximum places fetched from one Google Maps viewport. Values above 120 do not help because Google caps each viewport response.

## `enableSubdivision` (type: `boolean`):

When a map area is dense, split it into smaller quadrants to get past Google's visible-result cap. Recommended for city and region searches.

## `maxSubdivisionDepth` (type: `integer`):

How deep automatic area splitting can go. Higher values can find more places in dense cities, but use more requests.

## `multiZoomDelta` (type: `integer`):

Search nearby zoom levels around the resolved area. <b>0</b> disables this. <b>1</b> can improve coverage but roughly triples search requests.

## `zoom` (type: `integer`):

Override the automatically chosen Google Maps zoom level. Leave empty unless you need precise control.

## `reverseGeocodeMissingAddress` (type: `boolean`):

When Google returns coordinates but no address text, backfill the address from OpenStreetMap-based geocoding. This is free and cached.

## `concurrency` (type: `integer`):

How many scraping tasks run in parallel. Higher values are faster but use more proxy bandwidth.

## `requestTimeoutSecs` (type: `integer`):

Maximum seconds to wait for each Google request. Increase this if residential proxy responses are slow.

## `minRequestIntervalMs` (type: `integer`):

Minimum delay in milliseconds between Google requests. Lower values are faster; higher values are gentler when proxies are unstable.

## Actor input object example

```json
{
  "searchStringsArray": [
    "restaurant"
  ],
  "locationQueries": [
    "New York, USA"
  ],
  "geoStrictMatch": false,
  "maxCrawledPlacesPerSearch": 50,
  "language": "en",
  "skipClosedPlaces": false,
  "extractContactsFromWebsite": false,
  "contactsFetchContactPage": true,
  "contactsProxyFallback": true,
  "contactsFilterEmailsByWebsiteDomain": true,
  "contactsSkipChains": true,
  "contactsTimeoutSecs": 10,
  "maxReviewsPerPlace": 0,
  "reviewsSort": "newest",
  "additionalLanguages": [
    "fi"
  ],
  "countryCode": "US",
  "city": "Helsinki",
  "state": "California",
  "postalCode": "10001",
  "maxPlacesPerViewport": 80,
  "enableSubdivision": true,
  "maxSubdivisionDepth": 4,
  "multiZoomDelta": 0,
  "zoom": 13,
  "reverseGeocodeMissingAddress": true,
  "concurrency": 8,
  "requestTimeoutSecs": 30,
  "minRequestIntervalMs": 250
}
```

# Actor output Schema

## `places` (type: `string`):

Default dataset — one row per unique Google Maps place. Deduped by placeId across all viewports, languages, and search terms.

## `runSummary` (type: `string`):

JSON object with totals, unique placeIds, languages used, viewports searched, completed tasks, duration, diagnostic hints, and the filters that were applied.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "searchStringsArray": [
        "restaurant"
    ],
    "locationQueries": [
        "New York, USA"
    ],
    "maxCrawledPlacesPerSearch": 50,
    "language": "en"
};

// Run the Actor and wait for it to finish
const run = await client.actor("vortex_data/google-maps").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "searchStringsArray": ["restaurant"],
    "locationQueries": ["New York, USA"],
    "maxCrawledPlacesPerSearch": 50,
    "language": "en",
}

# Run the Actor and wait for it to finish
run = client.actor("vortex_data/google-maps").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "searchStringsArray": [
    "restaurant"
  ],
  "locationQueries": [
    "New York, USA"
  ],
  "maxCrawledPlacesPerSearch": 50,
  "language": "en"
}' |
apify call vortex_data/google-maps --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=vortex_data/google-maps",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Google Maps Scraper",
        "description": "Stop wasting your budget on slow, resource-heavy browser-based scrapers. This is the fastest, most cost-effective, and data-rich Google Maps scraper on Apify, designed for high-scale lead generation and market research.",
        "version": "1.6",
        "x-build-id": "nIE3A6kh1v2SnoHh1"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/vortex_data~google-maps/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-vortex_data-google-maps",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/vortex_data~google-maps/runs": {
            "post": {
                "operationId": "runs-sync-vortex_data-google-maps",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/vortex_data~google-maps/run-sync": {
            "post": {
                "operationId": "run-sync-vortex_data-google-maps",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "properties": {
                    "searchStringsArray": {
                        "title": "🔍 Search term(s)",
                        "uniqueItems": true,
                        "type": "array",
                        "description": "Type what you would normally search for in Google Maps, for example <b>restaurant</b>, <b>recording studio</b>, <b>dentist</b>, or <b>taksi</b>. Add one search term per line.<br><br>💡 Use specific terms for cleaner results. Very similar terms may improve coverage, but they also make the run longer.<br><br>⚠️ Looking for a specific business name? Add a clear 📍 <b>Location</b> too, otherwise Google Maps may return the matching name from another city or country.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "locationQueries": {
                        "title": "📍 Location(s)",
                        "maxItems": 50,
                        "uniqueItems": true,
                        "type": "array",
                        "description": "Define one or more search areas. Add one complete location per line, for example <b>Brooklyn, New York, United States</b>, <b>Seddon, Victoria, Australia</b>, or <b>Footscray, Victoria, Australia</b>.<br><br>Do not combine two cities in one line. Each location is geocoded separately, and <b>Number of places</b> applies per search term per location.<br><br>Simple formats usually work best: City + Country, or Neighborhood + City. Use 📡 <b>Geolocation parameters*</b> only when this field is empty. Use 🛰 <b>Custom search area</b> with a GeoJSON MultiPolygon when you need exact custom boundaries.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "geoStrictMatch": {
                        "title": "📍 Keep only places inside the search area",
                        "type": "boolean",
                        "description": "Turn this on to drop Google Maps results whose coordinates are outside the resolved 📍 <b>Location(s)</b>, 📡 <b>Geolocation parameters</b>, or 🛰 <b>Custom search area</b>.<br><br>For multiple locations, each result is checked against the specific location area that produced it. GeoJSON Polygon and MultiPolygon inputs are checked against the exact polygon. Small geocoded areas such as suburbs and neighborhoods use the exact OpenStreetMap boundary when available; broader geocoded areas and Point/radius inputs use the resolved bbox.<br><br>Strict mode requires each location to resolve to a real area boundary. If a location resolves only to a point, that location is skipped with a clear warning instead of returning unchecked rows.<br><br>Leave it off to keep all places Google returns for the query, including nearby results outside the area. The dataset will mark them with <code>insideSearchArea=false</code> when a boundary is available.",
                        "default": false
                    },
                    "maxCrawledPlacesPerSearch": {
                        "title": "💯 Number of places to extract (per search term, location, or URL)",
                        "minimum": 1,
                        "maximum": 100000,
                        "type": "integer",
                        "description": "Maximum number of places to keep for each search term in each location, or for each direct URL. Higher values take longer and may use more requests.<br><br>For quick tests, use <b>50</b>. For city-scale lead lists, use <b>500</b> or more.",
                        "default": 50
                    },
                    "language": {
                        "title": "🌍 Language",
                        "enum": [
                            "en",
                            "fi",
                            "sv",
                            "es",
                            "fr",
                            "de",
                            "it",
                            "pt",
                            "ru",
                            "uk",
                            "pl",
                            "tr",
                            "nl",
                            "ja",
                            "ko",
                            "zh-CN",
                            "ar",
                            "hi"
                        ],
                        "type": "string",
                        "description": "Google Maps language used for names, categories, addresses, and labels.",
                        "default": "en"
                    },
                    "skipClosedPlaces": {
                        "title": "⏩ Skip closed places",
                        "type": "boolean",
                        "description": "Skip places that Google marks as temporarily or permanently closed. Useful when you only want currently active businesses.",
                        "default": false
                    },
                    "extractContactsFromWebsite": {
                        "title": "⏩ Add-on: Company contacts enrichment (from website)",
                        "type": "boolean",
                        "description": "Visit each business website and extract business emails, extra phone numbers, and social media links such as Facebook, Instagram, LinkedIn, X / Twitter, YouTube, TikTok, Pinterest, and WhatsApp. Places without websites are still saved; they simply cannot be website-enriched.",
                        "default": false
                    },
                    "contactsFetchContactPage": {
                        "title": "Try a contact page if homepage has no email",
                        "type": "boolean",
                        "description": "If the homepage has no email, try up to five likely contact-style pages such as <code>/contact</code>, <code>/contact-us</code>, <code>/contacto</code>, <code>/sucursales</code>, <code>/ubicacion</code>, <code>/prensa</code>, <code>/booking</code>, <code>/kundservice</code>, or <code>/impressum</code>.",
                        "default": true
                    },
                    "contactsProxyFallback": {
                        "title": "Retry failed website contact pages with proxy",
                        "type": "boolean",
                        "description": "If a business website or contact page does not open directly, retry only that failed website request through Apify residential proxy. The Actor still tries direct website fetching first to keep enrichment fast, then uses proxy only as a recovery fallback for sites that block direct traffic. Turn this off if you want the lowest possible proxy bandwidth usage.",
                        "default": true
                    },
                    "contactsFilterEmailsByWebsiteDomain": {
                        "title": "Keep only emails related to the website",
                        "type": "boolean",
                        "description": "Enabled by default for cleaner leads: save emails that look related to the business website, including same-domain emails, close domain variants, and common inboxes such as Gmail/Yahoo when they are found on the business site.<br><br>Turn this off to save every valid-looking public email found on the visited homepage/contact pages, even if it belongs to a parent company, agency, vendor, platform, or another domain. Obvious fake/broken placeholders are still removed.",
                        "default": true
                    },
                    "contactsSkipChains": {
                        "title": "Skip global chains during contact enrichment",
                        "type": "boolean",
                        "description": "Skip enrichment for large chains whose corporate websites rarely contain useful local branch emails.",
                        "default": true
                    },
                    "contactsTimeoutSecs": {
                        "title": "Website timeout",
                        "minimum": 3,
                        "maximum": 60,
                        "type": "integer",
                        "description": "Maximum seconds to wait for each business website during contact enrichment.",
                        "default": 10
                    },
                    "maxReviewsPerPlace": {
                        "title": "Number of reviews to extract",
                        "minimum": 0,
                        "maximum": 1000,
                        "type": "integer",
                        "description": "How many reviews to attach to each place. Set to <b>0</b> for the fastest run. Reviews are fetched from Google's paginated review feed; large values can take noticeably longer and use more requests.",
                        "default": 0
                    },
                    "reviewsSort": {
                        "title": "Sort reviews by",
                        "enum": [
                            "newest",
                            "most_relevant",
                            "highest",
                            "lowest"
                        ],
                        "type": "string",
                        "description": "Requested review ordering for Google's paginated reviews endpoint. Google can still return its default ordering for some places.",
                        "default": "newest"
                    },
                    "additionalLanguages": {
                        "title": "➕ Extra languages",
                        "uniqueItems": true,
                        "type": "array",
                        "description": "Run the same search again in extra Google Maps languages, for example <code>fi</code>, <code>sv</code>, <code>es</code>, <code>de</code>. Each extra language adds another full pass and can uncover slightly different translated categories and rankings.",
                        "items": {
                            "type": "string"
                        },
                        "default": []
                    },
                    "countryCode": {
                        "title": "🗺 Country",
                        "type": "string",
                        "description": "Alternative way to define the search area. Used only when 📍 <b>Location</b> is empty. Use ISO country code, for example <code>US</code>, <code>FI</code>, <code>DE</code>."
                    },
                    "city": {
                        "title": "🌇 City",
                        "type": "string",
                        "description": "City name used only when 📍 <b>Location</b> is empty.<br><br>⚠️ Do not include state or country here; use the separate fields."
                    },
                    "state": {
                        "title": "State / region",
                        "type": "string",
                        "description": "State, province, or region used only when 📍 <b>Location</b> is empty. Mainly useful for countries such as the United States."
                    },
                    "county": {
                        "title": "County",
                        "type": "string",
                        "description": "County or similar regional unit used only when 📍 <b>Location</b> is empty."
                    },
                    "postalCode": {
                        "title": "Postal code",
                        "type": "string",
                        "description": "ZIP / postal code used only when 📍 <b>Location</b> is empty.<br><br>⚠️ Use one postal code at a time."
                    },
                    "customGeolocation": {
                        "title": "🛰 Custom search area (GeoJSON)",
                        "type": "object",
                        "description": "Define an exact custom search area with GeoJSON. Supported inputs: Polygon, MultiPolygon, or Point with <code>radiusKm</code>.<br><br>Use this when automatic city boundaries are too small, too large, or when you need a custom service area."
                    },
                    "startUrls": {
                        "title": "🔗 Google Maps URLs",
                        "type": "array",
                        "description": "Paste full Google Maps URLs for exact places or copied Google Maps search result pages.<br><br><b>Exact places:</b> open the place in Google Maps, click <b>Share</b>, then <b>Copy link</b>. Accepted: <code>google.com/maps/place/...</code>, <code>maps.app.goo.gl/...</code>, URLs with <code>query_place_id</code>, and URLs with <code>cid</code>.<br><br><b>Search pages:</b> copied <code>google.com/maps/search/.../@lat,lng,zoom</code> or <code>google.com/maps/search/.../@lat,lng,meters</code> URLs are scraped as searches in that visible map area. The Actor uses the search text and viewport from the URL; it does not guess one exact place.<br><br>If your link contains <code>/g/...</code>, <code>0x...:0x...</code>, or <code>cid=...</code>, keep it as a full URL here. Do not paste those identifiers into <b>Place IDs</b>.<br><br>Search URLs without <code>@lat,lng,zoom</code> or <code>@lat,lng,meters</code> are ignored because no search area is available.",
                        "items": {
                            "type": "object",
                            "required": [
                                "url"
                            ],
                            "properties": {
                                "url": {
                                    "type": "string",
                                    "title": "URL of a web page",
                                    "format": "uri"
                                }
                            }
                        }
                    },
                    "placeIds": {
                        "title": "🗃 Place IDs",
                        "uniqueItems": true,
                        "type": "array",
                        "description": "Paste only raw Google Place IDs, one per line.<br><br><b>Where to get them:</b> from Google Places API, Google's Place ID Finder, or from a Google Maps URL parameter named <code>query_place_id</code>.<br><br>Valid examples look like <code>ChIJreV9aqYWdkgROM_boL6YbwA</code>, <code>GhIJQWDl0CIeQUARxks3icF8U8A</code>, or longer IDs returned by Places API.<br><br>Do <b>not</b> paste full URLs, short <code>/g/...</code> IDs such as <code>11lmncvyn7</code>, fids such as <code>0x317067ffdca15d1b:0x367befc8c87c6fef</code>, or numeric CIDs here. If you have any of those, paste the full Google Maps URL into <b>Google Maps URLs</b> instead.<br><br>The Actor accepts a Place ID only when Google returns the same exact <code>placeId</code>.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "maxPlacesPerViewport": {
                        "title": "Places per map viewport",
                        "minimum": 1,
                        "maximum": 120,
                        "type": "integer",
                        "description": "Maximum places fetched from one Google Maps viewport. Values above 120 do not help because Google caps each viewport response.",
                        "default": 80
                    },
                    "enableSubdivision": {
                        "title": "Split dense areas automatically",
                        "type": "boolean",
                        "description": "When a map area is dense, split it into smaller quadrants to get past Google's visible-result cap. Recommended for city and region searches.",
                        "default": true
                    },
                    "maxSubdivisionDepth": {
                        "title": "Maximum split depth",
                        "minimum": 0,
                        "maximum": 7,
                        "type": "integer",
                        "description": "How deep automatic area splitting can go. Higher values can find more places in dense cities, but use more requests.",
                        "default": 4
                    },
                    "multiZoomDelta": {
                        "title": "Extra zoom passes",
                        "minimum": 0,
                        "maximum": 3,
                        "type": "integer",
                        "description": "Search nearby zoom levels around the resolved area. <b>0</b> disables this. <b>1</b> can improve coverage but roughly triples search requests.",
                        "default": 0
                    },
                    "zoom": {
                        "title": "Override zoom level",
                        "minimum": 1,
                        "maximum": 21,
                        "type": "integer",
                        "description": "Override the automatically chosen Google Maps zoom level. Leave empty unless you need precise control."
                    },
                    "reverseGeocodeMissingAddress": {
                        "title": "Fill missing addresses from coordinates",
                        "type": "boolean",
                        "description": "When Google returns coordinates but no address text, backfill the address from OpenStreetMap-based geocoding. This is free and cached.",
                        "default": true
                    },
                    "concurrency": {
                        "title": "Concurrent workers",
                        "minimum": 1,
                        "maximum": 32,
                        "type": "integer",
                        "description": "How many scraping tasks run in parallel. Higher values are faster but use more proxy bandwidth.",
                        "default": 8
                    },
                    "requestTimeoutSecs": {
                        "title": "Google request timeout",
                        "minimum": 5,
                        "maximum": 120,
                        "type": "integer",
                        "description": "Maximum seconds to wait for each Google request. Increase this if residential proxy responses are slow.",
                        "default": 30
                    },
                    "minRequestIntervalMs": {
                        "title": "Delay between Google requests",
                        "minimum": 0,
                        "maximum": 5000,
                        "type": "integer",
                        "description": "Minimum delay in milliseconds between Google requests. Lower values are faster; higher values are gentler when proxies are unstable.",
                        "default": 250
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
