# Apple Maps Business Data Scraper (`khadinakbar/apple-maps-business-scraper`) Actor

Scrape public Apple Maps businesses by keyword and location or place URL. Returns name, phone, website, address, and coordinates. Optional website email enrich. Do not use for Google Maps — see google-maps-leads-scraper. Charged $0.004 per place.

- **URL**: https://apify.com/khadinakbar/apple-maps-business-scraper.md
- **Developed by:** [Khadin Akbar](https://apify.com/khadinakbar) (community)
- **Categories:** Lead generation, Travel, MCP servers
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.00 / 1,000 place scrapeds

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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

## Apple Maps Business Data Scraper

Scrape **public Apple Maps** businesses by keyword and location, Apple Maps search URLs, or place links. Each dataset row is one place with **name, phone, website, address, and GPS coordinates** from Apple’s MapKit HTTP search — optionally enriched with a **public email** found on the business website. Run it from Apify Console, the Apify API, or Apify MCP with no Apple login.

Built for local lead lists, territory research, and CRM enrichment when your workflow starts from **Apple Maps** place search. MapKit search returns NAP + coordinates (and category/timezone when Apple exposes them). Ratings, review counts, and opening hours stay out of this SKU’s contract so every row matches fields Apple actually returns in public search.

### Best fit

Use this Actor when you need Apple Maps places for a category in a city, or you already have `maps.apple.com` place/search URLs and want a flat, billable NAP dataset for MCP, CRM, or spreadsheet workflows.

When you need Google Maps ratings, reviews, or lead scoring instead, continue with [Local Business Lead Generator — Google Maps](https://apify.com/khadinakbar/google-maps-leads-scraper). Keep Apple Maps URLs on this Actor and Google Maps workflows on the Google Maps sibling.

### Practical scenario

A field-marketing lead needs coffee shops in San Francisco for a weekend pop-up list. They run the Console prefill (`coffee shops`, `San Francisco, CA`, `maxResults: 5`). The dataset returns five Apple Maps places with phone, website, address, and coordinates. On a second run they turn on `enrichEmails` only for venues that already have websites, and pay the separate email event only when a public mailto or contact-page address is found.

### Quick start input

```json
{
  "searchQuery": "coffee shops",
  "location": "San Francisco, CA",
  "maxResults": 5,
  "enrichEmails": false,
  "adaptiveGrid": true,
  "language": "en-US",
  "countryCode": "US"
}
```

### Input reference

| Field | Type | What it controls |
| --- | --- | --- |
| `searchQuery` | string | Apple Maps keyword/category (prefill: coffee shops) |
| `searchQueries` | array | Extra keywords merged with `searchQuery` |
| `location` | string | City/region text (prefill: San Francisco, CA) |
| `latitude` / `longitude` | number | Optional map center |
| `placeUrls` | array | Apple Maps place URLs |
| `searchUrls` | array | Apple Maps search share URLs |
| `maxResults` | integer | Cap 1–500 (default 20, prefill 5) |
| `enrichEmails` | boolean | Optional public-website email enrich (default false) |
| `requireWebsite` / `requirePhone` | boolean | Keep only places with website/phone |
| `adaptiveGrid` | boolean | Expand nearby grid cells when centered (default true) |
| `language` | string | Locale (default en-US) |
| `countryCode` | string | Country hint (default US) |
| `proxyConfiguration` | object | Optional Apify proxy |

Empty input with no query or URLs finishes `INVALID_INPUT` with zero place charges.

### What data you receive

One dataset item is one Apple Maps place. Download the dataset as JSON, HTML, CSV, or Excel.

| Field | Meaning |
| --- | --- |
| `placeId` / `muid` | Apple identifiers when present |
| `name` | Business name |
| `category` | POI category when available |
| `phone` | Public phone from MapKit |
| `website` | Website URL from MapKit |
| `email` / `emailSourceUrl` | Optional public email + page URL |
| `address`, `street`, `city`, `state`, `postalCode`, `country`, `countryCode` | Address parts |
| `latitude`, `longitude` | Coordinates |
| `timezone` | When Apple exposes it |
| `appleMapsUrl` | Place link |
| `searchQuery`, `searchLocation` | Provenance of the search |
| `source` | Route label (e.g. mapkit-search) |
| `scrapedAt` | ISO-8601 collection time |

```json
{
  "placeId": "I7EXAMPLE",
  "name": "Example Coffee Co.",
  "category": "Cafe",
  "phone": "+1 415-555-0100",
  "website": "https://example-coffee.example",
  "email": null,
  "address": "1 Market St, San Francisco, CA 94105, United States",
  "city": "San Francisco",
  "state": "CA",
  "postalCode": "94105",
  "countryCode": "US",
  "latitude": 37.7936,
  "longitude": -122.3950,
  "appleMapsUrl": "https://maps.apple.com/place?place-id=I7EXAMPLE",
  "searchQuery": "coffee shops",
  "searchLocation": "San Francisco, CA",
  "source": "mapkit-search",
  "scrapedAt": "2026-09-15T12:00:00.000Z"
}
```

`OUTPUT` and `RUN_SUMMARY` in the default key-value store hold `outcome`, `itemsPushed`, and `chargedEventCounts`.

### Pricing

This Actor uses **Pay per event** plus **platform usage**. Event prices below are the launch defaults; treat the live Pricing tab on the Actor page as the current source of truth, because platform usage (compute and proxy) is billed to the caller in addition to named events.

| Event | Price |
| --- | --- |
| `apify-actor-start` | $0.00005 |
| `place-scraped` (primary) | $0.004 per saved place |
| `email-enriched` | $0.006 per found public email |

Empty searches and filtered duplicates skip the `place-scraped` event. Worked example: `maxResults: 5` on one city search ≈ `$0.00005` start + `5 × $0.004` = about `$0.02005` in named events, plus platform usage. With `enrichEmails` on, add `$0.006` only for each place where a public email is found.

### Best results

Start with `searchQuery` + `location` (or coordinates) and `maxResults` at 5–20 while validating a new market. Turn on `requirePhone` or `requireWebsite` when your CRM needs contactable rows. Use `adaptiveGrid` when a single center returns a thin first page and you want nearby cells filled in. Enable `enrichEmails` only after you have websites worth crawling, so you pay the email event for useful hits. Place URLs and search share URLs work well for refresh jobs when you already know the Apple Maps links.

### How it works

The Actor uses Apple MapKit over HTTP (no browser, no Apple ID). It searches by keyword/location or resolves place/search URLs, normalizes NAP + coordinates, dedupes by place id / MUID / geo+name, applies optional filters, and optionally fetches public website HTML for emails. Every run writes `OUTPUT` and `RUN_SUMMARY` with an honest outcome (`COMPLETE`, `PARTIAL`, `VALID_EMPTY`, `INVALID_INPUT`, `UPSTREAM_FAILED`, or `CONFIG_ERROR`).

### Use through the API

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

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });

