# Yellow Pages Canada Scraper (`memo23/yellowpages-ca-scraper`) Actor

Scrape yellowpages.ca and pagesjaunes.ca (Canada, not PagesJaunes.fr): business name, phone, email when listed, website, address, hours, rating, and categories. Keyword + city or a .ca search/profile URL. Mobile JSON, no browser. JSON or CSV out. $2.50 per 1,000 listings.

- **URL**: https://apify.com/memo23/yellowpages-ca-scraper.md
- **Developed by:** [Muhamed Didovic](https://apify.com/memo23) (community)
- **Categories:** Lead generation, Business
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.50 / 1,000 listings

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?

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 Canada Scraper

<p align="center"><img src="https://raw.githubusercontent.com/muhamed-didovic/muhamed-didovic.github.io/main/assets/logo-yellow-pages-ca.png" alt="Yellow Pages Canada Scraper logo" width="160" /></p>

Turn a yellowpages.ca or pagesjaunes.ca search into structured Canadian business rows: name, phone, email when listed, website, address, hours, categories, rating, and profile link.

Keyword + city, or a .ca URL. JSON or CSV out.

This is **Yellow Pages Canada** (YP Group). It is not [PagesJaunes.fr](https://apify.com/memo23/pagesjaunes-scraper-cheerio) and not [Canada411](https://apify.com/memo23/canada411-scraper) (people / white pages).

### Why Use This Scraper?

- Phone on every listing in the pizza/Toronto sample
- Native email when Yellow Pages publishes one (about 1 in 4 restaurants; lower on trades)
- Website and social links on the search row
- Hours, categories, rating, neighbourhood, coordinates
- English (yellowpages.ca) and French (pagesjaunes.ca) from the same mobile JSON
- HTTP only. No browser

### Overview

Built for sales teams, agencies, and researchers who need Canadian **business** leads from yellowpages.ca / pagesjaunes.ca.

Every start type becomes **one dataset row per merchant**. A keyword search, a pretty `/search/si/` URL, and a `/bus/` profile URL all emit the same flat shape. Rows are de-duplicated by Yellow Pages merchant id.

pagesjaunes.ca is the French Canadian twin of yellowpages.ca. It is not PagesJaunes.fr (Solocal, France).

### Supported Inputs

#### Search terms and locations

| Field | Example |
| --- | --- |
| `searchTerms` | `["pizza"]`, `["plumber", "dentist"]` |
| `locations` | `["Toronto, ON"]`, `["Montreal"]`, `["Vancouver"]` |

Every term is searched in every location. `["pizza","plumber"]` × `["Toronto","Montreal"]` is four searches. A term with no location is skipped.

Set `language` to `fr` to hit pagesjaunes.ca for keyword searches.

#### URL types

| URL type | Pattern | Example |
| --- | --- | --- |
| Pretty search | `/search/si/{page}/{what}/{where}/` | `https://www.yellowpages.ca/search/si/1/pizza/Toronto/` |
| Query-string search | `/search/?stype=si&what=&where=` | `https://www.pagesjaunes.ca/search/?stype=si&what=plombier&where=Montreal` |
| Business profile | `/bus/{Province}/{City}/{Slug}/{id}.html` | `https://www.yellowpages.ca/bus/Ontario/Toronto/Pizzaville/100363770.html` |

A pagesjaunes.ca URL forces French regardless of `language`. Search URLs are paginated automatically.

**Unsupported:** canada411.ca people pages, yellowpages.com (US), pagesjaunes.fr (France).

### Use Cases

| Audience | What they take from a run |
| --- | --- |
| Sales and outreach | Phone + website + email (when listed) by category and city |
| Agencies | Cross-joined term × city lists in one dataset |
| Local SEO | Categories, rating, neighbourhood, and the Yellow Pages profile URL |
| Researchers | Hours, coordinates, and out-of-business flags for a trade in a city |

### How It Works

1. Cross-join `searchTerms` × `locations`, plus any yellowpages.ca / pagesjaunes.ca URLs.
2. Call the mobile keyword search at 15 businesses per page (`pg=N`).
3. Fan out remaining pages in parallel, stopping at `maxItems` or `maxPages`.
4. Read email, website, hours, and categories from the search row.
5. Optionally load `/merchant/{id}` for extra phones (toll-free).
6. De-duplicate by merchant id and push one row per business.

<p align="center"><img src="https://raw.githubusercontent.com/muhamed-didovic/muhamed-didovic.github.io/main/assets/how-it-works-yellow-pages-ca.png" alt="How the Yellow Pages Canada scraper turns a search into business rows" width="800" /></p>

### Input Configuration

| Field | Type | Required | Notes |
| --- | --- | --- | --- |
| `searchTerms` | array | one of the start types | What you would type into Yellow Pages |
| `locations` | array | with `searchTerms` | City or `City, PV` |
| `startUrls` | array | one of the start types | Search or `/bus/` URLs on .ca |
| `maxItems` | integer | no | Default 50. Free users capped at 50. |
| `language` | string | no | `en` (yellowpages.ca) or `fr` (pagesjaunes.ca) |
| `scrapeDetails` | boolean | no | Default true. Extra phones only. |
| `maxPages` | integer | no | Default 20 (300 rows per search). |
| `maxConcurrency` | integer | no | Default 5. |
| `proxy` | object | no | Optional. Default is no Apify proxy. |

#### Example input

```json
{
  "searchTerms": ["pizza"],
  "locations": ["Toronto, ON"],
  "maxItems": 50
}
```

French Canadian twin:

```json
{
  "searchTerms": ["plombier"],
  "locations": ["Montreal"],
  "language": "fr",
  "maxItems": 50
}
```

### Output Overview

One flat row per business. Contact and address fields sit at the top level. `searchTerm` tells you which input produced the row. Email and website are present only when Yellow Pages lists them.

### Output Samples

Search: pizza in Toronto, ON.

```json
{
  "listingId": "100363770",
  "name": "Pizzaville",
  "phone": "4167363636",
  "phones": ["4167363636"],
  "email": "info@pizzaville.ca",
  "website": "https://www.pizzaville.ca/",
  "social": [{ "type": "FACEBOOK", "url": "http://www.facebook.com/pizzavilleinc" }],
  "street": "796 College St",
  "city": "Toronto",
  "province": "ON",
  "postalCode": "M6G 1C6",
  "neighbourhood": "Little Italy",
  "latitude": 43.6548587,
  "longitude": -79.4214047,
  "categories": ["Pizza & Pizzerias", "Restaurants"],
  "rating": null,
  "reviewCount": null,
  "hours": [{ "day": 1, "open": "11:00", "close": "23:00" }],
  "isOpen": true,
  "outOfBusiness": false,
  "yellowPagesUrl": "http://www.yellowpages.ca/bus/Ontario/Toronto/Pizzaville/100363770.html",
  "language": "en",
  "searchTerm": "pizza in Toronto, ON"
}
```

### Key Output Fields

**Identity:** `listingId`, `name`, `yellowPagesUrl`, `language`

**Contact:** `phone` (digits), `phones` (includes toll-free when `scrapeDetails` is on), `email`, `website`, `social`

**Place:** `address`, `street`, `city`, `province`, `postalCode`, `neighbourhood`, `latitude`, `longitude`

**Directory:** `categories`, `rating`, `reviewCount`, `hours`, `isOpen`, `outOfBusiness`

**Provenance:** `searchTerm`, `scrapedAt`

Phone is present on every row in a pizza/Toronto sample. Email is not: Yellow Pages only publishes an inbox on some merchants.

### Pricing

| Event | When | Rate |
| --- | --- | --- |
| Actor start | Once per run, per GB of memory | $0.005 |
| Listing | Each unique business in the dataset | $0.0025 ($2.50 / 1k) |
| Contact phone | Row has a phone | $0.002 |
| Contact email | Row has an email | $0.05 |

Phone and email are billed only when Yellow Pages publishes that field. A miss is free.

### FAQ

**Is this PagesJaunes.fr?** No. pagesjaunes.ca is the French Canadian twin of yellowpages.ca. For France use [PagesJaunes Scraper](https://apify.com/memo23/pagesjaunes-scraper-cheerio).

**Does it scrape Canada411 people?** No. Use [Canada411 Scraper](https://apify.com/memo23/canada411-scraper).

**Will every row have an email?** No. Same as the US directory: phone is common, inbox is not. Phone and email are billed as extra events only when the field is present.

**What does scrapeDetails add?** Extra phone types (toll-free). Email and website are already on the search row when Yellow Pages has them.

**Do I need a proxy?** No.

**French results?** Set `language` to `fr`, or paste a pagesjaunes.ca URL.

### Support

- Issues: [Actor Issues](https://console.apify.com/actors/CnHpZQ9Ipye6pUMN6/issues)
- Author site: <https://muhamed-didovic.github.io/>
- Email: <muhamed.didovic@gmail.com>

### Additional Services

Bulk province sweeps, scheduled refreshes, or a private API wrapper: <muhamed.didovic@gmail.com>

### Explore More Scrapers

Directory cousins: [Yellow Pages US Scraper](https://apify.com/memo23/yellow-pages-us-scraper), [Canada411 Scraper](https://apify.com/memo23/canada411-scraper), and [PagesJaunes.fr Scraper](https://apify.com/memo23/pagesjaunes-scraper-cheerio). More actors on [memo23](https://apify.com/memo23) and [didovic](https://apify.com/didovic).

### 🤖 For AI Agents & LLM Apps

Compact reference for AI agents calling this actor via the [Apify MCP server](https://mcp.apify.com) or the Apify API (actor: `memo23/yellowpages-ca-scraper`).

**Purpose:** Extract yellowpages.ca / pagesjaunes.ca business listings (name, phone, email when listed, website, split address, hours, categories) by keyword + city or URL.

**Minimal input:**

```json
{
  "searchTerms": ["pizza"],
  "locations": ["Toronto, ON"],
  "maxItems": 10
}
```

**Output:** one dataset row per business — `listingId`, `name`, `phone`, `phones`, `email`, `website`, `social`, `address`, `street`, `city`, `province`, `postalCode`, `neighbourhood`, `latitude`, `longitude`, `categories`, `rating`, `reviewCount`, `hours`, `isOpen`, `outOfBusiness`, `yellowPagesUrl`, `language`, `searchTerm`, `scrapedAt`.

**Behaviors an agent should know:**

- Always set `maxItems`. Free users are capped at 50.
- `searchTerms` needs at least one entry in `locations`.
- `startUrls` work on their own (yellowpages.ca or pagesjaunes.ca only).
- `language: "fr"` hits pagesjaunes.ca. A pagesjaunes.ca URL forces French.
- Email is often empty. Do not assume a contact-email field exists on every row.
- Each listing is $0.0025. A phone on the row is $0.002. An email is $0.05. Start is $0.005 per GB.
- Canada business directory only. Not Canada411 people, not PagesJaunes.fr, not yellowpages.com.

### ⚠️ Disclaimer

This Actor is an independent tool and is not affiliated with, endorsed by, or sponsored by Yellow Pages Digital & Media Solutions Limited, Yellow Pages Group, or Pages Jaunes. All trademarks mentioned are the property of their respective owners.

The scraper reads publicly listed Canadian business data from Yellow Pages Canada's mobile search API. Users are responsible for ensuring their use complies with yellowpages.ca and pagesjaunes.ca Terms of Service, PIPEDA and provincial privacy law, CASL if they send outreach, and any contractual obligations of their own organization.

### SEO Keywords

yellow pages canada scraper, yellowpages.ca scraper, pagesjaunes.ca scraper, scrape yellow pages canada, Yellow Pages Canada API, Canadian business listings scraper, Canada phone email scraper, pagesjaunes canada scraper, Apify yellow pages canada, Canadian restaurant leads, plumber leads Canada, local SEO Canada data, yellowpages.ca email scraper, Canadian directory scraper, pagesjaunes.ca leads

# Actor input Schema

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

What you would type into Yellow Pages, e.g. <b>pizza</b>, <b>plumber</b>, <b>dentist</b>. One row per term. Every term is searched in every location below.

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

Canadian city, e.g. <b>Toronto</b>, <b>Montreal</b>, <b>Vancouver</b>. City + province also works: <b>Toronto, ON</b>.

## `maxItems` (type: `integer`):

Stop after this many unique businesses across the whole run. Free plan is capped at 50.

## `startUrls` (type: `array`):

Examples: <code>https://www.yellowpages.ca/search/si/1/pizza/Toronto</code> or a <code>/bus/…/123.html</code> profile.

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

English hits yellowpages.ca. French hits pagesjaunes.ca. URLs pick the host themselves.

## `scrapeDetails` (type: `boolean`):

Loads the merchant JSON for extra phone numbers (toll-free). Email and website are already on the search row when Yellow Pages has them.

## `maxPages` (type: `integer`):

Each page holds 15 businesses. 20 pages = up to 300 per term + city.

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

How many pages and profiles to load at the same time.

## `proxy` (type: `object`):

Not needed. Only turn on if runs fail from your region.

## Actor input object example

```json
{
  "searchTerms": [
    "pizza"
  ],
  "locations": [
    "Toronto, ON"
  ],
  "maxItems": 50,
  "language": "en",
  "scrapeDetails": true,
  "maxPages": 20,
  "maxConcurrency": 5,
  "proxy": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `overview` (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": [
        "pizza"
    ],
    "locations": [
        "Toronto, ON"
    ],
    "proxy": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("memo23/yellowpages-ca-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": ["pizza"],
    "locations": ["Toronto, ON"],
    "proxy": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("memo23/yellowpages-ca-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": [
    "pizza"
  ],
  "locations": [
    "Toronto, ON"
  ],
  "proxy": {
    "useApifyProxy": false
  }
}' |
apify call memo23/yellowpages-ca-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,memo23/yellowpages-ca-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/CnHpZQ9Ipye6pUMN6/builds/3KAKD4LCFUSyBx1bh/openapi.json
