# Google Maps Posts Scraper: Track Posts & Rank Local Competitors (`getascraper/google-maps-business-posts-scraper`) Actor

Search Google Maps by keyword and location, or a direct place URL, to export every business post with text, dates, images, and CTAs, then rank businesses by posting recency and frequency. HTTP-only, no browser, datacenter proxy. Skip re-running competitor scrapers by hand. $0.00025 per post.

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

## Pricing

from $0.19 / 1,000 posts

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?

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 Business Posts Scraper & Posting Activity Leaderboard

<table width="100%">
<tr>
<td style="padding:24px 28px;background:#F0F9FF;border:1px solid #BAE6FD;border-top:4px solid #0284C7;border-radius:12px">
<span style="font-size:23px;font-weight:800;color:#0C4A6E;line-height:1.3">See who's actually posting on Google Maps in your market, ranked automatically.</span><br>
<span style="font-size:15px;color:#334155;line-height:1.6">Search a business type and a location and get a leaderboard of every matching business ranked by how often and how recently they post, plus the full post export underneath it: text, photos, dates, and calls to action. No more opening ten competitor listings by hand to see who's been active.</span>
</td>
</tr>
</table>

<table width="100%">
<tr>
<td style="padding:14px 12px;width:25%;background:#FFFFFF;border:1px solid #BAE6FD;border-radius:10px 0 0 10px;vertical-align:top">
<span style="font-size:15px;font-weight:800;color:#0369A1">🏆 Posting Activity Leaderboard</span><br>
<span style="font-size:12px;color:#334155">Every business in a search, ranked by days since their last post and how many they've posted recently.</span>
</td>
<td style="padding:14px 12px;width:25%;background:#FFFFFF;border:1px solid #BAE6FD;border-left:none;vertical-align:top">
<span style="font-size:15px;font-weight:800;color:#0369A1">📝 Full post export</span><br>
<span style="font-size:12px;color:#334155">Post text, photos, publish date, and call-to-action for every business, not just a summary.</span>
</td>
<td style="padding:14px 12px;width:25%;background:#FFFFFF;border:1px solid #BAE6FD;border-left:none;vertical-align:top">
<span style="font-size:15px;font-weight:800;color:#0369A1">🔁 Monitor mode</span><br>
<span style="font-size:12px;color:#334155">Schedule it and only see posts that are genuinely new since your last run.</span>
</td>
<td style="padding:14px 12px;width:25%;background:#FFFFFF;border:1px solid #BAE6FD;border-left:none;border-radius:0 10px 10px 0;vertical-align:top">
<span style="font-size:15px;font-weight:800;color:#0369A1">🌍 Any market, any language</span><br>
<span style="font-size:12px;color:#334155">Search any city or country, not just the US, in your own language.</span>
</td>
</tr>
</table>

***

### 🔍 What does this Actor do?

