# Google Maps Business Scraper — $0.50/1K, No API Key (`meka.im/google-maps-business-scraper`) Actor

Search Google Maps businesses with phone, website, split address, weekly hours, accessibility, ratings, ranking, and coordinates. $0.50 per 1,000 delivered. No Google API key; duplicates, failures, and empty searches are free.

- **URL**: https://apify.com/meka.im/google-maps-business-scraper.md
- **Developed by:** [Meka.im](https://apify.com/meka.im) (community)
- **Categories:** Lead generation
- **Stats:** 2 total users, 1 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$0.50 / 1,000 business delivereds

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/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

Google Maps businesses with complete details for **$0.50 per 1,000 delivered businesses**.

- Contact, address, hours, accessibility, ranking and location in the base price
- Core business fields aligned with the leading Google Maps Actor
- No charge for duplicates, failures, or empty searches
- No Google API key required

Type what you would type into Google Maps. Get a flat table of businesses with phone, website, address split into parts, coordinates, rating, review count, weekly opening hours, accessibility attributes and identifiers, ready for CSV, Excel, JSON or a warehouse.

### Quick start

1. Put your search terms in the **Search terms** field, one per line.
2. Click **Start**.
3. Export the Dataset, or read it with the API calls below.

```json
{
  "searchStringsArray": ["dentist in Austin TX", "florist in Boston"],
  "maxCrawledPlacesPerSearch": 20,
  "language": "en"
}
```

### Input

| Field | Type | Default | Notes |
|---|---|---|---|
| `searchStringsArray` | array of strings | required | One search term per line. Terms that name a place work best. Searched separately, results keep your order |
| `locationQuery` | string | | A single place to search in. Combined with each term |
| `city` / `county` / `state` / `postalCode` | string | | Optional, added to the location |
| `countryCode` | string | | Biases results toward this country |
| `language` | string | `en` | Language of names, categories and hours |
| `maxCrawledPlacesPerSearch` | integer | 20 | 1 to 20. See Limits |
| `customGeolocation` | object | | GeoJSON geometry; its centre becomes the map centre |
| `categoryFilterWords` | array | | Keep only these categories |
| `searchMatching` | string | `all` | `only_includes` or `only_exact` to match on the name |
| `placeMinimumStars` | string | | Drop businesses below this rating |
| `website` | string | `allPlaces` | `withWebsite` or `withoutWebsite` |

Duplicate search terms are searched once. A business that appears under two terms is written once.

All four filters run on results that were already fetched. They never add requests, and **anything they remove is not charged**.

#### Inputs from the reference Actor

This Actor also accepts the other inputs of the leading Google Maps Actor so an existing input file runs unchanged, even though they are not shown in the editor:

- **Accepted, no effect**: review, image, contact, lead, social-profile and competitor-analysis options, `scrapePlaceDetailPage`, `skipClosedPlaces` and the other add-on switches. Setting any of them to a non-default value is reported in the run log and in the run summary, so it is never silent.
- **Rejected**: `startUrls`, `placeIds` and `allPlacesNoSearchAction`. These change the input mode entirely, and quietly ignoring them would return results you did not ask for. A run that sets one stops with an explanation instead.

### Output

One row per business, in the order of your search terms.

| Field | Type | Notes |
|---|---|---|
| `fid` | string | Google's feature ID. The stable key for joining and de-duplication |
| `placeId` | string | Google Place ID, `ChIJ…` |
| `cid` | string | Decimal customer ID used by `maps?cid=` links |
| `kgmid` | string | Knowledge Graph machine ID |
| `businessProfileId` | string | Google Business Profile ID |
| `url` | string | Google Maps link |
| `title` | string | Business name |
| `categoryName` | string | Primary category |
| `categories` | array | All categories, primary first |
| `description` | string or null | Editorial summary |
| `tagline` | string or null | Short tagline. Additional field |
| `address` | string | Street address without the business name |
| `street` / `city` / `state` / `postalCode` / `countryCode` / `neighborhood` | string or null | Address split into parts |
| `location` | object | `{ lat, lng }`, each null when Google has no coordinates |
| `phone` | string or null | As Google displays it |
| `phoneUnformatted` | string or null | Digits only |
| `website` | string or null | |
| `totalScore` | number or null | Average rating |
| `reviewsCount` | number or null | See Limits |
| `openingHours` | array | `{ day, hours }`, Monday first |
| `openingHoursComplete` | boolean | Whether all seven days are present |
| `openState` | string or null | Open/closed text at the moment of the scrape. **Not** a permanent-closure flag |
| `timezone` | string | IANA timezone |
| `additionalInfo` | object | Accessibility attributes |
| `thumbnail` | string or null | Preview image |
| `searchString` / `language` / `rank` / `scrapedAt` | | Where this row came from |

Example row, trimmed:

```json
{
  "fid": "0x8644b4eb0b01986f:0x6b241d4769ed491e",
  "placeId": "ChIJbxgBC-u0RIYRHknsaUcdJGs",
  "title": "ATX Family Dental",
  "categoryName": "Dentist",
  "address": "1700 S 1st St, Austin, TX 78704",
  "city": "Austin", "state": "TX", "postalCode": "78704", "countryCode": "US",
  "location": { "lat": 30.2482229, "lng": -97.7559705 },
  "phone": "(512) 717-3147", "phoneUnformatted": "+15127173147",
  "website": "https://www.atxfamilydental.com/",
  "totalScore": 4.9, "reviewsCount": 902,
  "openingHours": [{ "day": "Monday", "hours": "8 AM–5 PM" }],
  "openingHoursComplete": true,
  "additionalInfo": { "Accessibility": [{ "Wheelchair accessible entrance": true }] },
  "searchString": "dentist in Austin TX", "rank": 1
}
```

### Pricing

$0.50 per 1,000 delivered businesses.

- A search term that returns nothing is not charged. Unlike some alternatives, this Actor does not fill an empty search with unrelated businesses.
- Duplicate search terms and businesses that appear under more than one term are charged once.
- Filtered-out businesses are not written and not charged.
- Failed searches are not charged.
- A resumed run does not refetch or recharge a business already in the Dataset.

The run summary records `delivered` alongside `charged`, so you can reconcile the bill against the Dataset.

### Limits

Stated here rather than discovered later.

**20 businesses per search term.** Google returns one page of 20 for a search, and this version does not page further. To cover a city, use more search terms or a tighter location, which is how large Google Maps datasets are built anyway.

**Review count and weekly hours come and go.** Google answers this endpoint with either a full payload or a lite one, and the choice is per response: either all 20 businesses carry the review count and seven-day hours, or none of them do. The run detects a lite payload and retries, waiting between attempts because that is what actually helps. Most businesses end up complete; some do not, and `openingHoursComplete` tells you which. `reviewsCount` is null when the payload omitted it.

**`openState` is not a closure flag.** It is the open or closed text at the moment of the scrape, so a business that is simply shut for the night reads as closed. This version does not extract a permanent-closure flag.

**`additionalInfo` covers accessibility only.** Other attribute groups are not part of the search response.

Not included in this version: review text, rating distribution, image counts and full image sets, claim status, emails and website contacts, and more than 20 businesses per search term.

### Compatibility

Designed to match the core search semantics of the leading Google Maps Actor, not its full input contract. Field names, types and null rules for the core business fields follow that Actor, verified field by field against 100 rows of a live run on 2026-09-20: **99.94% semantic match across 24 core fields and 1,632 comparisons**.

Result sets are not identical. Google returns a somewhat different set of businesses for the same search from one call to the next, and this version reads one page per term.

### API and automation

Read the Dataset directly:

```bash
curl "https://api.apify.com/v2/datasets/DATASET_ID/items?format=csv&clean=true" \
  -H "Authorization: Bearer APIFY_TOKEN"
```

Start a run and wait for its Dataset in one call:

```bash
curl -X POST "https://api.apify.com/v2/acts/ACTOR_ID/run-sync-get-dataset-items" \
  -H "Authorization: Bearer APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"searchStringsArray":["dentist in Austin TX"]}'
```

Each run also writes an `OUTPUT` record to the key-value store with how many search terms were requested and deduplicated, and how many businesses were delivered, charged, de-duplicated, filtered out, skipped on resume, and how many searches returned nothing or failed.

### FAQ

**Why did I get fewer than 20 businesses for a term?** Google returned fewer, or a filter removed some. Filtered-out businesses are not charged.

**A business has no phone or website. Was I charged?** Yes. That is a real answer: the business does not publish one. A row is only skipped when it has no stable identifier.

**`openingHoursComplete` is false. Why?** Google answered with its lite payload for that request and the retries did not land a full one. The hours you do get are correct, just not the whole week.

**My run stopped early.** If `stoppedOnBudget` is true in the run summary, the run hit the maximum charge you allowed. Raise the limit and start it again. Businesses already delivered are not refetched or recharged.

**Can I search by Place ID or a Google Maps URL?** Not in this version. A run that sets `placeIds` or `startUrls` stops with an explanation rather than returning something else.

**Can I get reviews?** Not from this Actor. See Limits.

### Legal

This Actor is independently developed and is not affiliated with, endorsed by, or sponsored by Google.

It reads a public endpoint. It does not sign in, does not use cookies or credentials, and does not reach anything behind a login. You are responsible for how you use the data, including any applicable terms and data-protection rules.

# Actor input Schema

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

What to look for, one term per line. "dentist in Austin TX" style terms with a place name work best. Each term is searched separately and results keep the order you typed.

## `locationQuery` (type: `string`):

A single place to search in, for example "Austin, TX". Combined with each search term. Leave empty if your terms already name the place.

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

Up to 20. Google returns one page of 20 per search, and this version does not page further, so higher values are capped at 20. Use more search terms or a tighter location to get more businesses.

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

Language of names, categories and opening hours.

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

Biases results toward this country.

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

Optional. Added to the location.

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

Optional. Added to the location.

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

Optional. Added to the location.

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

Optional. Added to the location.

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

A GeoJSON geometry. Its centre is used as the map centre for the search. The outline itself is not applied, so results near the edge are not clipped.

## `categoryFilterWords` (type: `array`):

Case-insensitive match against a business’s categories. Filtered-out businesses are not written and not charged.

## `searchMatching` (type: `string`):

"all" keeps everything Google returned. "only\_includes" keeps names containing the search term. "only\_exact" keeps exact name matches.

## `placeMinimumStars` (type: `string`):

Drop businesses rated below this. Businesses with no rating are dropped when a minimum is set.

## `website` (type: `string`):

Keep only businesses that have a website, or only those without one.

## Actor input object example

```json
{
  "searchStringsArray": [
    "coffee shop in Seattle"
  ],
  "maxCrawledPlacesPerSearch": 20,
  "language": "en",
  "searchMatching": "all",
  "placeMinimumStars": "",
  "website": "allPlaces"
}
```

# Actor output Schema

## `businesses` (type: `string`):

No description

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

No description

# 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": [
        "coffee shop in Seattle"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("meka.im/google-maps-business-scraper").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": ["coffee shop in Seattle"] }

# Run the Actor and wait for it to finish
run = client.actor("meka.im/google-maps-business-scraper").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": [
    "coffee shop in Seattle"
  ]
}' |
apify call meka.im/google-maps-business-scraper --silent --output-dataset

```

## MCP server setup

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

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/xX526hWflX2sMqVG2/builds/FjNEOiKRMP8Rx5iGc/openapi.json
