# Canada Restaurant Lead Finder - POS & Delivery Detection (`seibs.co/restaurant-lead-finder-canada`) Actor

Enriched Google Maps leads for Canadian restaurants, all 13 provinces. Detects POS (Lightspeed, TouchBistro, Toast), payments (Moneris), delivery (SkipTheDishes, Uber Eats) and reservations. Bilingual EN/FR, Canadian postal codes. For restaurant SaaS and delivery sales teams.

- **URL**: https://apify.com/seibs.co/restaurant-lead-finder-canada.md
- **Developed by:** [Seibs.co](https://apify.com/seibs.co) (community)
- **Categories:** Lead generation, Automation
- **Stats:** 2 total users, 0 monthly users, 88.5% runs succeeded, 1 bookmarks
- **User rating**: 4.00 out of 5 stars

## Pricing

from $5.00 / 1,000 base restaurant records

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

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
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.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp.md).

If your project is in a different language, use 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

## Canada Restaurant Lead Finder

<!-- TOP-SELL-START -->
> **TL;DR for restaurant-tech, food supply, and B2B SaaS reps selling INTO Canadian independent restaurants:** Pulls Canadian independent restaurants, cafes, brasseries, food trucks, and bars from Google Maps across all 13 provinces and territories, with detected POS (Toast, Square, Clover, Lightspeed, TouchBistro, Revel), online ordering and delivery (SkipTheDishes, DoorDash, Uber Eats, Ritual, Fantuan, ChowNow), reservation (OpenTable, Resy, Tock, Libro), emails, and social. Bilingual: classifies English and Quebec French listings. Canada-correct: 2-letter province codes, normalized `A1A 1A1` postal codes, province inferred from the postal code. Tim Hortons, A&W, Swiss Chalet, St-Hubert, Boston Pizza, Pizza Pizza, La Belle Province and other Canadian + cross-border chains are pre-blocked. Drop-in sibling of our US Restaurant Lead Finder: identical output schema and pricing.

### Run it in 30 seconds