[Google Business Profile posts](https://support.google.com/business/answer/7213077) let a
business publish updates, offers, and photos straight to its Google Maps listing. This Actor
searches Google Maps by business type and location, or reads a direct business link, and pulls
every recent post from every matching business: the text, the photos, when it was published,
and its call-to-action button.

What makes this different from a plain post exporter is the second thing it builds
automatically: a **Posting Activity Leaderboard**. Instead of handing you a long list of posts
you have to sort through yourself, it ranks every business in your search by how many days since
their last post, how many they've posted in the last 30 and 90 days, and what share are offers
versus events. Search once and you know, at a glance, who's most active in that market right
now.

Run it on a schedule with monitor mode on and it becomes a standing watch: each run reports only
the posts that are new since the last one, so you can track a market's activity over time
without re-reading the same posts every week.

***

### 👥 Who uses it?

**Local SEO agencies and consultants** - "I manage Google Business Profiles for a dozen client
locations and their local competitors. I used to open each competitor's Maps listing by hand to
check if they'd posted anything new that week. Now I run one search per market and get the whole
set ranked by activity, in minutes instead of an afternoon."

**Franchise and multi-location owners** - "I run six locations and could never tell which ones
were actually keeping their listings active and which had gone quiet for months. One search
across my own locations shows me exactly that, so I know who needs a nudge."

**Local business owners** - "I wanted to know if the shop down the street was really posting
every week like it seemed, or if I was just noticing it more. Now I have the actual numbers
instead of a guess."

***

### ⚙️ How it works

<table width="100%">
<tr>
<td style="padding:16px;width:33%;background:#FFFFFF;border:1px solid #BAE6FD;border-radius:10px 0 0 10px;vertical-align:top">
<span style="font-size:22px;font-weight:800;color:#0284C7">1</span><br>
<span style="font-size:14px;font-weight:700;color:#0C4A6E">Search or paste a link</span><br>
<span style="font-size:12px;color:#334155">Enter a business type and a location, like "nail salon" and "Austin, TX", or paste a direct Google Maps business URL.</span>
</td>
<td style="padding:16px;width:33%;background:#FFFFFF;border:1px solid #BAE6FD;border-left:none;vertical-align:top">
<span style="font-size:22px;font-weight:800;color:#0284C7">2</span><br>
<span style="font-size:14px;font-weight:700;color:#0C4A6E">Get every post</span><br>
<span style="font-size:12px;color:#334155">Each business's recent posts come back in full: text, photos, publish date, and call-to-action.</span>
</td>
<td style="padding:16px;width:33%;background:#FFFFFF;border:1px solid #BAE6FD;border-left:none;border-radius:0 10px 10px 0;vertical-align:top">
<span style="font-size:22px;font-weight:800;color:#0284C7">3</span><br>
<span style="font-size:14px;font-weight:700;color:#0C4A6E">See the leaderboard</span><br>
<span style="font-size:12px;color:#334155">A separate ranked view shows who's posting most, and most recently, with no manual sorting.</span>
</td>
</tr>
</table>

***

### 📥 Input

| Field | Type | Required | Description |
|---|---|---|---|
| Search terms | string | No | A business type or keyword, e.g. "nail salon". Used with Location to search a market. |
| Location | string | No | A city, region, or address, e.g. "Austin, TX". Used with Search terms. |
| Place URLs | array of URLs | No | One or more direct Google Maps business links, for a single-business export instead of a search. |
| Language | string | No | Language code for post text, e.g. "en", "es", "fr". |
| Country | string | No | Two-letter country code for the Google Maps results, e.g. "us", "gb". |
| Max places per search | integer | No | How many businesses to pull from a search and rank. Capped at 20 per search. |
| Max posts per place | integer | No | How many of a business's most recent posts to export. |
| Monitor mode | boolean | No | When on, only reports posts not seen in a previous run with the same state name. |
| Monitor state name | string | No | A label for this monitor's saved checkpoint, so you can track several markets separately. |
| Reset monitor state | boolean | No | Clears the saved checkpoint so every post is reported as new again. |
| Proxy configuration | object | No | Apify Proxy settings. The default works for every normal run. |

Provide either Search terms and Location, or Place URLs.

***

### 📤 Output

Every business's posts are exported as individual dataset items:

```json
{
    "businessName": "MILANO NAIL SPA AUSTIN | Best Nail Salon in Austin Texas",
    "placeUrl": "https://www.google.com/maps/place/data=!3m1!1s0x8644b5c05d399063:0x198d8363de0fa965",
    "text": "Elevate your look with 3D nail art tailored to every vibe...",
    "publishedAt": "2026-09-17T14:00:00.000Z",
    "expiresAt": "2027-03-18T16:01:13.000Z",
    "relativeDate": "5 hours ago",
    "ctaLabel": "Call now",
    "ctaPhone": "(512) 900-0206",
    "images": [
        "https://lh3.googleusercontent.com/geougc/AF1QipOjousaRg96PUMaAC8k_PKXmAM6n9BCm0l018P2=h400-no"
    ],
    "postUrl": "https://search.google.com/local/posts?q=...",
    "scrapedAt": "2026-09-17T18:32:26.771Z"
}
```

The Posting Activity Leaderboard is a separate view in the Output tab, one row per business:

```json
{
    "businessName": "MILANO NAIL SPA AUSTIN | Best Nail Salon in Austin Texas",
    "totalPostsSeen": 10,
    "postsLast30Days": 10,
    "postsLast90Days": 10,
    "daysSinceLastPost": 0,
    "offerPostShare": 0,
    "eventPostShare": 0,
    "hasCustomerUpdates": false
}
```

You can download either dataset in JSON, CSV, Excel, or HTML from the Output tab.

#### Data table

| Field | Type | Description |
|---|---|---|
| businessName | string | The business's name as listed on Google Maps. |
| text | string | The post's full text. |
| publishedAt | string | When the post was published. |
| expiresAt | string | When the post stops showing on the listing. |
| ctaLabel | string | The post's call-to-action button text, e.g. "Call now". |
| images | array of URLs | Photos attached to the post. |
| daysSinceLastPost | number | Leaderboard only: days since this business's most recent post. |
| postsLast30Days | number | Leaderboard only: posts published in the last 30 days. |
| postsLast90Days | number | Leaderboard only: posts published in the last 90 days. |
| offerPostShare | number | Leaderboard only: percentage of posts that read as an offer. |

***

### 💰 Pricing

This Actor uses Apify's pay-per-event pricing. You only pay for what it actually finds, an
empty run costs nothing, and there's no subscription. Check the Actor's page on the Apify Store
for the current rate.

***

### ⭐ Enjoying Google Maps Business Posts Scraper?

If this Actor saved you from checking competitor listings by hand, leaving a review helps other
buyers find it and tells us what to build next. You can leave one from the Actor's page on the
Apify Store.

***

### ❓ FAQ

**Does this work outside the US?**
Yes. Set the Language and Country fields to match the market you're searching; Google Maps
resolves the location from your search terms directly.

**Can I track a single competitor instead of a whole market?**
Yes, paste their Google Maps link into Place URLs instead of using Search terms and Location.

**What happens if a business hasn't posted recently?**
It still shows up on the leaderboard with its real days-since-last-post figure, so an inactive
competitor is visible too, not silently dropped.

**Does monitor mode need a schedule?**
Monitor mode itself just compares against the last saved run. Add an Apify schedule to the
Actor to have it check automatically on a recurring basis.

***

### 🔗 Other actors

- [Google Maps List Scraper: Curated Places & Leads](https://apify.com/getascraper/google-maps-list-scraper) ↗ - Extract every place from a public Google Maps shared list, with full contact and location details.
- [Kwork Monitor: Мониторинг заказов Kwork](https://apify.com/getascraper/kwork-monitor) ↗ - Ranks new Kwork freelance orders by opportunity the moment they're posted.
- [CNKI Scraper](https://apify.com/getascraper/cnki-scraper) ↗ - Tracks academic citation and ranking data with a breakdown rollup view.
- [GoCollect Scraper](https://apify.com/getascraper/gocollect-scraper) ↗ - Census, sold-price, and multi-category collectibles data.

# Changelog

This Actor's version history is a separate document: https://apify.com/getascraper/google-maps-business-posts-scraper/changelog.md

# Actor input Schema

## `searchTerms` (type: `string`):

A business type or keyword, e.g. "nail salon" or "brewery". Combined with Location below to search Google Maps for every matching business in that market. This is the mode that produces the Posting Activity Leaderboard (who's posting most, and most recently) - neither of the two competitor tools computes this ranking, they only export one business's posts at a time. Leave blank if you're using Place URLs below instead.

## `location` (type: `string`):

A city, region, or address, e.g. "Austin, TX" or "Manchester, UK". Used together with Search Terms; Google resolves the market area from this text directly, no separate geocoding step.

## `placeUrls` (type: `array`):

One or more direct Google Maps business URLs to export posts from, instead of a search. Use this for a single-business export. The other competitor tool with search support still can't do this AND rank against competitors in one run, this actor does both.

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

Language code for post text and dates, e.g. "en", "es", "fr", "de". One of the two named competitors is USA/English-only; this field is what unlocks other markets.

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

Two-letter country code Google should treat the request as coming from, e.g. "us", "gb", "de", "jp". Affects local business ranking and available results.

## `maxPlacesPerSearch` (type: `integer`):

How many businesses to pull from a single search and rank on the leaderboard. Google's own search response returns at most 20 real results in one page, so this is capped at 20, not a soft suggestion.

## `maxPostsPerPlace` (type: `integer`):

How many of a business's most recent owner posts to export. Also the number of posts the leaderboard's activity metrics (postsLast30Days, postsLast90Days) are computed from, so a low value can understate an active poster.

## `monitorMode` (type: `boolean`):

When on, this run only outputs posts it hasn't seen before for the state name below, so a scheduled run reports fresh activity instead of the same posts every time. The leaderboard is still built from every post seen this run, not just the new ones.

## `stateName` (type: `string`):

A short label for this monitor's saved checkpoint, e.g. "austin-nail-salons". Use a different name per market you track so their checkpoints don't overwrite each other.

## `resetState` (type: `boolean`):

Clears the saved checkpoint for the state name above before this run, so every post is reported as new again. Useful the first time you set up monitoring for a market, or after changing your search terms.

## `proxyConfiguration` (type: `object`):

Apify Proxy settings. Datacenter (the default) is confirmed sufficient for Google Maps' internal search and place APIs used here, no browser or residential IPs required.

## Actor input object example

```json
{
  "searchTerms": "nail salon",
  "location": "Austin, TX",
  "placeUrls": [],
  "language": "en",
  "country": "us",
  "maxPlacesPerSearch": 20,
  "maxPostsPerPlace": 10,
  "monitorMode": false,
  "stateName": "default",
  "resetState": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `posts` (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 = {
    "searchTerms": "nail salon",
    "location": "Austin, TX",
    "placeUrls": [],
    "language": "en",
    "country": "us",
    "maxPlacesPerSearch": 20,
    "maxPostsPerPlace": 10,
    "monitorMode": false,
    "stateName": "default",
    "resetState": false,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("getascraper/google-maps-business-posts-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 = {
    "searchTerms": "nail salon",
    "location": "Austin, TX",
    "placeUrls": [],
    "language": "en",
    "country": "us",
    "maxPlacesPerSearch": 20,
    "maxPostsPerPlace": 10,
    "monitorMode": False,
    "stateName": "default",
    "resetState": False,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("getascraper/google-maps-business-posts-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 '{
  "searchTerms": "nail salon",
  "location": "Austin, TX",
  "placeUrls": [],
  "language": "en",
  "country": "us",
  "maxPlacesPerSearch": 20,
  "maxPostsPerPlace": 10,
  "monitorMode": false,
  "stateName": "default",
  "resetState": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call getascraper/google-maps-business-posts-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,getascraper/google-maps-business-posts-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/eppF0yXbgpmD0g060/builds/jkSr9VcEfXkt2xIJl/openapi.json