const run = await client.actor('khadinakbar/apple-maps-business-scraper').call({
  searchQuery: 'dentists',
  location: 'Austin, TX',
  maxResults: 10,
  enrichEmails: false,
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

### Use with AI agents through Apify MCP

> Find 10 coffee shops in San Francisco, CA on Apple Maps. Return name, phone, website, address, latitude, longitude, appleMapsUrl, and scrapedAt. Read the dataset plus OUTPUT for outcome and provenance, then confirm cost on the Pricing tab.

Connect via <https://mcp.apify.com>. Cost signal: about `$0.004` per saved place plus platform usage; email enrich is a separate `$0.006` event. Confirm current event prices on the Actor Pricing tab before large runs. Scope stays public MapKit NAP fields; ratings and hours belong on other Actors when you need them.

### Outcomes

| Outcome | Meaning |
| --- | --- |
| `COMPLETE` | At least one place saved. |
| `PARTIAL` | Some places saved; others empty or hit a charge cap. |
| `VALID_EMPTY` | Search or place resolved with zero matching public places. |
| `INVALID_INPUT` | No usable query, location, or Apple Maps URLs. |
| `UPSTREAM_FAILED` | Upstream MapKit transport returned no useful places. |

### Legal & responsible use

Use this Actor for **public** Apple Maps / MapKit business listing data you are authorized to process under applicable laws and site terms. Respect privacy rules (including GDPR/CCPA where applicable) and marketing contact rules for outreach. Keep use cases to legitimate research, lead generation, and analysis. Apple and Apple Maps are trademarks of Apple Inc. This independent Actor is not affiliated with, associated with, or endorsed by Apple Inc.

### Builder's note

I found that MapKit search needs a short-lived token bootstrap before `api.apple-mapkit.com` accepts place and geocode calls. I designed the path as HTTP-only (token → bootstrap → search/geocode/place) after live probes against city keyword searches and place URLs, and empty place-id lookups finish as `VALID_EMPTY` so unknown IDs stay honest and unbilled. Optional email enrich stays a separate charged event so NAP-only runs stay cheap.

# Actor input Schema

## `searchQuery` (type: `string`):

Business category or keyword to search on Apple Maps, such as coffee shops or dentists. Combine with location or latitude/longitude for best coverage. Prefer a short category phrase over a full sentence. This is Apple Maps search, not Google Maps — use google-maps-leads-scraper for Google.

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

Optional list of additional Apple Maps search keywords processed in the same run. Merged with searchQuery after dedupe. Use for related categories in one city (for example cafes and bakeries). Each unique place is still billed once.

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

City, region, or address text that centers the Apple Maps search, such as San Francisco, CA. Used with searchQuery when latitude/longitude are not set. Leave empty when providing exact coordinates or Apple Maps search URLs. Improves local NAP coverage for territory lists.

## `latitude` (type: `number`):

Optional map center latitude in decimal degrees. Pair with longitude for precise geocentering without relying on geocoded location text. When set with searchQuery, adaptiveGrid can expand nearby cells. Ignored for place URL lookups that already include coordinates.

## `longitude` (type: `number`):

Optional map center longitude in decimal degrees. Pair with latitude for precise geocentering. Use WGS84 values from Apple Maps share links or a known venue. Leave empty when location text or searchUrls already define the area.

## `placeUrls` (type: `array`):

Direct Apple Maps place links (maps.apple.com/place or place-id/auid query params). Each valid place is fetched and billed as place-scraped when persisted. Invalid non-Apple URLs are skipped with a warning. Prefer place URLs when you already have listing links.

## `searchUrls` (type: `array`):

Apple Maps search share URLs that include a query (and optional center/span). The Actor extracts the query and optional map center, then runs MapKit search. Place URLs pasted here are treated as place lookups. Non-Apple URLs are ignored.

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

Hard cap on unique places saved after dedupe and filters. Defaults to 20. Prefill 5 keeps quality tests fast and cheap. Maximum 500. Each persisted place costs $0.004 as place-scraped.

## `enrichEmails` (type: `boolean`):

When true, visit the business website and likely contact pages to find a public email address. Emails are charged separately as email-enriched at $0.006 only when found. Defaults to false. Does not invent or pattern-generate emails.

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

When true, keep only places that expose a website URL in the MapKit payload. Useful for outbound lists that need a domain before enrichment. Defaults to false so phone-only listings remain. Filtered-out places are not billed.

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

When true, keep only places that expose a public telephone number. Useful for call-center or SMS workflows. Defaults to false. Filtered-out places are not billed as place-scraped.

## `adaptiveGrid` (type: `boolean`):

When true and a map center is known, expand search across nearby grid cells to improve coverage for dense categories. Defaults to true. Disable for a single-point probe or when searchUrls already encode a tight span. Extra cells stop once maxResults is reached.

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

Locale for MapKit requests, such as en-US. Affects category and address language when Apple returns localized fields. Defaults to en-US. Use a BCP-47 style tag Apple Maps accepts for your market.

## `countryCode` (type: `string`):

ISO country hint for MapKit search, such as US. Helps disambiguate city names that exist in multiple countries. Defaults to US. Does not replace location or coordinates when those are set.

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

Optional Apify proxy settings for MapKit and website enrichment HTTP. Leave off for the default direct path. Enable Apify Residential if Apple or business sites return 403/429. This is not an Apple ID session and does not unlock private Maps data.

## Actor input object example

```json
{
  "searchQuery": "dentists",
  "searchQueries": [
    "cafes",
    "bakeries"
  ],
  "location": "Austin, TX",
  "latitude": 37.7749,
  "longitude": -122.4194,
  "placeUrls": [
    "https://maps.apple.com/place?place-id=I123"
  ],
  "searchUrls": [
    "https://maps.apple.com/search?query=coffee&center=37.77,-122.42"
  ],
  "maxResults": 5,
  "enrichEmails": false,
  "requireWebsite": false,
  "requirePhone": false,
  "adaptiveGrid": true,
  "language": "en-US",
  "countryCode": "US"
}
```

# Actor output Schema

## `results` (type: `string`):

One row per Apple Maps place: NAP fields, coordinates, optional email, and scrapedAt.

## `output` (type: `string`):

Final outcome, itemsPushed, placesFound, emailsEnriched, and chargedEventCounts.

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

Diagnostic RUN\_SUMMARY with filters, grid cells, warnings, and billing counters.

# 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 = {
    "searchQuery": "coffee shops",
    "location": "San Francisco, CA",
    "maxResults": 5,
    "enrichEmails": false,
    "adaptiveGrid": true,
    "language": "en-US",
    "countryCode": "US"
};

// Run the Actor and wait for it to finish
const run = await client.actor("khadinakbar/apple-maps-business-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 = {
    "searchQuery": "coffee shops",
    "location": "San Francisco, CA",
    "maxResults": 5,
    "enrichEmails": False,
    "adaptiveGrid": True,
    "language": "en-US",
    "countryCode": "US",
}

# Run the Actor and wait for it to finish
run = client.actor("khadinakbar/apple-maps-business-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 '{
  "searchQuery": "coffee shops",
  "location": "San Francisco, CA",
  "maxResults": 5,
  "enrichEmails": false,
  "adaptiveGrid": true,
  "language": "en-US",
  "countryCode": "US"
}' |
apify call khadinakbar/apple-maps-business-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,khadinakbar/apple-maps-business-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/cvmZbYW6v9NUYbzsz/builds/4SlhWkPcxf66X0MsB/openapi.json
