# Yellow Pages US Scraper (`spookyweb/yellow-pages-us-scraper`) Actor

🏷️ From $0.69 / 1K | Scrape US businesses from Yellow Pages by trade and city. Every business page is opened for the full record: name, phone, email, address, website, services, hours, coordinates and socials. Adverts left out unless asked for, duplicates removed before you are charged.

- **URL**: https://apify.com/spookyweb/yellow-pages-us-scraper.md
- **Developed by:** [丂卩ㄖㄖҜㄚ](https://apify.com/spookyweb) (community)
- **Categories:** Lead generation, AI, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.69 / 1,000 businesses

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

## Yellow Pages US Scraper

Type a trade and a city and get the businesses trading there, as rows you can sell to: name, phone, email, full address, website, services, opening hours, coordinates and social profiles. Every business's own page is opened to collect the full record, not just the search result. Adverts are left out unless you ask for them, duplicates are removed before you are charged, and a business with no phone can be dropped before it ever reaches your list.

### 🔍 What does Yellow Pages US Scraper do?

You name the trades and the places, and it returns one row per unique business.

There are two ways to say what to collect, and they work together in one run:

- **Trades and places.** Put `plumbers` into `searchTerms` and `Austin, TX` into `locations`. Every trade is searched in every place, so three trades across four cities is twelve searches in one run.
- **Pages you paste.** Put a Yellow Pages search or category page into `urls` and it is walked exactly as given. Mix them with the trades above.

The easiest way to try it is to leave everything alone and click Start. The defaults are a working job.

Four things are worth understanding before you run it, because they are the whole product.

**Every business's own page is read, not just the search results.** A Yellow Pages search result carries a name, a phone and a link. The email, the services, the payment methods, the coordinates and the social profiles are only on the business's own page, so this Actor opens that page for every business it returns. That is what turns a directory listing into something you can run a campaign from. Turn `includeDetails` off if you only want the search fields and want them fast.

**Adverts are not results, and here they are not mixed in.** Yellow Pages puts paid placements above and among the real listings. They are usually national call centres with an 800 number rather than a business in the city you searched, and every tool that returns them mixed in is selling you a lead list you then have to clean. This one leaves them out by default. Turn `includeAds` on if you want them, and they arrive marked `isAd` so you can still tell them apart.

**You pay per unique business, after deduplication.** The same business appears on several pages of one search and across two searches that overlap, which is exactly what happens when you scan a metro area by trade. Before anything is delivered or charged, businesses are deduplicated on Yellow Pages' own id, so the number on your bill is the number of usable rows in your dataset. Duplicates are counted, reported, and cost you nothing.

**A business you filtered out is never charged.** `requirePhone`, `requireWebsite` and `minReviews` are read before the row is counted, so a lead list of only callable businesses costs you only the callable ones.

### ⬇️ Input

| Field | Type | Default | What it does |
|---|---|---|---|
| `searchTerms` | array | `["plumbers"]` | Trades to search for, one per line |
| `locations` | array | `["Austin, TX"]` | Cities or ZIP codes. Required when you give a trade |
| `urls` | array | empty | Yellow Pages pages to walk as given |
| `maxResults` | integer | `200` | Unique businesses to return in total. This is what you pay for |
| `maxPagesPerSearch` | integer | `10` | How deep to go into each search. About 30 businesses a page |
| `includeDetails` | boolean | `true` | Open each business's own page for email, services, coordinates and socials |
| `includeAds` | boolean | `false` | Include paid placements, marked `isAd` |
| `requirePhone` | boolean | `false` | Drop businesses with no phone |
| `requireWebsite` | boolean | `false` | Drop businesses with no website |
| `minReviews` | integer | empty | Drop businesses under this many reviews |

Limits worth knowing: up to **50 searches in one run** (trades multiplied by places), a search term up to **120 characters**, and up to **20,000 businesses**. Each is refused with a message saying which limit it was, before anything is collected or charged.

**A trade always needs a place.** Without one, Yellow Pages answers for wherever the run happened to exit, which on a rotating address pool is a different city every run. The run refuses rather than handing you a dataset that looks right and is not.

```json
{
  "searchTerms": ["plumbers", "hvac contractors"],
  "locations": ["Austin, TX", "Round Rock, TX"],
  "maxResults": 500,
  "requirePhone": true
}
```

### ⬆️ Output

One row per unique business. The fields below the line come from the business's own page and are collected when `includeDetails` is on, which it is by default.

| | Field | What it holds |
|---|---|---|
| 🆔 | `ypId` | Yellow Pages' own id, which is what the run deduplicates on |
| 🏢 | `name` | The business name as listed |
| ☎️ | `phone` | The listed number, in the grouping a caller dials |
| 📍 | `street` | Street address. Null on a business that serves an area |
| 📍 | `city`, `state`, `zip` | The address, split so you can filter and sort on it |
| 🔗 | `website` | The website the listing links to, where it has one |
| 🏷️ | `categories` | The trades Yellow Pages files the business under |
| ⭐ | `reviewCount` | How many reviews the listing carries. Most listings have none: 19 of 200 on the measured run |
| ⭐ | `rating` | The star rating, 1 to 5 in half steps. Null where there are no reviews |
| 🕒 | `openingHours` | Opening hours as published, such as `Mo-Fr 09:00-17:00`. Null where the listing gives none |
| 📅 | `yearsInBusiness` | Years trading, where the listing claims any |
| 📢 | `isAd` | Whether this is a paid placement rather than a result |
| 🔗 | `ypUrl` | The business page on Yellow Pages, for checking a row by hand |
| 📝 | `snippet` | The description line under the listing |
| 🧭 | `foundVia` | The search page the row came from |
| 🕐 | `scrapedAt` | When the row was collected, ISO 8601 UTC |
| | | |
| ✉️ | `email` | The business email, where the business published one |
| 🔧 | `services` | The individual services the business lists, split into separate entries |
| 📝 | `description` | The business's own description of itself, useful for personalising an approach |
| 💳 | `paymentMethods` | What the business accepts, such as visa, cash, amex |
| 🗺️ | `areaServed` | The area the business says it covers, often wider than its address |
| 📌 | `latitude`, `longitude` | Coordinates, for mapping and radius filtering |
| 📅 | `yearEstablished` | The year the business says it was founded |
| 🗣️ | `languages` | Languages the business says it speaks besides English |
| 🖼️ | `logo` | The business photo or logo |
| 👍 | `facebook`, `twitter`, `instagram`, `linkedin`, `youtube` | Social profiles, each in its own column |
| ✅ | `detailScraped` | Whether the business's own page was read. See the note below |

**`detailScraped` matters when a column is empty.** A blank `email` on a row where `detailScraped` is `true` means Yellow Pages holds no email for that business. A blank on a row where it is `false` means the page could not be read and the answer is unknown. Every other tool leaves you unable to tell those apart.

#### How full are the detail fields?

They are as full as the business made them, and no fuller. Measured on 60 plumbers in Austin, TX on 29 August 2026, with every detail page read successfully:

| Field | Businesses that published one |
|---|---|
| `name`, `phone`, `website`, `categories`, `logo` | 100% |
| `areaServed`, `city`, `state`, `zip` | 97% |
| `latitude` and `longitude`, `openingHours` | 80% |
| `description` | 77% |
| `paymentMethods` | 65% |
| `services` | 55% |
| `yearEstablished` | 45% |
| `email` | 25% |
| `rating` and `reviewCount` | 25% |
| social profiles | 2% |

These are printed rather than hidden because a lead list is bought on what it actually contains. Around a quarter of US Yellow Pages businesses publish an email; the rest publish a website instead, and `website` is on effectively all of them. If you need an address for every business, run the domains from this dataset through an email finder.

### 💡 Why scrape Yellow Pages?

**Territory lead lists.** A trade and a list of towns gives you every business in that market with a phone number, which is the input to a cold calling list or a direct mail run.

**Enrichment input.** Turn `requireWebsite` on and the output is a domain list, which feeds straight into an email finder or a tech stack check. A quarter of businesses publish an email here directly; the domain column covers the rest.

**Market sizing.** How many roofers trade in a metro area, how many carry reviews, and how long they have been going, is three columns of this dataset and a pivot table.

**Competitor mapping.** Categories, services and review counts across a city say who the established firms are and what they sell before you price against them.

**Map and radius work.** Coordinates come with the row, so a dataset drops straight into a map or a drive time filter without a geocoding step or a geocoding bill.

### 💰 How much does it cost?

You pay **$0.0008 per unique business delivered**, and nothing else beyond Apify's own start charge. A thousand businesses is **$0.80** on the free plan. Every paid Apify plan pays less through the Bronze, Silver, Gold, Platinum and Diamond discount tiers, down to **$0.65 per thousand** on Diamond.

Four things about that price are worth being explicit about:

- **A duplicate costs nothing.** A business found on four pages is one row and one charge.
- **A filtered business costs nothing.** An advert, or a business without the phone you required, is dropped before it is counted.
- **A run that finds nothing pays the start charge and no more.** A search with no results, a refused address and a blocked page are all delivered as a sentence rather than a bill.
- **The detail pass is included in that price, not charged on top.** Opening every business's own page is roughly thirty times the requests of reading search results alone. It is inside the per business rate rather than a second charge, so the number on your bill is still the number of rows in your dataset.
- **Yellow Pages sits behind a Cloudflare challenge**, which no ordinary request gets past whatever headers it sends. This Actor goes through an unblocking proxy that solves it, so the page arrives as plain HTML and no browser is paid for. That is what keeps the full record affordable at this price.

`maxResults` is a hard cap, so the most any run can cost is `maxResults` times the rate, plus the start charge.

On the run above, 200 businesses cost $0.16 on the free plan and $0.13 on Diamond. A thousand is $0.80 and $0.65.

### ⏱️ How long does a run take?

Reading every business's own page is what takes the time. A search result page hands over about thirty businesses in one request; the detail pass is one request per business, so a full run is roughly thirty times the requests of a listing-only one.

Measured on 29 August 2026, with every detail page read successfully:

| Businesses asked for | With full details | Listing fields only |
|---|---|---|
| 60, one search | 2 minutes 47 seconds | 23 seconds |
| 200, four searches | 7 minutes 43 seconds | 74 seconds |
| 1,000, five trades across four metros | 26 minutes 14 seconds | 10 minutes 10 seconds |

Memory peaked at 80MB on the 1,000 business run, inside the 256MB this Actor asks for. Throughput settles at about 0.65 pages a second and does not improve by asking for more at once, because the unblocking proxy is the queue rather than this Actor.

**If you want speed over depth, turn `includeDetails` off.** You get the search result fields, in the times in the right hand column, at the same price per business.

**Set the run timeout to suit the size of the ask.** The run stops cleanly inside whatever timeout it is given and tells you it did, rather than being killed with the work lost. Measured: a 120 business ask given a 90 second timeout finished in 69 seconds, delivered the 51 businesses it had, and said so. Allow about **30 minutes per thousand** with details on, or about **15 minutes** with them off. The default is 3600 seconds, which covers the largest run in the table above. You are charged per business delivered rather than per minute, so a generous timeout costs you nothing.

A search that runs out of new businesses stops on its own rather than walking empty pages, so a thin trade in a small town finishes quickly and cheaply.

### 🔌 Integrations

The output schema is declared, so this plugs into Make, Zapier, n8n, Google Sheets, Airtable and Slack through Apify's own integrations without anyone writing a parser first. Point a webhook at the run and a finished dataset can land in a CRM on its own.

### 🔄 API usage

```bash
curl -X POST "https://api.apify.com/v2/acts/spookyweb~yellow-pages-scraper/runs?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"searchTerms": ["plumbers"], "locations": ["Austin, TX"], "maxResults": 200, "requirePhone": true}'
```

### ❓ FAQ

#### Do I need a Yellow Pages account or an API key?

No. There is nothing to register and no key to manage.

#### Why do I need to give a location?

Because Yellow Pages will silently pick one for you based on where the run exits, and on a rotating address pool that is a different city each time. A dataset built that way looks correct and is not, which is worse than an error.

#### Why are adverts excluded by default?

Because an advert is not a lead. The paid slots are usually national call centres rather than businesses in the city you searched, and a list you have to clean before you can call it is not the thing you bought.

#### How complete are the fields?

Every business carries an id, a name, a phone, a website and categories. The rest depends on what the business itself published, and the measured coverage for each field is in the table further up rather than left for you to discover after paying.

#### Why is the email column empty on some rows?

Because about three quarters of US Yellow Pages businesses never published one. Where a row has `detailScraped` set to `true` and no email, that is Yellow Pages' complete record of that business and no tool can return one from this source. Where `detailScraped` is `false`, the page could not be read and the answer is unknown.

Almost every business does publish a website. If you need an address for all of them, take the `website` column and run it through an email finder.

#### Can I skip the detail pages and just get the search results?

Yes. Set `includeDetails` to `false`. The run finishes several times faster and costs the same per business, but the rows carry no email, services, coordinates or social profiles.

#### How many businesses can I get from one search?

Yellow Pages serves about 30 a page and paginates deeply, but a given trade in a given town has a real floor. When a search runs out, the run says so in its status message and stops rather than walking empty pages at your expense.

#### Are the phone numbers formatted?

They are kept exactly as Yellow Pages published them, which is the grouping a person reads and dials. Reformatting them would lose the area code separation.

#### Does it get blocked?

Yellow Pages runs a Cloudflare challenge. This Actor goes through an unblocking proxy that solves it, and when a page is challenged anyway it retries on a fresh route rather than on the one that just failed. When a page cannot be read at all, the run says so and charges nothing for it.

#### Is scraping Yellow Pages legal?

This collects information published openly on the public web, with no login and no account, which is the same information a person browsing the site can see. It collects business contact details rather than personal data. You are responsible for how you use the output, including under GDPR, CAN-SPAM, TCPA and any local rules on unsolicited contact, and for honouring do-not-call registers before you dial a list.

#### Something is wrong or missing

Open an issue on the Issues tab. Layout changes on the source site are the usual cause and are usually a quick fix.

### 🙋 Feedback

Ideas, missing fields and bug reports all go on the Issues tab, and they get read.

### 🔎 You might also like

- **Company Email Finder**, to turn the websites in this output into work email addresses.
- **Website Contact Scraper**, to pull emails, phones and socials from the sites this finds.
- **Company Data Enrichment**, to add company details to a list of domains.

# Actor input Schema

## `searchTerms` (type: `array`):

What to search for, one per line, written the way a customer would say it: plumbers, dentists, wedding photographers. Each one is searched in every location below, so three trades across four cities is twelve searches.

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

Where to search, one per line, as a city and state such as Austin, TX or a ZIP code such as 78701. This is required whenever you give a trade: without it Yellow Pages answers for wherever the run happened to exit, which is a different city every time and produces a dataset that looks right and is not.

## `urls` (type: `array`):

Paste Yellow Pages search or category pages, one per line, and each is walked as given rather than rebuilt. Mix them freely with the trades and cities above. A link that cannot be read stops the run before anything is charged rather than being silently skipped.

## `maxResults` (type: `integer`):

How many unique businesses to return in total across every search, from 1 to 20000. Duplicates are removed before this is counted and before anything is charged, so this is the number of usable rows you get and the number you pay for.

## `maxPagesPerSearch` (type: `integer`):

How deep to go into each search before moving to the next. Yellow Pages serves about 30 businesses a page. A search that runs out of new businesses stops on its own whatever this is set to.

## `includeDetails` (type: `boolean`):

Open each business's own Yellow Pages page to collect the email, services, payment methods, coordinates, opening hours and social profiles. This is what makes a row usable for outreach, so it is on by default. Turning it off returns the listing fields only and finishes several times faster.

## `includeAds` (type: `boolean`):

Yellow Pages puts paid placements above and among the real results, usually national call centres rather than local businesses. They are left out by default because an advert is not a lead. Turn this on to get them, marked with isAd so you can still tell them apart.

## `requirePhone` (type: `boolean`):

Drop businesses with no phone number. A lead you cannot call is not a lead, and a dropped business is not charged.

## `requireWebsite` (type: `boolean`):

Drop businesses with no website link. Useful when the next step is enriching the domain for emails. A dropped business is not charged.

## `minReviews` (type: `integer`):

Drop businesses with fewer reviews than this, which is the cheapest proxy for a business that is actually trading. Businesses with no reviews at all are dropped too. Leave empty to keep everything.

## Actor input object example

```json
{
  "searchTerms": [
    "plumbers",
    "roofers"
  ],
  "locations": [
    "Austin, TX",
    "78701"
  ],
  "urls": [
    "https://www.yellowpages.com/austin-tx/plumbers"
  ],
  "maxResults": 200,
  "maxPagesPerSearch": 10,
  "includeDetails": true,
  "includeAds": false,
  "requirePhone": false,
  "requireWebsite": false,
  "minReviews": 5
}
```

# Actor output Schema

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

One row per unique business: name, phone, full address split into street, city, state and ZIP, website, categories, review count, years in business, whether it was a paid placement, and the search page it came from.

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

What the run did: businesses delivered, duplicates removed before charging, how many were excluded by your filters and not charged, pages read, and whether any address was challenged.

# 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": [
        "plumbers"
    ],
    "locations": [
        "Austin, TX"
    ],
    "urls": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("spookyweb/yellow-pages-us-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": ["plumbers"],
    "locations": ["Austin, TX"],
    "urls": [],
}

# Run the Actor and wait for it to finish
run = client.actor("spookyweb/yellow-pages-us-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": [
    "plumbers"
  ],
  "locations": [
    "Austin, TX"
  ],
  "urls": []
}' |
apify call spookyweb/yellow-pages-us-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,spookyweb/yellow-pages-us-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/CYJd6ykxTX8cVowhs/builds/aspFd9QKk3Qv5ousA/openapi.json
