# Yellow Pages NZ $0.8💰 Scraper (`abotapi/yellow-nz-scraper`) Actor

From $0.8/1K. Scrapes business listings from Yellow.co.nz (New Zealand Yellow Pages). Extract comprehensive business information, including contact details, emails, ratings, reviews, opening hours, and geo coordinates.

- **URL**: https://apify.com/abotapi/yellow-nz-scraper.md
- **Developed by:** [Abot API](https://apify.com/abotapi) (community)
- **Categories:** Lead generation, Automation, Developer tools
- **Features:** Limited permissions
- **Stats:** 14 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.80 / 1,000 results

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

## Yellow.co.nz Scraper: Business Contacts, Ratings & Reviews

Yellow.co.nz Scraper turns New Zealand's Yellow Pages directory into structured business data. Search any trade or keyword in any NZ location and get contact details, ratings, reviews, opening hours and geo coordinates for every matching business. Run a one-off search, resume an interrupted run, or track a search on a schedule with incremental mode to get only new and changed listings, then export to JSON, CSV or Excel, or pull the results straight into your app through the API.

### Why This Scraper?

- **Full contact data from every listing.** Phone, email, full postal address, latitude and longitude, and opening hours, pulled straight from each business's detail page.
- **Search by any trade and any NZ location.** Enter a keyword and a place name, no need to browse categories by hand.
- **Automatic pagination.** Walks every search result page for you, with independent caps on pages and total listings so you control run size and cost.
- **Deep enrichment on demand.** Turn on detail page scraping for reviews, star ratings, year established, trade associations, services and payment methods, or leave it off for a fast, contact only pass.
- **Built for schedules.** Incremental mode returns only new and changed listings on recurring runs, and a genuinely blocked first page fails the run loudly instead of quietly returning an empty dataset.
- **Resume support.** Continue an interrupted run from its run or dataset ID without re-collecting, and re-paying for, listings already gathered.
- **Pipe results into your apps.** Optionally send a summary of each business straight into Notion, Linear or Airtable through MCP connectors.

### Use Cases

- **Lead generation teams:** pull phone numbers and email addresses for a trade and region to build an outreach list.
- **Local SEO and directory agencies:** audit categories, ratings and review counts for a niche across multiple towns.
- **Market researchers:** build a census of every business in a trade across New Zealand, complete with location data for mapping.
- **Ops and compliance teams:** track new competitors or closures in a market with scheduled incremental runs.

### Data You Get

> Sample shape: values are illustrative placeholders, not from a live record.

| Field | Example |
| --- | --- |
| `name` | `"Example Plumbing Services Ltd"` |
| `phone` | `"07 012 3456"` |
| `email` | `"info@example-plumbing.co.nz"` (when detail pages are scraped) |
| `address` | `"10 Example Street, Hamilton, WKO, 3200"` |
| `fullAddress` | `{ "street": "10 Example Street", "locality": "Hamilton", "region": "WKO", "postalCode": "3200", "country": "NZ" }` |
| `latitude` / `longitude` | `-37.787` / `175.2793` |
| `categories` | `["Plumbers", "Drainlayers"]` |
| `rating` | `4.5` |
| `reviewCount` | `12` |
| `reviews` | `[{ "author": "Jane Example", "rating": 5, "text": "Quick, tidy and reasonably priced.", "date": "2024-03-15" }]` |
| `website` | `"https://example-plumbing.co.nz"` |
| `detailUrl` | `"https://yellow.co.nz/hamilton/bpp/example-plumbing-services-12345678"` |
| `openStatus` | `"OPEN NOW"` (also `"CLOSED NOW"`) |
| `openingHours` | `["Mo-Fr 07:00-17:00", "Sa 08:00-12:00"]` |
| `description` | `"Example Plumbing Services has been serving Hamilton homes and businesses since 1998..."` |
| `yearEstablished` | `"1998"` |
| `slogan` | `"Your trusted local plumber"` |
| `associations` | `"Master Plumbers, Gasfitters and Drainlayers NZ"` |
| `paymentAccepted` | `"visa, cash, mastercard"` |
| `servicesAndProducts` | `["Plumbing", "Drainage", "Gasfitting"]` |
| `imageUrl` / `logoUrl` | `"https://i2.ypcdn.com/blob/..."` / `"https://i1.ypcdn.com/blob/..."` |
| `searchTerms` / `searchLocation` | `"Plumbers"` / `"Hamilton"` |
| `changeType` (incremental mode only) | `"NEW"` (also `UPDATED` / `UNCHANGED` / `REAPPEARED` / `EXPIRED`) |
| `changedFields` / `firstSeenAt` / `lastSeenAt` (incremental mode only) | `["phone"]` / `"2026-01-01T00:00:00Z"` / `"2026-01-02T00:00:00Z"` |

Detail pages can also return `generalInfo`, a longer business description than the search snippet, when a business's profile carries one. The four incremental mode fields above appear only when `incrementalMode` is on; on a normal run they are absent from the record entirely, not empty.

### How to Use

1. Enter **Search Terms** (a trade or keyword) and **Location** (a city, region, or "New Zealand").
2. Turn **Scrape Detail Pages** on for full contact and profile data, or off for a fast, search-results-only pass.
3. Set **Max Listings** and/or **Max Pages** to control run size and cost, and **Max Concurrency** to control how many detail pages fetch at once.
4. Click **Start**, then download the dataset as JSON, CSV or Excel, or read it through the API.

**A quick contact list, no detail pages:**

```json
{
  "searchTerms": "Electricians",
  "location": "Auckland",
  "scrapeDetails": false,
  "maxListings": 50
}
```

**Full enrichment with an explicit proxy group:**

```json
{
  "searchTerms": "Plumbers",
  "location": "Wellington",
  "scrapeDetails": true,
  "maxConcurrency": 5,
  "maxListings": 100,
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyGroups": ["RESIDENTIAL"]
  }
}
```

**Track a search on a schedule, only new and changed listings:**

```json
{
  "searchTerms": "Cafes",
  "location": "Christchurch",
  "incrementalMode": true,
  "stateKey": "christchurch-cafes",
  "emitExpired": true
}
```

**Resume an interrupted run:**

```json
{
  "searchTerms": "Builders",
  "location": "New Zealand",
  "resumeFromRunId": "<PREVIOUS_RUN_OR_DATASET_ID>",
  "maxListings": 0
}
```

#### Run it from your code

Python:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("abotapi/yellow-nz-scraper").call(run_input={"searchTerms": "Plumbers", "location": "Auckland"})
for business in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(business["name"], business.get("phone"))
```

JavaScript:

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('abotapi/yellow-nz-scraper').call({ searchTerms: 'Plumbers', location: 'Auckland' });
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

Or connect it to Make, Zapier, n8n, Google Sheets or webhooks from the **Integrations** tab.

#### How Max Pages and Max Listings interact

Yellow.co.nz serves roughly 30 listings per search results page. **Max Pages** bounds how many of those pages the actor walks; leave it empty to walk every page the site has for that search. **Max Listings** is the actual run size cap, checked before each page is processed, so a run stops as soon as it has collected enough listings even if Max Pages would allow more. `0` on Max Listings, or an empty or `0` Max Pages, means no cap from that field; an empty Max Listings falls back to its default of 20.

#### Resume and recurring updates

Two different things, both in the same input section because they are easy to confuse:

- **Resume from run ID** (`resumeFromRunId`) continues ONE specific interrupted run. Paste a previous run ID (or dataset ID) from this actor; listings already collected there are skipped, so this run only returns new ones.
- **Incremental mode** (`incrementalMode`) is for running the same search again and again (daily or weekly) and getting only what changed, without pasting a run ID every time. The actor keeps its own baseline in a key-value store, keyed by a hash of `searchTerms` + `location` + `scrapeDetails` (the caps `maxListings` / `maxPages` / `maxConcurrency` are deliberately excluded from that key, so changing them alone never starts a new monitoring campaign). Use `stateKey` to name a campaign explicitly or to share state across differently-configured runs.

Each output record in incremental mode gets four extra fields: `changeType` (`NEW` / `UPDATED` / `UNCHANGED` / `REAPPEARED` / `EXPIRED`), `changedFields`, `firstSeenAt`, `lastSeenAt`. `UNCHANGED` listings are suppressed (not returned, not billed) unless `emitUnchanged` is on.

`EXPIRED` rows are produced only once a run has scanned the tracked search to a genuine, uncapped, non-resumed end, and only when `emitExpired` is on. A run interrupted by a page failure, a `maxListings` or `maxPages` cap, or a resumed continuation never emits `EXPIRED` rows, and the stored state for those listings is left exactly as it was. `REAPPEARED` can only happen to a listing that a PRIOR complete, `emitExpired` run had already marked `EXPIRED`; a listing that was simply never marked gone stays `UNCHANGED` or turns `UPDATED`, it is never `REAPPEARED` on its own.

**What counts as "changed":** every field is compared except a few intentional exclusions, so the site's own rendering quirks never look like a business changed.

- `openStatus` ("OPEN NOW" / "CLOSED NOW") reflects the business's fixed hours crossed with the current time, not the business itself, so it is excluded from change detection while still being shown in the output.
- `imageUrl` / `logoUrl` are served from a small pool of mirror hosts chosen per request; only the mirror host is normalized away for comparison, the URL in the output is always the one actually observed.
- `description` (the search-results snippet) is sometimes served cut short mid-sentence with a trailing ellipsis. A pair where the shorter side is a truncation-marked prefix of the longer side is treated as unchanged; a genuine edit still registers as `UPDATED`.
- `address` sometimes includes an NZ region-code segment and sometimes does not. Only that formatting difference is normalized away; a genuinely different street number, name or postcode still registers as `UPDATED`.
- `address` occasionally renders as a "Servicing: <region list>" service-area line instead of a street address. That substitution is treated like a failed fetch: the output and the stored baseline keep the last known real street address rather than overwriting it. A business that has never had a real street address keeps showing its service-area text.

**Failed or partial detail fetch:** if a business's detail page fails to fetch, or a field comes back empty, on a given run, that field is backfilled from the last known good value before comparison, so a transient failure never looks like the business lost data and never poisons the next comparison. A field that never had a real value stays empty; nothing is invented.

### Send results into your apps (MCP connectors)

Optionally pipe the scraped results into the apps you already use, via Model Context Protocol (MCP) connectors. This is an extra delivery step **after** the scrape; the Apify dataset is never changed.

**What gets written to the connector:** a condensed, human-readable **summary** of each record, not the full JSON. Each item becomes one entry with a **title** and its key fields flattened to plain text. The **complete record always stays in the Apify dataset**.

1. Authorize a connector once under **Apify → Settings → Integrations** (Notion, Linear, Airtable, or Apify).
2. Select it in the **"Pipe results into your apps"** input field. (If the picker is empty, you haven't authorized a connector yet.)
3. For **Notion**, also set `notionParentPageUrl` to the page where items should be created.

The connection is mediated by Apify's MCP proxy, so this actor never sees your third-party credentials. Leave the field empty to skip.

#### URL mode (Start URLs)

Paste yellow.co.nz links into **Start URLs** and the run scrapes them instead of running a keyword search. Search Terms and Location are ignored when you do. Plain place names work in Location (for example `Ponsonby` or `Auckland`), and a search that matches nothing finishes cleanly with an empty dataset rather than an error.

| Link type | Example | What you get |
| --- | --- | --- |
| Search results | `https://yellow.co.nz/search?search_terms=Plumbers&geo_location_terms=Auckland` | Every listing on the results pages, page after page (a `page=` value in the link is honoured). |
| Browse page (place and category) | `https://yellow.co.nz/auckland/plumbers` | The listings of that place and category, page after page. |
| One business page | `https://yellow.co.nz/auckland/bpp/onehunga-plumbers-579426046` | That single business, identified by the number at the end of the link (never by a text search). |

Any other yellow.co.nz or non-yellow.co.nz link is skipped, with a warning in the run log naming the link.

**Untouched form:** the Start URLs field ships with one example of each link type above. If you run with the field untouched and Search Terms and Location unchanged, the run scrapes those three examples (bounded by Max Listings). If you only change Search Terms or Location (for example through the API) while leaving Start URLs untouched, the keyword search runs exactly as before. Clear the list, or send an empty list, to force a keyword search.

#### Several searches, filters and flat fields

- **Search Queries / Locations**: give several keywords and several New Zealand places in one run; every keyword and place pair is searched once and duplicates are removed. Empty lists fall back to Search Terms and Location.
- **Filters** (applied to the scraped result, so the run still reads the pages behind them): Minimum rating, Only businesses with an email, Only businesses with a website. Only listings that pass count towards Max Listings and are billed as results. Email filtering needs Scrape Detail Pages.
- **Extra flat fields** on every row: `businessId`, `listingUrl`, `category`, `street`, `city`, `suburb`, `region`, `postalCode`, `country`, `addressText`, `allEmails`, `emailSource`, `searchQuery`, `source`, `observedAt`. `suburb` is read from the search-result address when it has a separate suburb segment (for example "Auckland Central"), also on rows whose detail page loaded, otherwise it is absent; `city` and `postalCode` fall back to the card address when the detail page has no structured address. The nested fields stay unchanged.
- Sorting is not offered: the site has no verified sort option. Email and contact enrichment beyond the business page is not part of this actor.

### Input Parameters

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `searchTerms` | string | `Plumbers` | Business type or keyword to search for. |
| `location` | string | `New Zealand` | Location to search in (city, region, or "New Zealand"). |
| `searchQueries` | array | none | Several keywords in one run; combined with Locations, each pair searched once, duplicates removed. Empty uses Search Terms. Ignored when Start URLs is customised; Start URLs left at the shipped examples do not block it. |
| `locations` | array | none | Several New Zealand places in one run. Empty uses Location. |
| `minRating` | number | none | Keep only businesses rated at least this (1-5); unrated businesses are dropped when set. |
| `hasEmail` | boolean | `false` | Keep only businesses whose page shows an email. Needs Scrape Detail Pages. |
| `hasWebsite` | boolean | `false` | Keep only businesses that list a website. |
| `startUrls` | array | three example links (one per link type) | Paste yellow.co.nz links instead of typing a search. See the URL mode table below. |
| `maxPages` | integer | none (empty walks every result page) | Optional bound on how many search result pages to walk (roughly 30 listings per page); the run size is otherwise controlled by Max Listings. |
| `maxListings` | integer | `20` | Hard cap on total business listings returned. `0` means no cap. |
| `maxReviews` | integer | `20` | Most customer reviews kept per business (0 to 100; needs Scrape Detail Pages). The business pages list at most 5 reviews, so the default of 20 does not cut anything today; the cap only matters if the site shows more. Changing it never marks a business as updated in incremental mode. Reviews are included in the per-record price, nothing extra is charged. |
| `scrapeDetails` | boolean | `true` | Fetch each business's detail page for email, full address, geo coordinates, opening hours, reviews and more. |
| `maxConcurrency` | integer | `5` | Maximum concurrent detail page requests (1-20). |
| `proxy` | object | none (backup connection) | Proxy configuration. Apify Proxy with the RESIDENTIAL group is recommended; see Plan Requirement. |
| `resumeFromRunId` | string | none | Continue one interrupted run: paste its run or dataset ID and already-collected listings are skipped. |
| `incrementalMode` | boolean | `false` | Turn on for recurring monitoring of the same search; later runs return only NEW and UPDATED listings (plus REAPPEARED, once an earlier run with Emit expired listings on had marked a listing EXPIRED). |
| `stateKey` | string | none | Optional. Name a monitoring campaign, or share state deliberately across differently-configured runs. |
| `emitUnchanged` | boolean | `false` | Also return (and bill for) listings unchanged since the last run, marked UNCHANGED. |
| `emitExpired` | boolean | `false` | Also return (and bill for) listings from a previous run no longer found, marked EXPIRED. Only produced after a run that scanned the tracked search to its natural end, never on a resumed or capped run. |
| `ignoreFieldsForChanges` | array | none | Incremental mode only: field names (dots for nested, e.g. `reviews.date`) that never count as a change. They stay in the output; `openStatus` is always ignored. |
| `mcpConnectors` | array | none | Optional: send a summary of each record to apps you authorized under Integrations. |
| `notionParentPageUrl` | string | none | Notion connector only: page under which item pages are created. |
| `maxNotifyListings` | integer | `50` | Cap on items written to each connector per run. Does not affect the dataset. |

### Output Example

> Sample shape: values are illustrative placeholders, not from a live record.

```json
{
  "name": "Example Plumbing Services Ltd",
  "phone": "07 012 3456",
  "email": "info@example-plumbing.co.nz",
  "address": "10 Example Street, Hamilton, WKO, 3200",
  "fullAddress": {
    "street": "10 Example Street",
    "locality": "Hamilton",
    "region": "WKO",
    "postalCode": "3200",
    "country": "NZ"
  },
  "latitude": -37.787,
  "longitude": 175.2793,
  "categories": ["Plumbers", "Drainlayers"],
  "rating": 4.5,
  "reviewCount": 12,
  "reviews": [
    {
      "author": "Jane Example",
      "rating": 5,
      "text": "Quick, tidy and reasonably priced.",
      "date": "2024-03-15"
    }
  ],
  "website": "https://example-plumbing.co.nz",
  "detailUrl": "https://yellow.co.nz/hamilton/bpp/example-plumbing-services-12345678",
  "openStatus": "OPEN NOW",
  "openingHours": ["Mo-Fr 07:00-17:00", "Sa 08:00-12:00"],
  "description": "Example Plumbing Services has been serving Hamilton homes and businesses since 1998...",
  "yearEstablished": "1998",
  "slogan": "Your trusted local plumber",
  "associations": "Master Plumbers, Gasfitters and Drainlayers NZ",
  "paymentAccepted": "visa, cash, mastercard",
  "servicesAndProducts": ["Plumbing", "Drainage", "Gasfitting"],
  "imageUrl": "https://i2.ypcdn.com/blob/example-image",
  "logoUrl": "https://i1.ypcdn.com/blob/example-logo",
  "searchTerms": "Plumbers",
  "searchLocation": "Hamilton"
}
```

### Plan Requirement

Yellow.co.nz refuses most requests that do not come from a residential connection, so for dependable results use **Proxy Configuration** with Apify Proxy and the **RESIDENTIAL** group. Choosing it without a country pins the connection to New Zealand. Even then a single connection can be refused, so the actor retries each page on a fresh connection and falls back to a backup connection when the selected one is unavailable.

Apify free plans do not include residential proxies. On a free plan, or with no proxy set, the run continues on the backup connection and logs a warning that some pages may still be unavailable. If the very first results page cannot be loaded, the run fails with a clear message instead of returning an empty dataset. To get residential access, upgrade your Apify plan or supply your own proxy URLs.

### FAQ

#### How much does it cost?

Billing has two events. A start charge applies once per run, scaled by the memory the run uses (per GB). On top of that, each result row written to the dataset is charged once. Rows that are not delivered (for example unchanged listings in incremental mode, unless you turn on Emit unchanged listings) are not charged. The **Pricing** tab on the actor page shows the current rates. Use **Max Listings** (and **Max Pages**) to cap the cost of any run.

#### Is it legal to scrape Yellow.co.nz?

This actor collects only publicly available business listing data. You are responsible for how you use it: follow Yellow.co.nz's terms and the laws that apply to you, and get legal advice if you plan commercial redistribution. Customer reviews and photos may carry their own rights beyond the underlying facts.

#### Can I track a search on a schedule and get only new or changed listings?

Yes. Schedule the actor from the **Schedules** tab and turn on **Incremental changes for scheduled runs**. Later runs then return only NEW and UPDATED listings (plus REAPPEARED for a listing that an earlier run with Emit expired listings on had marked EXPIRED); unchanged listings are not billed unless you also turn on **Emit unchanged listings**.

#### What's the difference between Resume from run ID and Incremental mode?

They solve different problems. **Resume from run ID** continues ONE specific interrupted run from a pasted run or dataset ID. **Incremental mode** is for running the SAME search again and again on a schedule; the actor remembers its own baseline and classifies every listing against it, so you never have to paste an ID.

#### Why did my run fail instead of returning an empty dataset?

If Yellow.co.nz refuses every attempt at the very first search page, the run fails with a clear message instead of reporting success with nothing collected, so a genuinely blocked run is never confused with a search that legitimately matched zero businesses. A refusal on a later page, after real listings were already collected, does not fail the run; it keeps what was already gathered.

#### Can I use it with AI agents or MCP?

Yes. Call it from any Apify integration or MCP client, and use the connector field to push results into Notion, Linear or Airtable.

### 🔗 Want more Yellow data?

Pair this actor with these related scrapers from the same team:

<table>
<tr><td>📇 <a href="https://apify.com/abotapi/yellow-pages-au-scraper"><b>Yellow Pages AU Scraper</b></a><br>Scrape Yellow Pages Australia (yellowpages.com.au) business listings by category and...</td><td>📇 <a href="https://apify.com/abotapi/pagesjaunes-fr-scraper"><b>PagesJaunes FR</b></a><br>Scrapes business listings from PagesJaunes.fr (French Yellow Pages). Extract...</td></tr>
<tr><td>📇 <a href="https://apify.com/abotapi/herold-at-scraper"><b>Herold.at Scraper</b></a><br>Scrape Herold.at business listings across Austria into clean JSON. Extract names...</td><td>📇 <a href="https://apify.com/abotapi/truelocal-com-au-scraper"><b>TrueLocal AU Directory Listings &amp; Reviews Scraper</b></a><br>Scrape TrueLocal.com.au business listings by keyword, location, or URL. Extract names...</td></tr>
<tr><td>📇 <a href="https://apify.com/abotapi/local-ch-scraper"><b>Local.ch</b></a><br>Pull structured company data from local.ch, including name, address, GPS, phone, email...</td><td>📇 <a href="https://apify.com/abotapi/hipages-business-scraper"><b>HiPages Scraper</b></a><br>Scrape business listings from HiPages Australia by category, location, or URL. Extract...</td></tr>
</table>

👉 [Browse all abotapi scrapers](https://apify.com/abotapi)

### 💬 Support

- 🐞 **Found a bug or a missing field?** Open a ticket on the [Issues tab](https://apify.com/abotapi/yellow-nz-scraper/issues/open). We usually reply within hours.
- 🛠️ **Need another site, extra fields or a private build?** Email <contact@abotapi.com>.
- ⭐ **Enjoying it?** A quick review on the actor page helps other users find it.

# Actor input Schema

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

Optional. Paste yellow.co.nz links instead of typing a search: a search link (https://yellow.co.nz/search?search_terms=Plumbers\&geo_location_terms=Auckland), a browse link (https://yellow.co.nz/auckland/plumbers), or one business page (https://yellow.co.nz/auckland/bpp/onehunga-plumbers-579426046). Every other kind of link is skipped with a warning in the log. When you fill this in, Search Terms and Location are ignored. The form ships with one example of each link type; leave it untouched (and Search Terms / Location unchanged) and the run scrapes those three examples. To search by keyword and place instead, change Search Terms or Location, or clear this list.

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

Business type or keyword to search for (e.g., 'Plumbers', 'Electricians', 'Restaurants')

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

Location to search in (e.g., 'New Zealand', 'Auckland', 'Wellington', 'Christchurch')

## `searchQueries` (type: `array`):

Optional. Several keywords in one run (e.g. plumber, electrician). Combined with Locations below; each keyword and place pair is searched once and duplicates are removed. Empty = use Search Terms. Ignored when Start URLs is customised.

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

Optional. Several New Zealand places in one run (e.g. Auckland, Wellington). Empty = use Location above (New Zealand is nationwide).

## `minRating` (type: `number`):

Optional. Keep only businesses rated at least this (1-5). Unrated businesses are dropped when set. Leave empty for no rating filter.

## `hasEmail` (type: `boolean`):

Keep only businesses whose page shows an email. Needs Scrape Detail Pages.

## `hasWebsite` (type: `boolean`):

Keep only businesses that list a website.

## `maxListings` (type: `integer`):

Hard cap on total business listings returned. 0 means no cap (bounded by Maximum Pages / result count instead).

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

Optional bound on how many search result pages to walk (30 results per page). Leave empty to walk every result page; the run size is controlled by Max Listings, which this field defers to.

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

Maximum concurrent detail page requests. Lower values are safer against rate limiting.

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

Fetch each business's detail page for rich data: email, full address, geo coordinates, opening hours, reviews, year established. Slower but much more comprehensive.

## `maxReviews` (type: `integer`):

Maximum number of customer reviews kept on each business (needs Scrape Detail Pages). 0 returns no reviews.

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

Optional proxy settings. Recommended for large scrapes to avoid IP-based blocking.

## `resumeFromRunId` (type: `string`):

Optional: paste a previous run ID (or dataset ID) from this actor. Listings already collected there are skipped, so this run only returns new ones. Leave empty for a normal run. For recurring daily monitoring of the same search, use Incremental mode below instead.

## `incrementalMode` (type: `boolean`):

Turn this on for daily or recurring monitoring. The first run returns all matching listings as NEW. Later runs normally return only NEW, UPDATED, and REAPPEARED listings. Turn on "Emit unchanged" or "Emit expired" only when you also want those listings returned (and billed). State is kept separately for each search term/location/detail-mode setup; use State key when you want to name or deliberately share a monitoring campaign. To continue one specific interrupted run instead, use Resume from run ID above.

## `stateKey` (type: `string`):

Optional. Name this monitoring campaign to keep its state stable, or to deliberately share state across differently-configured runs. Leave empty to let the actor derive a key automatically from the search term/location/detail settings — different searches then never mix state with each other.

## `emitUnchanged` (type: `boolean`):

Off by default. Turn on to also return listings that have not changed since the last run, marked UNCHANGED. This returns — and bills — extra rows you already have, so leave it off unless you specifically want the full snapshot every run.

## `emitExpired` (type: `boolean`):

Off by default. Turn on to also return listings that were present in a previous run but are no longer found, marked EXPIRED. Only produced once a run has fully scanned the tracked search — not when Max Listings/Max Pages capped it, or when Resume was used. This returns — and bills — extra synthetic rows, so leave it off unless you need expiry tracking.

## `ignoreFieldsForChanges` (type: `array`):

Field names that must NOT count as a change in incremental mode, e.g. `imageUrl`. Use dots for nested fields (`reviews.date`). The fields stay in the output; they are only left out of change detection and `changedFields`. Changing this list never causes a one-time wave of UPDATED rows.

## `mcpConnectors` (type: `array`):

Optionally send the scraped results into the apps you already use, via Model Context Protocol (MCP) connectors. Authorize a connector once under Apify → Settings → Integrations, then select it here. The connector receives a condensed, human-readable summary per item (title + key fields), not the full JSON — the complete record stays in the dataset. Leave empty to skip. Supported: Notion (https://mcp.notion.com/mcp), Linear (https://mcp.linear.app/sse), Airtable (https://mcp.airtable.com/mcp), Apify (https://mcp.apify.com).

## `notionParentPageUrl` (type: `string`):

URL (or id) of the Notion page under which item pages are created. Required to enable the Notion export; ignored by other connectors.

## `maxNotifyListings` (type: `integer`):

Cap on items written to each connector per run. Does not affect the dataset.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://yellow.co.nz/search?search_terms=Plumbers&geo_location_terms=Auckland"
    },
    {
      "url": "https://yellow.co.nz/auckland/plumbers"
    },
    {
      "url": "https://yellow.co.nz/auckland/bpp/onehunga-plumbers-579426046"
    }
  ],
  "searchTerms": "Plumbers",
  "location": "New Zealand",
  "hasEmail": false,
  "hasWebsite": false,
  "maxListings": 20,
  "maxConcurrency": 5,
  "scrapeDetails": true,
  "maxReviews": 20,
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "maxNotifyListings": 50
}
```

# Actor output Schema

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

Individual business records with contact info, ratings, reviews, and location data

## `detail` (type: `string`):

Contacts, address & hours view of the dataset

## `reviews` (type: `string`):

Ratings & reviews view of the dataset

## `changes` (type: `string`):

Changes (incremental mode) view of the dataset

# 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 = {
    "startUrls": [
        {
            "url": "https://yellow.co.nz/search?search_terms=Plumbers&geo_location_terms=Auckland"
        },
        {
            "url": "https://yellow.co.nz/auckland/plumbers"
        },
        {
            "url": "https://yellow.co.nz/auckland/bpp/onehunga-plumbers-579426046"
        }
    ],
    "searchTerms": "Plumbers",
    "location": "New Zealand",
    "incrementalMode": false,
    "emitUnchanged": false,
    "emitExpired": false
};

// Run the Actor and wait for it to finish
const run = await client.actor("abotapi/yellow-nz-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 = {
    "startUrls": [
        { "url": "https://yellow.co.nz/search?search_terms=Plumbers&geo_location_terms=Auckland" },
        { "url": "https://yellow.co.nz/auckland/plumbers" },
        { "url": "https://yellow.co.nz/auckland/bpp/onehunga-plumbers-579426046" },
    ],
    "searchTerms": "Plumbers",
    "location": "New Zealand",
    "incrementalMode": False,
    "emitUnchanged": False,
    "emitExpired": False,
}

# Run the Actor and wait for it to finish
run = client.actor("abotapi/yellow-nz-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 '{
  "startUrls": [
    {
      "url": "https://yellow.co.nz/search?search_terms=Plumbers&geo_location_terms=Auckland"
    },
    {
      "url": "https://yellow.co.nz/auckland/plumbers"
    },
    {
      "url": "https://yellow.co.nz/auckland/bpp/onehunga-plumbers-579426046"
    }
  ],
  "searchTerms": "Plumbers",
  "location": "New Zealand",
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false
}' |
apify call abotapi/yellow-nz-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,abotapi/yellow-nz-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/eU0pDyoDHguwyDBaQ/builds/kalnwFnbpwkuyBO4n/openapi.json
