# Google Places Search (`harpoon/google-places-search`) Actor

Extract business and place data for any area or free-text query — names, addresses, phones, websites, opening hours, ratings, and review counts.

- **URL**: https://apify.com/harpoon/google-places-search.md
- **Developed by:** [Harpoon](https://apify.com/harpoon) (community)
- **Stats:** 2 total users, 1 monthly users, 28.6% runs succeeded, 1 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.50 / 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 Places Search

Turn any area — or a plain-English sentence — into a structured list of Google Maps places: **name, address, phone, website, opening hours, rating, review count, and coordinates.** Clean, complete, and ready to use.

- **Reliable, complete data.** One row per place with contact details, hours, ratings, and coordinates — no duplicate records.
- **Search any shape of area.** A rectangle, a circle, a named region, or a custom polygon — or just describe the search in a sentence.
- **Filter before you pay.** Rating, open-now, price level, and category filters are applied for you, so you don't pay to return places you'll throw away.
- **Watch it work.** A live map plots each place as it's found while the run is running.
- **Any scale.** A handful of places or a whole country, exported to JSON, CSV, Excel, or XML.

> Built for lead generation, competitor tracking, local market analysis, and place-data enrichment.

### What can Google Places Search do?

| Input mode | What it does |
|------------|--------------|
| `ai_prompt` | Describe what you want in one sentence; the actor works out the search terms, area, and filters for you. |
| `search_terms` (+ an area) | Find every place matching each term inside an area; results are merged with no duplicates. |
| `categories` | Restrict to Google place types (restaurant, cafe, hotel, pharmacy, gym, …). |
| `place_ids` | Skip the search and fetch full details for places you already know. |

Every run also gives you export to JSON / CSV / Excel / XML, API and webhook access, scheduled runs, and MCP access for AI agents.

### What data can you extract?

One row per place. Grouped by what buyers usually need:

| 📦 Group | 🔎 Fields |
|----------|-----------|
| **Identity** | `name`, `name_alt`, `place_id`, `group.name` (category), `price` (food/drink only) |
| **Contact** | `phone.display`, `phone.actual`, `website.url`, `website.display` |
| **Location** | `full_address`, `address_parts`, `latitude`, `longitude`, `plus_code`, `maps_url` (opens the place in Google Maps), `thumbnail_url` (map preview image) |
| **Reputation** | `rating`, `reviews` |
| **Hours** | `open_status`, `is_open`, `opening_hours[]` (`day` + `hours[]`) |
| **Meta** | `lang` |

### How to use Google Places Search

1. [Create](https://console.apify.com/sign-up) a free Apify account.
2. Open **Google Places Search** in Apify Console.
3. Enter an area and either **Search terms** or a **plain-language prompt**.
4. Set **Max results** (`limit`) and any filters you want.
5. Click **Save & Start**. Results stream into the dataset as they're found.
6. Download from **Storage ▸ Dataset** as JSON, CSV, Excel, or XML.

While the run is going, open the live map URL from the run log to watch results appear. A snapshot of the map is saved afterward under the key-value store key `live-map`.

### Input

There are three ways to give the actor work — **use one**:

- **Natural language** — set `ai_prompt` only, e.g. `"Cheap hostels within 5 km of Bondi Beach in Sydney"`. The actor turns it into terms, an area, and filters, and figures out the location for you.
- **Search** — set `search_terms` (and/or `categories`) plus exactly one area selector.
- **Direct lookup** — set `place_ids` to skip searching entirely.

#### The area (provide exactly one)

| Field | Meaning | Example |
|-------|---------|---------|
| `bounds` | Rectangle, `lowLat,lowLng,highLat,highLng` | `48.80,2.25,48.90,2.40` |
| `circle` | Point + radius in km | `48.8566,2.3522,5` |
| `region` | Named region as `country:ADMtype:shapeID` | `FRA:ADM1:12345` |
| `polygon` | GeoJSON `coordinates` array (`[lng,lat]` rings) as a JSON string | `[[[2.25,48.80],[2.40,48.90],[2.25,48.90],[2.25,48.80]]]` |

> When `ai_prompt` names a location, its area wins and any manual area selector is ignored.

#### Result filters

Narrow the search with `min_rating`, `open_now`, `price_levels`, `categories`, `ranking` (`relevance` / `distance`), `country` bias, `strict_type`, `service_area_businesses`, `future_openings`, and `language`.

#### Post-request filters

Some conditions are checked against each place's full details rather than the search itself. `post_filters` maps a detail field to a condition. Both filter groups are free — you're only charged per place returned, never for filtering.

| Fields | Operators | Example |
|--------|-----------|---------|
| `website`, `phone` | `{"exists": true\|false}` | `{"website": {"exists": false}}` (no website) |
| `reviews`, `rating` | `{"gt"\|"gte"\|"lt"\|"lte": number}` | `{"reviews": {"gte": 200}}` |
| `is_open` | `{"eq": true\|false}` | `{"is_open": {"eq": true}}` |
| `price` | `{"in": [...]}` or `{"contains": "$$"}` | `{"price": {"in": ["MODERATE"]}}` |

**Example input**

```json
{
  "search_terms": ["coffee", "tea", "juice"],
  "categories": ["cafe"],
  "bounds": "40.705,-74.020,40.775,-73.930",
  "min_rating": 4,
  "post_filters": { "website": { "exists": true } },
  "limit": 100
}
```

See the **Input** tab above for every parameter and its default.

### Output

Results land in the default dataset under **Storage ▸ Dataset**. Browse the built-in **Overview**, **Contact info**, **Location**, and **Full details** views, or download JSON, CSV, Excel, or XML.

**Full place record**

```json
{
  "place_id": "ChIJ...",
  "name": "Kim's Island",
  "full_address": "175 Main St, Staten Island, NY 10307",
  "latitude": "40.5107736",
  "longitude": "-74.2482624",
  "thumbnail_url": "https://tile.openstreetmap.org/19/154012/197509.png",
  "maps_url": "https://www.google.com/maps/search/?api=1&query=40.5107736,-74.2482624&query_place_id=ChIJ...",
  "name_alt": "",
  "phone": { "display": "(718) 356-5168", "actual": "+17183565168" },
  "website": { "url": "http://kimsislandsi.com/", "display": "kimsislandsi.com" },
  "reviews": 91,
  "rating": 4.3,
  "price": "$$",
  "plus_code": "XRX4+VQ Staten Island, NY",
  "address_parts": ["175 Main St", "Staten Island", "NY 10307"],
  "opening_hours": [
    { "day": "Monday", "hours": ["10:00–22:00"] },
    { "day": "Sunday", "hours": ["12:00–20:00"] }
  ],
  "open_status": "OPEN",
  "is_open": true,
  "lang": "en",
  "group": { "uri": "", "id": "0x89c3ca9c11f90c25:0x6cc8dba851799f09", "name": "Kim's Island" }
}
```

Field names are lowercase snake\_case.

### What can you do with the data?

#### 1. Build a contactable local lead list

1. Set `search_terms` (e.g. `["plumber","electrician"]`) and a `bounds` or `circle` for your target area.
2. Add `post_filters: { "phone": { "exists": true }, "website": { "exists": true } }` to keep only reachable businesses.
3. Export the **Contact info** view to CSV and load it into your CRM.

#### 2. Scan competitors in a district

1. Set `categories: ["restaurant"]` and a `bounds` across the district.
2. Filter `min_rating`, and set `ranking: "distance"` to work outward from a point.
3. Export and compare `rating` and `reviews` grouped by `group.name`.

#### 3. Enrich a list you already have

1. Put your known Google place IDs in `place_ids`.
2. Run to fetch `phone`, `website`, `opening_hours`, and coordinates.
3. Join the results back onto your records by `place_id`.

#### 4. Cover a whole country

1. Use an `ai_prompt` like `"pharmacies in Portugal"`, or a `region` selector.
2. Set a generous `limit` for the area you're covering.
3. Export — duplicates are removed automatically.

### How much does Google Places Search cost?

The actor is priced **per returned place** (pay-per-event), plus Apify platform usage. The rate is **$2.00 per 1,000 places** ($0.002 each), and starting a run is free.

- 1,000 places ≈ **$2.00** in actor cost (plus a small platform fee).
- Filtered-out or failed places are not charged.

See the **Pricing** tab for plan discounts.

### FAQ

**Do I need a Google account, cookies, or my own API key?**
No. No login, cookies, or account is needed, and you don't have to supply an API key — everything required to run is configured for you.

**Can it get private or restricted data?**
No. It only returns publicly available place and business data.

**How many results can I get?**
`limit` (default `60`) caps the whole run across every term and category. There's no fixed platform cap, but very high limits over wide areas take longer and cost more.

**What language are the results in?**
English by default. Set `language` to another code (e.g. `fr`, `es`) for different text and categories. When you use `ai_prompt`, the language is taken from your prompt.

**Is it legal to extract this data?**
It returns publicly available business data only. You're responsible for how you use the results and for complying with the terms of the data source and your local laws.

**Can I use it from the API, SDKs, or an AI agent?**
Yes — see the **API** tab above for JavaScript, Python, REST, and CLI examples, or connect the Apify MCP server to call it from Claude, ChatGPT, or another MCP client.

**Something isn't working.**
Make sure exactly one area selector is set, and that you've provided terms, categories, place IDs, or a prompt. If it still fails, open an issue in the **Issues** tab with your input JSON.

### Notes and limitations

- **Results are in English by default** — set `language` for other languages.
- **One area selector at a time** — `bounds`, `circle`, `region`, and `polygon` are mutually exclusive.
- **`region` needs a shape ID** (`country:ADMtype:shapeID`), not a free-text place name. Use `ai_prompt` or `bounds`/`circle` for plain names.
- **`price` is only available for food-and-drink place types**, and `opening_hours` may be missing for some places.
- **`ai_prompt` is authoritative** — when it works out terms, area, or filters, it overrides the manual values.

### Integrations

Call the actor from your own code or an AI agent — see the **API** tab for JavaScript, Python, REST, and CLI. MCP clients can add `harpoon/google-places-search` via the Apify MCP server. Schedule runs or trigger them with webhooks from Apify Console.

### Support

Found a bug or have feedback? Open an issue in the **Issues** tab, or email **islam.tazerout@outlook.com**.

# Actor input Schema

## `ai_prompt` (type: `string`):

e.g. "Restaurants and cafes in Brooklyn rated above 4 stars" or "Cheap hostels within 5 km of Bondi Beach".

## `search_terms` (type: `array`):

What to look for, e.g. restaurant, cafe, bar. Each term is searched separately; results are merged and duplicates removed.

## `categories` (type: `array`):

Only keep places of these types. Leave empty for all types. If no search term is given, each type is used as the term too.

## `bounds` (type: `string`):

A rectangle as lowLat,lowLng,highLat,highLng. E.g. 48.80,2.25,48.90,2.40

## `circle` (type: `string`):

A point plus a radius in km: lat,lng,radiusKm. E.g. 48.8566,2.3522,5

## `region` (type: `string`):

A region reference as country:ADMtype:shapeID, e.g. FRA:ADM1:12345.

## `polygon` (type: `string`):

A GeoJSON polygon — the coordinates array (rings of \[lng,lat], longitude first) as a JSON string.

## `place_ids` (type: `array`):

Google place IDs (e.g. ChIJ...) to fetch directly. Results are merged and duplicates removed.

## `min_rating` (type: `number`):

Only keep places rated this high or higher (0 to 5, in 0.5 steps).

## `open_now` (type: `boolean`):

Only keep places that are currently open.

## `price_levels` (type: `array`):

Only keep these price levels.

## `ranking` (type: `string`):

How to order the results.

## `country` (type: `string`):

Bias results toward a country/region (2-letter code, e.g. fr, us).

## `strict_type` (type: `boolean`):

Only keep places whose primary type exactly matches one of the Place types.

## `service_area_businesses` (type: `boolean`):

Also include businesses that visit or deliver to customers but have no physical address (e.g. plumbers, cleaning services).

## `future_openings` (type: `boolean`):

Also include businesses that are not open yet but will open in the future.

## `post_filters` (type: `object`):

Conditions checked against each place's fetched details (the search request itself can't filter these). Each key is a detail field and each value is a condition. Fields: website, phone ({"exists": true|false}), is\_open ({"eq": true|false}), reviews, rating ({"gt"|"gte"|"lt"|"lte": number}), price ({"in": \["MODERATE",...]} or {"contains": "$$"}).

## `limit` (type: `integer`):

Maximum number of places to return across the whole run. Higher values take longer and cost more.

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

Language code for returned text, e.g. en, fr, es, de, pt-BR, zh-TW. Results are returned in this language where the source supports it.

## Actor input object example

```json
{
  "ai_prompt": "Restaurants and cafes in Brooklyn rated above 4 stars",
  "search_terms": [
    "restaurant",
    "cafe"
  ],
  "categories": [
    "restaurant",
    "cafe"
  ],
  "bounds": "48.80,2.25,48.90,2.40",
  "circle": "48.8566,2.3522,5",
  "region": "FRA:ADM1:12345",
  "polygon": "[[[2.25,48.80],[2.40,48.90],[2.25,48.90],[2.25,48.80]]]",
  "place_ids": [
    "ChIJ..."
  ],
  "min_rating": 4,
  "open_now": false,
  "price_levels": [
    "MODERATE",
    "EXPENSIVE"
  ],
  "ranking": "",
  "country": "fr",
  "strict_type": false,
  "service_area_businesses": false,
  "future_openings": false,
  "post_filters": {
    "website": {
      "exists": false
    },
    "reviews": {
      "gte": 200
    }
  },
  "limit": 60,
  "language": "fr"
}
```

# Actor output Schema

## `dataset` (type: `string`):

One row per place. Export as JSON, CSV, Excel, or XML.

# 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 = {
    "search_terms": [
        "coffee"
    ],
    "bounds": "40.705,-74.020,40.775,-73.930",
    "limit": 60,
    "language": "en"
};

// Run the Actor and wait for it to finish
const run = await client.actor("harpoon/google-places-search").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 = {
    "search_terms": ["coffee"],
    "bounds": "40.705,-74.020,40.775,-73.930",
    "limit": 60,
    "language": "en",
}

# Run the Actor and wait for it to finish
run = client.actor("harpoon/google-places-search").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 '{
  "search_terms": [
    "coffee"
  ],
  "bounds": "40.705,-74.020,40.775,-73.930",
  "limit": 60,
  "language": "en"
}' |
apify call harpoon/google-places-search --silent --output-dataset

```

## MCP server setup

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

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/eF8IJmg9mxKl5T1J7/builds/HCNdOLf0TU7Bbhifq/openapi.json
