# Google Local Services Ads Scraper (LSA Leads + Website) (`latentlark/google-local-services-ads`) Actor

Find which plumbers, roofers, lawyers and other pros buy Google Local Services Ads in a city or ZIP: name, website, rating, reviews, years in business, phone and profile link. Feeds Lead Audit. Pay per provider.

- **URL**: https://apify.com/latentlark/google-local-services-ads.md
- **Developed by:** [LatentLark](https://apify.com/latentlark) (community)
- **Categories:** Lead generation, SEO tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.80 / 1,000 provider founds

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 Local Services Ads Scraper (LSA Leads + Website)

**For home-services marketing agencies and lead-gen operators.** See which plumbers, roofers, HVAC companies, lawyers, dentists and other pros are **paying for Google Local Services Ads (LSA)** in any US ZIP code or city: name, **website**, rating, reviews, years in business, phone and highlights.

**Outcome:** a ready prospect list of businesses that already spend on Google ads, so your pitch goes to buyers, not tire-kickers.

- 📢 **Only LSA advertisers:** the sponsored "Google Guaranteed / Google Screened" style pros from Google's Local Services page, in the order Google shows them.
- ⭐ **Trust signals:** rating, review count, 1–5 star breakdown, years in business, "in business since".
- 📞 **Contact:** phone number, the public LSA profile link and the business **website** (found automatically, see below).
- 🧭 **Many trades × many ZIPs in one run:** every service is searched in every location; duplicates across ZIPs are skipped for free.
- 💸 **Pay per provider:** $0.004 per provider row. Error and empty rows are free. No login, no Google API key.

### Quick start (first run in about 30 seconds)

1. Click **Try for free** (or **Start** in Console). The form is prefilled with service `plumber`, location `78701` (downtown Austin, TX) and **5 providers max**.
2. Click **Start**. You get 5 rows in the **Output** tab for about **$0.03** ($0.01 start + 5 × $0.004).
3. Add your trades (`roofer`, `electrician`, `personal injury lawyer`…) and your target ZIPs or cities, and raise **Max providers per search** to 20.

### What you can do with it

- **Prospect LSA buyers:** businesses running Local Services Ads already have ad budget. Pitch them LSA management, call tracking, review generation or a better website.
- **Market sizing:** how many pros advertise per trade per metro, how established they are (years, reviews).
- **Competitor watch:** see who ranks #1–#3 in the LSA box for your client's trade and ZIPs; schedule it weekly.
- **"LSA buyers with bad websites":** feed the dataset into [Google Maps No-Website Leads (Audit & Pitch)](https://apify.com/latentlark/local-lead-audit) to audit each website and get a pitch angle (see *Lead Audit pipeline* below).
- **Straight into a spreadsheet:** pipe the dataset to Google Sheets with [Google Sheets Export](https://apify.com/latentlark/sheets-export).

### Input

| Field | What it does |
|---|---|
| `services` | Trades customers search for, one per line (`plumber`, `roofer`, `HVAC`, `personal injury lawyer`) |
| `locations` | US ZIP codes or cities, one per line (`78701`, `Austin, TX`). 1 search per service × location |
| `maxProvidersPerSearch` | Up to 20 (Google shows about 20 per search). Default 20, prefilled 5 for a cheap test |
| `maxProviders` | Optional hard cap for the whole run |
| `findWebsites` | Look up each pro's own website (default on, no extra charge) |
| `maxWebsiteLookups` | Cap on website lookups per run (default 200, max 1000). Pros beyond the cap get `website: null` |
| `dedupe` | Skip the same pro found in another ZIP (default on, duplicates are free) |
| `includeHours` | Add weekly opening hours |
| `language` / `country` | Google `hl` / `gl` (default `en-US` / `us`) |
| `proxyConfiguration` | Optional. Direct requests work; use RESIDENTIAL (US) only if you get rate-limited on big runs |

Example (3 trades × 2 ZIPs = 6 searches, up to 120 providers):

```json
{ "services": ["plumber", "roofer", "electrician"], "locations": ["78701", "75201"], "maxProvidersPerSearch": 20 }
```

### Output

One dataset item per provider (`rowType: "provider"`):

```json
{
  "rowType": "provider",
  "service": "plumber",
  "location": "78701",
  "query": "plumber 78701",
  "searchUrl": "https://www.google.com/localservices/prolist?q=plumber+78701&hl=en-US&gl=us&src=1",
  "position": 1,
  "name": "Radiant Plumbing, Air Conditioning, & Electrical",
  "category": "Plumbers",
  "serviceArea": "Austin",
  "rating": 4.8,
  "reviewCount": 18046,
  "ratingHistogram": {
    "1": 678,
    "2": 146,
    "3": 112,
    "4": 377,
    "5": 16733
  },
  "yearsInBusiness": 27,
  "inBusinessSince": 1999,
  "phone": "+15129671551",
  "website": "https://radiantplumbing.com/",
  "domain": "radiantplumbing.com",
  "websiteMatch": "name",
  "websiteSource": "yahoo",
  "highlights": [
    "24/7 emergency services",
    "Local business"
  ],
  "badge": null,
  "badgeCode": 0,
  "isSponsored": true,
  "openNow": "Open",
  "profileUrl": "https://www.google.com/localservices/profile?cs=0&q=Radiant+Plumbing,+Air+Condit…",
  "photoUrl": "https://lh3.googleusercontent.com/BmmoXJOyEXkateM_UvubRWF0PE…",
  "lsaCid": "6779199783",
  "lsaBid": "9140608052",
  "lsaPid": "9999999999",
  "country": "US",
  "scrapedAt": "2026-10-26T18:45:00+00:00"
}
```

- `position` = order in Google's Local Services list for that search (1 = top).
- `website` = the pro's own site, or `null` if none was found. `websiteMatch` says how it was verified: `phone` (LSA phone found on the homepage), `nameOnPage` (business name on the homepage and in the domain) or `name` (domain clearly matches the name). Directories (Yelp, Angi, BBB, Facebook…) are never returned.
- `title`, `categoryName`, `city` = copies of name / category / service area so the rows plug straight into Lead Audit.
- `badge` = the badge Google shows on the card (`Google Guaranteed`, `Google Screened`…), `null` when the card shows none. In late-2026 tests most cards showed no badge; `badgeCode` keeps Google's raw value.
- A search with no LSA advertisers gives one free `rowType: "empty"` row; a blocked search gives one free `rowType: "error"` row.
- **Storage → Key-value store → SUMMARY**: searches run, providers per search, duplicates skipped, websites found.
- Dataset views: **Overview** and **Lead Audit** (name, website, phone, category, city, rating, reviews).

### Lead Audit pipeline (LSA → audit → Sheets)

1. Run this Actor for your trades and ZIPs (website lookup on).
2. Start [Local Lead Audit](https://apify.com/latentlark/local-lead-audit) with **mapsDatasetId** = this run's dataset ID. It reads `title`, `website`, `phone`, `categoryName`, `city`, `rating`, `reviewCount` and audits each site (speed, mobile, SSL, contact form…) with a pitch angle.
3. Send the audit dataset to Google Sheets with [Google Sheets Export](https://apify.com/latentlark/sheets-export).

You can chain the steps with Apify **Integrations → Run Actor** on *Run succeeded*.

### Pricing

Pay per event:

- **Actor start:** $0.01 per run (per GB of memory; default 256 MB = one start).
- **provider:** $0.004 per provider row pushed to the dataset (website lookup included, no extra event).
- Volume discount by Apify plan: **$0.0036** (Bronze), **$0.0032** (Silver), **$0.0028** (Gold and above) per provider.
- Duplicates, empty searches and failed searches are **free**.

Examples: 1 trade × 1 ZIP × 20 providers ≈ **$0.09**; 3 trades × 10 ZIPs (≈600 rows before de-dup) ≈ **$2.40** max. Set **Max cost per run** in Console to cap spend.

### Soft fail (schedules stay green)

Input problems (no service, no location, too many searches, unsupported proxy) end the run **SUCCEEDED** with one free explanatory row and a clear status message, so scheduled runs and integrations do not break.

### How it works and limitations

- Reads Google's public Local Services list page (`google.com/localservices/prolist`) for "service + location", the same page behind "More plumbers" in Google Search. No login.
- Google Local Services Ads run mainly in the **US** (some trades in Canada). Non-US locations usually return no providers.
- Google shows about **20 providers per search**; for more coverage add more ZIPs (neighbouring ZIPs overlap; duplicates are free).
- The order rotates a little between requests (LSA is an ad auction), so `position` is a snapshot.
- LSA cards do not list a website, so the Actor searches the web for `name + city` (then `name + phone`) and verifies the top results against the homepage. In tests it found the site for about 75–100% of pros; the rest get `website: null` (never an error). LSA phones are often Google call-tracking numbers, so `phone` matches are rare and `nameOnPage` is the usual proof.
- Website lookup adds about 2–3 s per pro (3 in parallel). Turn it off (`findWebsites: false`) for the fastest runs.
- Google no longer shows the Guaranteed/Screened badge on most list cards; `badge` is usually `null`.

### FAQ

**I got 0 providers.** Google shows no LSA ads for that trade there. Try the city (`Austin, TX`) instead of a ZIP, a common trade name (`plumber`, not `pipe relining`), or a US location.

**Some pros have no website.** No convincing match was found (or the business has no site). We return `null` rather than guess, and the row is still charged as a normal provider row.

**Some searches failed.** Google rate-limited the requests. Re-run, lower **Parallel searches**, or set the proxy to RESIDENTIAL with country US. Failed searches are not charged.

**Is this the Google Ads API / LSA API?** No. It reads the public listing page, so you see competitors' ads, not your own account data.

### More from LatentLark

- [Google Maps No-Website Leads (Audit & Pitch)](https://apify.com/latentlark/local-lead-audit): audit the websites of local businesses and get a pitch per lead.
- [Google Maps Rank Tracker (Geo-Grid Local SEO Heatmap)](https://apify.com/latentlark/google-maps-rank-tracker): where a business ranks on Google Maps across a city.
- [Google Sheets Export](https://apify.com/latentlark/sheets-export): send any dataset to Google Sheets.

# Actor input Schema

## `services` (type: `array`):

What customers search for, one per line: plumber, roofer, electrician, HVAC, personal injury lawyer, dentist, locksmith, pest control…

## `locations` (type: `array`):

One per line, e.g. 78701 or Austin, TX. Every service is searched in every location (1 search each). Google Local Services Ads are mainly US/Canada.

## `maxProvidersPerSearch` (type: `integer`):

Google shows up to about 20 Local Services Ads per service + location. Lower it for a cheap test.

## `maxProviders` (type: `integer`):

Hard cap for the whole run (after de-duplication). 0 or empty = no cap besides the run's max charge.

## `findWebsites` (type: `boolean`):

LSA listings have no website field. When on, each provider is looked up with a quick web search (name + phone + city) and the site is kept only if its homepage shows the same phone number or the domain clearly matches the business name. Not found = empty website (never guessed). Adds ~2-4 s per provider, no extra charge.

## `maxWebsiteLookups` (type: `integer`):

Caps lookup work on big runs. Providers beyond this cap are still returned (website empty).

## `dedupe` (type: `boolean`):

The same pro often appears in nearby ZIPs. Duplicates are skipped and not charged.

## `includeHours` (type: `boolean`):

Adds a 7-day hours array to each provider.

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

Google interface language (hl), e.g. en-US or es-US.

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

Google country (gl). LSA results are best in us; ca also works in some trades.

## `maxConcurrency` (type: `integer`):

Searches run in parallel. Keep low (3) to avoid Google rate limits.

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

Leave empty (recommended: direct US requests work). If Google rate-limits you, use RESIDENTIAL with country US.

## Actor input object example

```json
{
  "services": [
    "plumber"
  ],
  "locations": [
    "78701"
  ],
  "maxProvidersPerSearch": 5,
  "findWebsites": true,
  "maxWebsiteLookups": 200,
  "dedupe": true,
  "includeHours": false,
  "language": "en-US",
  "country": "us",
  "maxConcurrency": 3
}
```

# Actor output Schema

## `overview` (type: `string`):

Name, rating, reviews, years in business, phone, website, badge.

## `summary` (type: `string`):

Searches run, providers found, duplicates skipped.

# 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 = {
    "services": [
        "plumber"
    ],
    "locations": [
        "78701"
    ],
    "maxProvidersPerSearch": 5
};

// Run the Actor and wait for it to finish
const run = await client.actor("latentlark/google-local-services-ads").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 = {
    "services": ["plumber"],
    "locations": ["78701"],
    "maxProvidersPerSearch": 5,
}

# Run the Actor and wait for it to finish
run = client.actor("latentlark/google-local-services-ads").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 '{
  "services": [
    "plumber"
  ],
  "locations": [
    "78701"
  ],
  "maxProvidersPerSearch": 5
}' |
apify call latentlark/google-local-services-ads --silent --output-dataset

```

## MCP server setup

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

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/jZUJTQ7mtDidMPcAr/builds/tMc43QTXo7CFDZxve/openapi.json