```python
## Via the Apify Python SDK
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("seibs.co/restaurant-lead-finder-canada").call(run_input={
    "search_terms": ["restaurant", "cafe"],
    "locations": ["Toronto, ON", "Montreal, QC"],
    "enrichment_tier": "premium",
    "max_results_per_query": 50
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)
````

Or via curl:

```bash
curl -X POST "https://api.apify.com/v2/acts/seibs.co~restaurant-lead-finder-canada/run-sync-get-dataset-items?token=<YOUR_APIFY_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"search_terms": ["restaurant", "cafe"], "locations": ["Toronto, ON"], "enrichment_tier": "premium", "max_results_per_query": 50}'
```

Or click "Try for free" on this page if you prefer the no-code UI.

### What does it do?

Pulls Canadian independent restaurants from Google Maps and enriches each record with website-derived signals: detected POS systems (Toast, Square, Clover, Lightspeed, TouchBistro, Revel, Aloha, Micros), online ordering and delivery (SkipTheDishes, DoorDash, Uber Eats, Ritual, Fantuan, ChowNow, BentoBox), reservation platforms (OpenTable, Resy, Tock, Libro, SevenRooms), emails, social profiles, and owner names. The single biggest filter restaurant SaaS sales teams want is "show me independents in this Canadian metro who DON'T already use Toast / Lightspeed" or "show me restaurants on SkipTheDishes only with no direct ordering." That's exactly what this actor produces.

### Why a separate Canadian actor?

It is the same data engine as our US Restaurant Lead Finder, localized so the output is genuinely Canada-correct, not a label swap:

- **Geography.** Province/territory codes (AB BC MB NB NL NS NT NU ON PE QC SK YT), Canadian postal-code normalization to `A1A 1A1`, and province inference from the postal code's Forward Sortation Area when Google Maps omits the province. Country is set to `CA`. A genuine US result from a border-city query (Windsor/Detroit, Vancouver/Bellingham) is left labeled US, not relabeled CA.
- **Bilingual classification.** Quebec French category needles on every service line (brasserie, boulangerie, rotisserie, casse-croute, poutine, taverne, bar a vin).
- **Canadian delivery stack.** SkipTheDishes (the dominant Canadian aggregator), Ritual, and Fantuan added to tech detection, alongside the US aggregators that operate in Canada.
- **Canadian chains pre-blocked.** Tim Hortons, A\&W, Harvey's, Swiss Chalet, St-Hubert, Boston Pizza, Pizza Pizza, Mary Brown's, La Belle Province, Valentine, Normandin and more, on top of the cross-border US chains.

POS note: TouchBistro (Toronto) and Lightspeed (Montreal) are Canadian companies and heavily deployed here, so they are first-class detections.

### Input

Minimum input is `search_terms` and `locations`.

```json
{
  "search_terms": ["restaurant", "pizza", "sushi", "brasserie"],
  "locations": ["Toronto, ON", "Montreal, QC", "Vancouver, BC"],
  "max_results_per_query": 100,
  "enrichment_tier": "premium",
  "exclude_chains": true
}
```

Locations take `City, PROV` (2-letter province code), a neighbourhood, or a postal FSA (e.g. `M5V`). Up to 10 search terms and 25 locations per run (max 50 combinations).

### Output

**Sample output:** [`.actor/sample-output.json`](./.actor/sample-output.json) - copy-paste-ready preview of real-looking records.

First record inline:

```json
{
  "place_id": "ChIJpTvG15DL1IkRd8S0KlBVNTI",
  "name": "Maple & Birch Kitchen",
  "category": "Canadian restaurant",
  "vertical": "restaurants_ca",
  "service_line": "full_service_restaurant",
  "address": {
    "street": "412 Queen St W",
    "city": "Toronto",
    "state": "ON",
    "zip": "M5V 2A8",
    "country": "CA",
    "lat": 43.6487,
    "lng": -79.3973
  },
  "phone": "(416) 555-0142",
  "phone_e164": "+14165550142",
  "website": "https://mapleandbirch.ca/",
  "rating": 4.7,
  "reviews_count": 612,
  "confidence_score": 0.74,
  "enrichment": {
    "tier": "premium",
    "emails": ["info@mapleandbirch.ca", "events@mapleandbirch.ca"],
    "tech_stack": ["touchbistro", "opentable", "skipthedishes", "ritual", "wordpress"]
  }
}
```

Each item is one restaurant lead. See the **Detailed** dataset view for every field, or the **Overview** view for the columns most outbound teams use.

### Pricing - Pay Per Event

| Event | Price | When charged |
|---|---|---|
| Base record | $0.005 | Always, per emitted record |
| Email enrichment | + $0.005 | When at least one email is found |
| Premium signals | + $0.010 | When POS / payment / ordering / delivery / reservation platform detected |
| Scheduled delta run | $0.05 | Once per scheduled run in monitor mode (emits only new/changed restaurants) |

**Effective price per record:** $0.005 base / $0.010 with email / $0.020 fully enriched. You only pay for events that actually fire - a base-tier run of 1,000 restaurants is $5; the same run fully enriched with POS/delivery detection is about $20.

### Build a growing, de-duplicated database (accumulate mode)

Set the **`collection`** input to a name (e.g. `toronto-db`) to accumulate leads into one persistent, de-duplicated dataset across many runs:

- The actor skips restaurants already in that collection (deduped by Google place ID, **before** enrichment - so you never pay to re-scrape a place you already have) and appends only **new** leads to a named dataset of the same name.
- Re-run with the same `collection` across different cities and search terms (or on a schedule) and the database keeps growing - **no duplicates**, and you only pay per-record for net-new leads.
- Leave `collection` blank for a normal one-off run.

This is the right way to build a large lead database: rotate inputs across metros/terms under one shared collection name, instead of re-running the same input (which just returns the same places). Accumulate mode adds a small per-run surcharge.

### Coverage: provinces and major metros

- **ON** Toronto, Ottawa, Mississauga, Hamilton, London, Brampton, Kitchener, Windsor, Markham, Vaughan
- **QC** Montreal, Quebec City, Laval, Gatineau, Longueuil, Sherbrooke, Trois-Rivieres
- **BC** Vancouver, Surrey, Burnaby, Richmond, Victoria, Kelowna
- **AB** Calgary, Edmonton, Red Deer, Lethbridge
- **MB** Winnipeg  **SK** Saskatoon, Regina  **NS** Halifax  **NB** Moncton, Fredericton, Saint John
- **NL** St. John's  **PE** Charlottetown  **NT** Yellowknife  **NU** Iqaluit  **YT** Whitehorse

### Restaurant types covered

`full_service_restaurant | fast_casual | qsr | cafe_bakery | bar_brewery | pizzeria | food_truck | ghost_kitchen`

### Platforms detected

**POS:** Toast, Square, Clover, Lightspeed (Montreal), TouchBistro (Toronto), Revel, Aloha (NCR), Micros (Oracle Simphony), Upserve, Tabit, Lavu, plus Canadian systems Maitre'D (Posera), Veloce/PayFacto, and Squirrel Systems.

**Payments:** Moneris (the dominant Canadian processor, an RBC/BMO joint venture), Global Payments Canada, Stripe, PayPal - a strong buying signal for payment sales teams.

**Delivery / ordering:** SkipTheDishes (the dominant Canadian aggregator), Ritual, Fantuan, UEAT (Quebec), DoorDash, Uber Eats, Grubhub, ChowNow, BentoBox, Olo, Popmenu, Smooth Commerce, Incentivio.

**Reservations:** OpenTable, Resy, Tock, Libro, SevenRooms, Yelp Reservations.

**Website builders:** WordPress, Shopify, Wix, Squarespace, Weebly, GoDaddy, Duda - a restaurant on a generic site builder is a prime target for website / all-in-one sales.

**Email marketing:** Mailchimp, Klaviyo, Constant Contact, ActiveCampaign.

**Analytics / social:** Google Tag Manager, Facebook Pixel, Instagram embed.

### How to read the tech detections (important)

Treat `tech_stack` as a list of **confirmed positives, not a full inventory**:

- A platform that **is** listed was fingerprinted on the site - reliable.
- A platform that is **not** listed means **not detected**, which is not the same as "not used". Many "Order Online" / "Reserve" buttons are JavaScript widgets that render after the page loads, so they can be invisible to HTML scraping; niche platforms may not be fingerprinted yet; and some restaurants have no website or a social-only presence.

For whether a restaurant **offers** delivery, takeout, dine-in, curbside, or reservations at all, use **`google_signals`** - these flags come from the Google Maps listing itself (not the website), so a `false` is Google's own answer and a missing key means Google did not report it (unknown, not false).

Combine the two: `google_signals.delivery = true` with no delivery platform in `tech_stack` is a restaurant that **delivers but whose platform we could not fingerprint** - usually your best "investigate / likely greenfield" segment. `google_signals.delivery = false` is a genuine "does not offer delivery".

### FAQ

**Q: Is this legal?**
A: Yes. Data is sourced from publicly listed Google Maps business profiles and the restaurants' own public websites. No login walls bypassed; no private data scraped. You are responsible for CASL (Canada's Anti-Spam Legislation) and applicable provincial privacy law (PIPEDA, Quebec Law 25) compliance when contacting leads.

**Q: Does it handle Quebec French listings?**
A: Yes. Service-line classification includes French category needles (brasserie, boulangerie, rotisserie, casse-croute, poutine, etc.), and detected French-language service is surfaced in `enrichment.languages_offered`.

**Q: How are Canadian addresses normalized?**
A: Country is set to `CA`, the province is canonicalized to a 2-letter code (from an English or French name, or inferred from the postal code), and the postal code is tidied to `A1A 1A1`.

**Q: Why might a run return only a few records?**
A: The most common cause is the Apify free plan's monthly platform-credit ceiling, after which the actor soft-fails into a clearly-labeled demo mode with sample records and an upgrade message. Upgrade to Apify Starter ($49/mo) to remove the ceiling.

**Q: How do I find restaurants NOT using a specific POS?**
A: Filter the dataset where `enrichment.tech_stack` does not contain the platform identifier (e.g. exclude `toast`, `lightspeed`, `touchbistro`).

### Related Actors

- [Restaurant Lead Finder (US)](https://apify.com/seibs.co/restaurant-lead-finder) - the US sibling, same engine and schema
- [B2B Sales Triggers](https://apify.com/seibs.co/b2b-sales-triggers) - intent signals layered on top of each restaurant
- [Google Maps Reviews Pro](https://apify.com/seibs.co/google-maps-reviews-pro) - full review history per restaurant
- [Email Verifier (Batch)](https://apify.com/seibs.co/email-verifier-batch) - verify the scraped emails before outreach

### Support

- Email: support@seibs.co
- GitHub issues: https://github.com/seibs-co/apify-portfolio/issues

### Changelog

See [CHANGELOG.md](./CHANGELOG.md).

### Found this useful?

If this actor saved you time or money, please leave a quick review on the Apify Store: https://apify.com/seibs.co/restaurant-lead-finder-canada#reviews

# Actor input Schema

## `search_terms` (type: `array`):

Google Maps search queries. Combined with each location to form (term × location) tuples. Example: \['restaurants', 'cafe', 'pizza']. Quebec French terms work too ('restaurant', 'cafe', 'brasserie', 'poutine'). Up to 10 terms per run.

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

Canadian locations to search in. Format: 'City, PROV' (2-letter province code), neighbourhood, or postal FSA. Province codes: AB BC MB NB NL NS NT NU ON PE QC SK YT. Examples: \['Toronto, ON', 'Montreal, QC', 'Vancouver, BC', 'Le Plateau-Mont-Royal', 'M5V']. Up to 25 locations per run.

## `max_results_per_query` (type: `integer`):

Cap on records returned per (term × location) tuple. Hard max 100 to protect compute budget.

## `enrichment_tier` (type: `string`):

Determines which signals are extracted and which Pay-Per-Event charges apply. base = Maps profile only ($0.005/record). email = + scraped email ($0.010/record). premium = + POS/payment/delivery platform detection + reservation system + service area ($0.020/record). Set 'premium' to enable deep tech-stack detection (incurs additional compute time).

## `service_lines` (type: `array`):

Restrict output to these restaurant types. Records that don't classify into one of these are dropped before enrichment. Leave empty to allow all.

## `exclude_chains` (type: `boolean`):

Drop records matching a built-in chain blocklist (Tim Hortons, A\&W, Swiss Chalet, St-Hubert, Boston Pizza, Pizza Pizza, La Belle Province, plus McDonald's, Starbucks, Subway and other cross-border chains — 130+ brands). Useful when targeting independents for SaaS sales.

## `max_concurrent_enrichments` (type: `integer`):

Parallel HTTP fetches against restaurant websites. Lower if you hit rate limits. 4 is safe.

## `collection` (type: `string`):

Name a persistent collection (e.g. 'toronto-db') to build ONE growing, de-duplicated lead database across many runs. When set, the actor skips restaurants already in this collection (deduped by Google place ID, BEFORE enrichment - so you don't pay to re-scrape them) and appends only NEW leads to a named dataset of the same name. Re-run with the same collection name across cities and search terms to keep growing it with no duplicates. Leave blank for a normal one-off run. Note: accumulate mode adds a per-run surcharge.

## Actor input object example

```json
{
  "search_terms": [
    "restaurants"
  ],
  "locations": [
    "Toronto, ON"
  ],
  "max_results_per_query": 50,
  "enrichment_tier": "base",
  "service_lines": [],
  "exclude_chains": true,
  "max_concurrent_enrichments": 4
}
```

# Actor output Schema

## `datasetItems` (type: `string`):

Narrow, token-efficient slice of every record. Consumer: LLM agents (Claude, GPT, LangChain tools), MCP hosts, dashboards. SDR-skim fields: name, vertical, service line, city, province, phone, email, website, confidence\_score.

## `datasetItemsDetailed` (type: `string`):

All fields for every record. Consumer: humans browsing the dataset in the Apify UI, RAG ingest pipelines, and full backups. Larger payload — not recommended as a direct LLM tool response.

## `datasetItemsUnwind` (type: `string`):

Same records, but each element of the `technologies` array is promoted to its own row. Consumer: Zapier / Make / n8n / Google Sheets users who want one row per item, and LLM agents that prefer flat rows over nested arrays.

## `datasetItemsMcp` (type: `string`):

First 50 overview records as a clean JSON array. Wrap on the agent side in an MCP tool-call response envelope, e.g. `{ "ok": true, "data": <this array>, "meta": { "actor": "restaurants_ca", "count": <len>, "view": "overview" } }`. Consumer: MCP servers, Claude Desktop, Cursor, OpenAI Assistants tool calls.

## `datasetItemsCsv` (type: `string`):

Spreadsheet-friendly export of the overview view. Consumer: humans, sales-ops teams, Excel / Google Sheets users.

# 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 = {
    "search_terms": [
        "restaurants"
    ],
    "locations": [
        "Toronto, ON"
    ],
    "max_results_per_query": 50,
    "enrichment_tier": "base",
    "max_concurrent_enrichments": 4
};

// Run the Actor and wait for it to finish
const run = await client.actor("seibs.co/restaurant-lead-finder-canada").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 = {
    "search_terms": ["restaurants"],
    "locations": ["Toronto, ON"],
    "max_results_per_query": 50,
    "enrichment_tier": "base",
    "max_concurrent_enrichments": 4,
}

# Run the Actor and wait for it to finish
run = client.actor("seibs.co/restaurant-lead-finder-canada").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "search_terms": [
    "restaurants"
  ],
  "locations": [
    "Toronto, ON"
  ],
  "max_results_per_query": 50,
  "enrichment_tier": "base",
  "max_concurrent_enrichments": 4
}' |
apify call seibs.co/restaurant-lead-finder-canada --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=seibs.co/restaurant-lead-finder-canada",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Canada Restaurant Lead Finder - POS & Delivery Detection",
        "description": "Enriched Google Maps leads for Canadian restaurants, all 13 provinces. Detects POS (Lightspeed, TouchBistro, Toast), payments (Moneris), delivery (SkipTheDishes, Uber Eats) and reservations. Bilingual EN/FR, Canadian postal codes. For restaurant SaaS and delivery sales teams.",
        "version": "0.2",
        "x-build-id": "6gzWh82z3SRVL5cLM"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/seibs.co~restaurant-lead-finder-canada/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-seibs.co-restaurant-lead-finder-canada",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/seibs.co~restaurant-lead-finder-canada/runs": {
            "post": {
                "operationId": "runs-sync-seibs.co-restaurant-lead-finder-canada",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/seibs.co~restaurant-lead-finder-canada/run-sync": {
            "post": {
                "operationId": "run-sync-seibs.co-restaurant-lead-finder-canada",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "required": [
                    "search_terms",
                    "locations",
                    "enrichment_tier"
                ],
                "properties": {
                    "search_terms": {
                        "title": "Search terms",
                        "maxItems": 10,
                        "type": "array",
                        "description": "Google Maps search queries. Combined with each location to form (term × location) tuples. Example: ['restaurants', 'cafe', 'pizza']. Quebec French terms work too ('restaurant', 'cafe', 'brasserie', 'poutine'). Up to 10 terms per run.",
                        "default": [
                            "restaurants"
                        ],
                        "items": {
                            "type": "string"
                        }
                    },
                    "locations": {
                        "title": "Locations (Canada)",
                        "maxItems": 25,
                        "type": "array",
                        "description": "Canadian locations to search in. Format: 'City, PROV' (2-letter province code), neighbourhood, or postal FSA. Province codes: AB BC MB NB NL NS NT NU ON PE QC SK YT. Examples: ['Toronto, ON', 'Montreal, QC', 'Vancouver, BC', 'Le Plateau-Mont-Royal', 'M5V']. Up to 25 locations per run.",
                        "default": [
                            "Toronto, ON"
                        ],
                        "items": {
                            "type": "string"
                        }
                    },
                    "max_results_per_query": {
                        "title": "Max results per query",
                        "minimum": 1,
                        "maximum": 100,
                        "type": "integer",
                        "description": "Cap on records returned per (term × location) tuple. Hard max 100 to protect compute budget.",
                        "default": 50
                    },
                    "enrichment_tier": {
                        "title": "Enrichment tier",
                        "enum": [
                            "base",
                            "email",
                            "premium"
                        ],
                        "type": "string",
                        "description": "Determines which signals are extracted and which Pay-Per-Event charges apply. base = Maps profile only ($0.005/record). email = + scraped email ($0.010/record). premium = + POS/payment/delivery platform detection + reservation system + service area ($0.020/record). Set 'premium' to enable deep tech-stack detection (incurs additional compute time).",
                        "default": "base"
                    },
                    "service_lines": {
                        "title": "Restaurant types (optional filter)",
                        "uniqueItems": true,
                        "type": "array",
                        "description": "Restrict output to these restaurant types. Records that don't classify into one of these are dropped before enrichment. Leave empty to allow all.",
                        "items": {
                            "type": "string",
                            "enum": [
                                "full_service_restaurant",
                                "fast_casual",
                                "qsr",
                                "cafe_bakery",
                                "bar_brewery",
                                "pizzeria",
                                "food_truck",
                                "ghost_kitchen"
                            ]
                        },
                        "default": []
                    },
                    "exclude_chains": {
                        "title": "Exclude national chains",
                        "type": "boolean",
                        "description": "Drop records matching a built-in chain blocklist (Tim Hortons, A&W, Swiss Chalet, St-Hubert, Boston Pizza, Pizza Pizza, La Belle Province, plus McDonald's, Starbucks, Subway and other cross-border chains — 130+ brands). Useful when targeting independents for SaaS sales.",
                        "default": true
                    },
                    "max_concurrent_enrichments": {
                        "title": "Max concurrent website enrichments",
                        "minimum": 1,
                        "maximum": 8,
                        "type": "integer",
                        "description": "Parallel HTTP fetches against restaurant websites. Lower if you hit rate limits. 4 is safe.",
                        "default": 4
                    },
                    "collection": {
                        "title": "Accumulate into a collection (optional)",
                        "type": "string",
                        "description": "Name a persistent collection (e.g. 'toronto-db') to build ONE growing, de-duplicated lead database across many runs. When set, the actor skips restaurants already in this collection (deduped by Google place ID, BEFORE enrichment - so you don't pay to re-scrape them) and appends only NEW leads to a named dataset of the same name. Re-run with the same collection name across cities and search terms to keep growing it with no duplicates. Leave blank for a normal one-off run. Note: accumulate mode adds a per-run surcharge."
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
