# 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:** 2,498 total users, 741 monthly users, 99.8% runs succeeded, 43 bookmarks
- **User rating**: 4.84 out of 5 stars

## Pricing

from $0.70 / 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.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — 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** extracts local businesses from [Google Maps](https://www.google.com/maps). No API key, no quota. One place per row: name, category, address, coordinates, phone, website, rating and opening hours, plus emails and reviews when you want them.

- 🔍 **Search:** by keyword and place, `dentist` in `Austin, Texas, United States`
- 🗺 **Or by a shape you draw:** a polygon, a radius, a Google Maps URL, a Place ID
- 🧲 **Contacts:** emails, phones and socials from each business's own website
- ⭐ **Reviews:** text, rating, author, date
- 🌍 **Any country, any language:** local names, local categories
- 🎯 **One business, one row:** deduplicated across the whole run
- 💳 **Pricing:** per result, never per hour
- 📤 **Export:** JSON, CSV, Excel, XML, HTML, or straight from the API

### How to scrape Google Maps

```json
{
  "searchStringsArray": ["plumber", "electrician"],
  "locationQueries": ["Austin, Texas, United States", "Dallas, Texas, United States"],
  "maxCrawledPlacesPerSearch": 300
}
```

Two search terms × two locations × 300 = **up to 1,200 places**. Press **Start**. Rows show up in the **Output** tab while the run is still going.

**Good search terms.** Different trades, each finding places the others miss:
`["plumber", "electrician", "hvac contractor", "roofing contractor"]`

**Poor search terms.** Variants of one word, costing time without adding places:
`["plumber", "plumbers", "plumbing", "plumber near me"]`

| Write this | Not this |
| :--- | :--- |
| `dentist` | `dentist in Austin`. The term is searched in *every* location |
| One location per line | `Austin, Dallas`. Read as one name |
| `Paris, Texas, USA` | `Paris`. A shared name resolves to the biggest one |
| `50` on your first run | `5000` before you have seen the rows |

### Output

#### Google Maps Scraper output example

```json
{
  "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",
  "postalCode": "10036",
  "countryCode": "US",
  "location": { "lat": 40.7602998, "lng": -73.9907758 },
  "phone": "+1 917-265-8444",
  "website": "https://www.birdandbranch.com/",
  "totalScore": 4.6,
  "reviewsCount": 412,
  "currentStatus": "Closed",
  "openingHoursToday": { "day": "Thursday", "hours": "7 AM–5 PM" },
  "placeId": "ChIJTVhsxFNYwokRXgPwYnY0vgI",
  "url": "https://www.google.com/maps/search/?api=1&query=Bird+%26+Branch&query_place_id=ChIJTVhsxFNYwokRXgPwYnY0vgI",
  "searchString": "coffee shop",
  "scrapedAt": "2026-08-27T10:30:00Z"
}
```

If a column is empty, Google didn't have it.

#### What data does Google Maps Scraper extract?

| Group | Columns |
| :--- | :--- |
| **Identity** | `title` `categoryName` `categories` `categoryId` `description` `subTitle` |
| **Address** | `address` `street` `city` `postalCode` `state` `countryCode` `neighborhood` `plusCode` `timezone` |
| **Position** | `location` `entranceLocation` |
| **Contact** | `phone` `phoneUnformatted` `website` `websiteDisplay` |
| **Reputation** | `totalScore` `reviewsCount` `reviewsUrl` |
| **Open or closed** | `openingHoursToday` `currentStatus` `nextOpensAt` `permanentlyClosed` `temporarilyClosed` |
| **Photos** | `imageUrl` `imageUrls` `imagesCount` `imageCategories` |
| **Hotels** | `hotelStars` `hotelPrice` `hotelAmenities` `hotelDescription` `hotelCheckInDate` `hotelCheckOutDate` |
| **Commerce** | `menu` `price` `reserveTableUrl` `bookingLinks` |
| **Listing** | `ownerName` `claimThisBusinessUrl` `placeTags` `additionalInfo` `isAdvertisement` |
| **Identifiers** | `placeId` `cid` `fid` `kgmid` `url` |
| **Run** | `searchString` `rank` `language` `scrapedAt` `searchPageUrl` |

`categoryId` reads identically in every language: `jewelry_store` in Tokyo and in Lisbon.

The **Output** tab has ready-made views: *Overview*, *Lead generation*, *Reviews*, *Photo gallery*, *Hotels*, *Full details*.

### Add-ons

#### Scrape emails and contacts from Google Maps

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

Opens each place's own website and adds `emails` `additionalPhones` `whatsapps` `facebooks` `instagrams` `linkedIns` `twitters` `youtubes` `tiktoks` `pinterests` `tripadvisors` `yelps` `foursquares`.

- It keeps addresses that belong to the site. One on a different domain only survives if the page clearly presents it as a contact, so you don't end up with the web agency that built the site.
- The list is ordered most useful first, so the first address is the one to write to.
- You're charged only where it found something.
- Expect fewer emails than places. Plenty of businesses have no website, and plenty of websites never show an address.
- It skips two dozen global chains. A head office isn't the branch you found.

#### Scrape Google Maps reviews

```json
{ "maxReviewsPerPlace": 20, "reviewsSort": "newest" }
```

You get the text and the rating, when it was written, and who wrote it: their name, their profile link, how many reviews and photos they've posted, whether they're a Local Guide. Plus the language it was written in, and any photos attached.

Sort by `newest`, `most_relevant`, `highest` or `lowest`. It asks for up to `8000` per place, and `reviewsScrapedCount` tells you how many actually arrived.

**Each review is charged.** 20 reviews × 100 places = 2,000 charged reviews, and the run takes considerably longer. Start low.

#### Scrape full place details from Google Maps

```json
{ "extractPlaceDetails": true }
```

Opens each saved place's own Maps page and adds `openingHours` for the whole week, `reviewsDistribution` `reviewsTags` `popularTimesHistogram` `timeSpent` `editorialSummary` `peopleAlsoSearch` — plus `gasPrices` at fuel stations and `evConnectors` at charge points.

- One extra request per saved place, so the run takes longer. Budget for it.
- You're charged only where the page had something to add.
- It fills in `reviewsCount` where the search answer came back without one, from the star breakdown on the place's own page.

#### Scrape every photo from Google Maps

```json
{ "maxPhotosPerPlace": 30 }
```

A search result carries one photo whatever the place is. This adds `photoGallery` (each photo as `{url, source}`), `photoGalleryUrls` (a flat list to download from) and `photoGalleryCount`.

Expect anywhere from a handful to a few hundred: a village store has three, a national museum has hundreds. `0` is off, `300` is the most you can ask for.

- The place's own photos come first, then the ones visitors attached to reviews. The single photo a search result shows stays in `imageUrl`.
- It rides the same request as the details add-on, so switching both on costs one request per place, not two.
- You're charged once per place whose gallery came back, never per photo. Asking for 240 costs the same as asking for 3.
- It's every photo the place's own record carries, which isn't every photo Google holds for it.

### Search by area, URL or Place ID

#### Scrape a custom area, polygon or radius

For a delivery zone, a sales territory, or a radius around a point.

```json
{ "customGeolocation": { "type": "Point", "coordinates": [-74.006, 40.7128], "radiusKm": 5 } }
```

Paste whatever geojson.io, QGIS or Google My Maps gives you: a `Point` with `radiusKm`, a `Polygon`, a `MultiPolygon`, a `Feature` or a `FeatureCollection`. Draw something smaller than a district and it can come back empty.

#### Scrape Google Maps by URL

A CRM export, a supplier list, competitors you watch.

```json
{ "startUrls": [{ "url": "https://maps.app.goo.gl/xxxx", "userData": { "crmId": "A-1042" } }] }
```

**Accepts:** a Google Maps share link · a `…/maps/place/…` URL · a copied search page with a map view.

**Returns:** `userData` back as `inputUserData`, so results join onto your own records.

#### Scrape Google Maps by Place ID

```json
{ "placeIds": ["ChIJreV9aqYWdkgROM_boL6YbwA"] }
```

Full `ChIJ…` identifiers only, from the Places API or Google's Place ID Finder. Short `/g/…` ids aren't Place IDs and neither are URLs. If you've got a URL, paste it as a URL.

### Results

#### How many results will Google Maps Scraper return?

`maxCrawledPlacesPerSearch` applies **per search term, per location**, so terms × locations × the limit is a ceiling rather than a forecast.

You get fewer when:

- **The area holds less.** Forty coffee shops is forty coffee shops. Past that, Google pads its answer with unrelated places, saved and charged like any other unless you switch **Only what you searched for** on.
- **Search terms overlap.** A place two terms both find is one row, not two.

Extra languages add places rather than copies. Each one searches the same ground again, and you only get what the earlier pass missed.

Every run publishes a map of its results, showing the area it searched with its places on it — as many as a browser can open, and the legend counts any that did not fit. You can look at your coverage instead of guessing at it.

#### Filter Google Maps results

| In the form | Field | Effect |
| :--- | :--- | :--- |
| 📍 Only places inside the area | `geoStrictMatch` | Drops everything outside the search area. A place Google files under your city counts as inside. |
| 🚚 Keep unlocated places | `keepUnverifiedLocations` | Keeps places Google gives no address: a plumber with a van, a studio run from home. **Only acts when the setting above is on**, which otherwise drops them. |
| ⏩ Skip closed places | `skipClosedPlaces` | Drops places Google marks closed, **temporarily as well as permanently**. |
| ⏩ Only what you searched for | `skipPlacesNotMatchingSearch` | Drops places it recognises as unrelated. What it cannot judge is kept. |

You're not charged for anything these drop, and it doesn't count against your limit either. The run just keeps looking.

With **Only what you searched for** on, every place kept carries `searchTermMatch`:

| Value | Meaning |
| :--- | :--- |
| `match` | The search term names this kind of place |
| `arguable` | Only this run's own results vouch for it |
| `not-a-match` | Not what you searched for |
| `not-judged` | Not enough evidence to decide. Kept, and charged |

It gets rid of most of the obviously unrelated ones and rarely throws away something you wanted. It isn't a guarantee of a clean list, though. Anything it can't judge, it keeps, and you pay for that. A search term that names a kind of place (`bakery`, `pest control service`) judges well; a vague phrase can leave almost every place undecided.

### Pricing

#### How much does it cost to scrape Google Maps?

Charged per result, never per hour. The **Pricing** tab shows current rates.

| Event | Charged |
| :--- | :--- |
| Place scraped | Per row in your dataset |
| Place with contact details | Only where the add-on found something |
| Review scraped | Per review |
| Place with full details | Only where the page had something to add |
| Place with full photo gallery | Once per place, never per photo |
| Actor start | Once per gigabyte of the run's memory |

Every add-on is off by default. Set **maxTotalChargeUsd** before you start and the run stops cleanly when it gets there, keeping everything it found.

### Limits and speed

- Places limit: `10` minimum, no maximum.
- Rows are saved as they're found, so a run that stops early keeps everything up to that point.
- Raise the run's timeout for multi-city jobs, or it'll stop before it's done.
- The **Setup** block in the log prints what each location resolved to, and flags ambiguous ones.

### FAQ

#### Do I need a Google Maps API key?

No. No Google billing account, no quota, nothing to apply for.

#### Why did I get fewer places than I asked for?

Four reasons, and the run's summary names which one:

- The area holds fewer places than you asked for.
- Several of your search terms found the same place, so you get it once.
- A filter dropped rows. You weren't charged for them.
- The run hit its timeout or your charge ceiling. Everything found by then is saved.

#### Why are the results not what I searched for?

Once an area runs out of what you asked for, Google keeps answering with whatever else is nearby, and those places get saved like any other. Switch **Only what you searched for** on and they're dropped before they reach you, at no charge. Whatever stays carries a `searchTermMatch` verdict.

#### Why are there places from the next town?

Google returns what it finds relevant, and near a boundary that takes in the neighbours. Leave **Only places inside the area** off and they arrive marked `insideSearchArea: false`. Switch it on and they're dropped.

Switch **Keep unlocated places** on with it, or you also lose every mobile trade. They have no coordinates to check.

#### My run returned nothing at all. What went wrong?

Usually one of four. The location resolved to a different place of the same name. Your search term is too specific to be a category anyone's filed under. A filter removed everything. Or the Place IDs and URLs weren't valid places. The run writes one row saying which, marked `recordType: "run-diagnostic"`.

#### Why are some emails missing or wrong?

Emails come off each place's own website. No website, no email, and a website that never publishes an address gives you nothing either.

By default it keeps only addresses on that site's own domain, which is what stops an agency's address landing on its client's row.

#### How do I make a run faster?

The run tells you what is holding it up. Near the top of the log, **Run plan** prints a **Bounded by** line with one of two answers.

- **`this memory's CPU`.** Raise the run's memory. Apify grants CPU in proportion to it, and the Actor sizes its own worker pools from the CPU it is given, so a larger machine searches more areas at once.
- **`the far end`.** It is waiting on Google, not on itself. More memory buys nothing here.

#### Can I run this on a schedule or from my own code?

Yes. Schedule it from the Actor page, or start it through the [Apify API](https://docs.apify.com/api/v2).

#### Is it legal to scrape Google Maps?

It collects business listings Google Maps shows to anyone, plus what a business publishes on its own website. It logs into nothing and takes no personal data.

What you may do with that data depends on where you and the businesses are. Apify's guide to [ethical web scraping](https://blog.apify.com/what-is-ethical-web-scraping-and-how-do-you-do-it/) is a good start; your own case is a question for a lawyer.

### Support

Read the run first: the **Log** ends with what was searched, saved and dropped, and the `OUTPUT` record says the same via the API.

Still wrong? Open the **Issues** tab and include the run. Release notes are on **Changelog**.

Includes a place-category taxonomy © Overture Maps Foundation, [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/).

# Actor input Schema

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

What you would type into Google Maps: <code>restaurant</code>, <code>dentist</code>, <code>recording studio</code>. One per line — each term is searched in every Location.<br><br>Leave the city out of the term. A term that names the place searches for it twice and returns less.

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

One place per line: <code>New York, USA</code>, <code>Brooklyn, New York, United States</code>, <code>Bavaria, Germany</code>. Each is searched on its own, and the places limit below applies to every term in every location.<br><br>City plus country works best. Two places on one line resolve to neither.

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

How many places to keep for each search term in each location. Use <b>50</b> for a quick test, <b>500</b> or more for a city-scale lead list.<br><br>Asking for more than an area holds makes the run longer, not richer: Google starts answering with whatever else is nearby.

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

Drops every place outside the search area — not saved, not charged. A place just past the boundary still counts as inside when Google files it under the city you asked for.<br><br>Off: nearby towns are kept too, marked <code>insideSearchArea=false</code>.

## `keepUnverifiedLocations` (type: `boolean`):

A mobile plumber or a studio run from home has no address on Google and a placeholder coordinate, so strict matching has nothing to check and drops it. On: kept anyway, flagged <code>locationUnverified=true</code>, with an empty address.<br><br>Often the best rows for lead generation.

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

Leaves out places Google marks temporarily or permanently closed.

## `skipPlacesNotMatchingSearch` (type: `boolean`):

When an area runs out of what you asked for, Google pads the answer with whatever else is there. Turn this on to drop those rows, never charged.<br><br>Not a 100% guarantee: it judges best when a term names a kind of place (<code>dentist</code>, <code>coffee shop</code>); vague terms may stay unjudged. Only with this on does a row carry <code>searchTermMatch</code>, the verdict it was judged by.

## `languages` (type: `array`):

The language Google answers in: names, categories, addresses.<br><br>Add more to search the same ground again in another language — each pass surfaces places the previous one buried, and costs another pass. Not in the list? Type any ISO code (<code>cs</code>, <code>pt-BR</code>) and press Enter.

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

Takes emails, extra phone numbers and social links from the business's own site. The homepage first; if it shows no email, up to five of the pages the site links as its contact, stopping at the first with an address. Never a crawl.<br><br>Only works where a place has a website. Enriched places are charged separately.

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

How many reviews to attach to each place. <b>0</b> is the fastest run; large values make every place slower and its row much bigger.

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

Which reviews come first. Google returns its own order for some places whatever is asked.

## `extractPlaceDetails` (type: `boolean`):

Opens each saved place's own Maps page for what a search never carries: hours for the whole week, the 1-5 star breakdown behind the average, the words reviewers repeat, how busy it is by hour and day, the editorial summary, and fuel prices or EV connectors where a place has them.<br><br>One extra request per place, charged separately.

## `maxPhotosPerPlace` (type: `integer`):

How many photos to take from each saved place's own gallery. <b>0</b> is off. A search result carries one photo whatever the place is; this returns what the place holds — 3 for a village shop, 240 for a national museum.<br><br>It rides the same request as the details add-on. Charged once per place whose gallery came back, not per photo.

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

An exact area instead of a named place — a district you drew, a delivery zone, a radius around a point.<br><br>Paste what your map editor exported (geojson.io, QGIS, My Maps): <code>FeatureCollection</code>, <code>Feature</code>, <code>Polygon</code>, <code>MultiPolygon</code>, or a <code>Point</code> with <code>radiusKm</code>. Several shapes are all searched; very small ones can come back empty.

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

One URL per line.<br><br>A place: open it and use Share → Copy link (<code>maps.app.goo.gl/…</code> or <code>…/maps/place/…</code>). A copied search page (<code>…/maps/search/coffee/@60.17,24.94,13z</code>) is scraped as that map view. Short links are followed for you.

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

One per line, from the Places API, Google's Place ID Finder, or the <code>query\_place\_id</code> parameter of a Maps URL.<br><br>Full IDs only — not URLs, not short <code>/g/…</code> ids, not <code>fid</code>s.

## Actor input object example

```json
{
  "searchStringsArray": [
    "dentist",
    "orthodontist"
  ],
  "locationQueries": [
    "Brooklyn, New York, United States",
    "Jersey City, NJ"
  ],
  "maxCrawledPlacesPerSearch": 500,
  "geoStrictMatch": true,
  "keepUnverifiedLocations": true,
  "skipClosedPlaces": false,
  "skipPlacesNotMatchingSearch": false,
  "languages": [
    "en",
    "es"
  ],
  "extractContactsFromWebsite": true,
  "maxReviewsPerPlace": 10,
  "reviewsSort": "newest",
  "extractPlaceDetails": true,
  "maxPhotosPerPlace": 30,
  "customGeolocation": {
    "type": "Point",
    "coordinates": [
      -73.9857,
      40.7484
    ],
    "radiusKm": 3
  },
  "startUrls": [
    {
      "url": "https://www.google.com/maps/place/?q=place_id:ChIJN1t_tDeuEmsRUsoyG83frY4"
    },
    {
      "url": "https://www.google.com/maps/search/coffee/@60.1699,24.9384,13z"
    }
  ],
  "placeIds": [
    "ChIJN1t_tDeuEmsRUsoyG83frY4",
    "ChIJreV9aqYWdkgROM_boL6YbwA"
  ]
}
```

# Actor output Schema

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

Default dataset: one row per unique Google Maps place, plus a clearly marked non-place diagnostic row when a zero-place run needs to explain what happened. Place rows are deduped by placeId across all viewports, languages, and search terms.

## `resultsMap` (type: `string`):

Interactive map of the run: the search area drawn as a polygon, with the places plotted on top of it and coloured by whether they fall inside that area. A run bigger than a browser can open plots as many as it can and counts the rest in the legend. Places whose coordinates Google only fills with a placeholder (unlocated places, marked locationUnverified) are counted in the legend rather than pinned to a location that does not exist.

## `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,
    "languages": [
        "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,
    "languages": ["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(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).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,
  "languages": [
    "en"
  ]
}' |
apify call vortex_data/google-maps --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,vortex_data/google-maps"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/AabCualFIriz3X6Fs/builds/7u514eesoBWm1l7XA/openapi.json
