# Apple Maps Scraper - Places, Phones, Hours & Ratings (`dami_studio/apple-maps-scraper`) Actor

Apple Maps answers any one search with 25 places and stops. This tiles the area into a grid, searches each tile and drops duplicates: one run returned 250 unique San Francisco coffee shops. Name, address, coordinates, phone, website, hours, rating. $0.40 per 1,000 places.

- **URL**: https://apify.com/dami\_studio/apple-maps-scraper.md
- **Developed by:** [Dami's Studio](https://apify.com/dami_studio) (community)
- **Categories:** Lead generation, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.40 / 1,000 place returneds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Apple Maps Scraper API

Apple Maps answers any one search with 25 places and then stops. This gets past that by splitting
the area into a grid and searching each tile, then dropping the duplicates: a real run for
`coffee shop` in San Francisco came back with 250 unique places.

Each one carries name, full address, coordinates, category, phone number, website, opening hours,
star rating, review count, price level, amenities and photos.

Give it a search term and a city, or paste Apple Maps links. It reads the same endpoints the Apple
Maps web app reads, so what you get is Apple's own place data. You don't need an Apple Developer
account or a key.

### What you get

One row per place. A real run for `coffee shop` in San Francisco returned rows like this:

```json
{
  "placeId": "IB7DAEFD6CC4ED79F",
  "name": "Saint Frank Coffee",
  "category": "Cafe",
  "categories": ["Dining", "Coffee Shop", "Cafe"],
  "categoryId": "dining.cafe.coffee_shop",
  "address": "2340 Polk St, San Francisco, CA  94109, United States",
  "street": "2340 Polk St",
  "city": "San Francisco",
  "stateCode": "CA",
  "postalCode": "94109",
  "countryCode": "US",
  "latitude": 37.7981225,
  "longitude": -122.4659999,
  "timezone": "America/Los_Angeles",
  "phone": "+14158818062",
  "phoneFormatted": "+1 (415) 881-8062",
  "website": "https://www.saintfrankcoffee.com",
  "rating": 3.9,
  "reviewCount": 88,
  "priceRange": "$$",
  "openingHoursText": "Sun: 07:00-18:00; Mon: 07:00-18:00; Tue: 07:00-18:00; Wed: 07:00-18:00; Thu: 07:00-18:00; Fri: 07:00-18:00; Sat: 07:00-18:00",
  "amenities": ["Accepts Apple Pay", "Accepts Contactless Payments", "Wheelchair Accessible"],
  "appleMapsUrl": "https://maps.apple.com/place?place-id=IB7DAEFD6CC4ED79F&_provider=9902"
}
```

Full field list: `placeId`, `muid`, `name`, `category`, `categories`, `categoryId`, `placeType`,
`address`, `addressLines`, `street`, `subLocality`, `city`, `state`, `stateCode`, `postalCode`,
`country`, `countryCode`, `latitude`, `longitude`, `timezone`, `phone`, `phoneFormatted`,
`additionalPhones`, `website`, `rating`, `ratingMax`, `reviewCount`, `priceLevel`, `priceRange`,
`openingHours`, `openingHoursText`, `hoursType`, `amenities`, `photos`, `reviews`, `description`,
`editorialTitle`, `editorialText`, `isClaimable`, `quickLinks`, `appleMapsUrl`, `searchQuery`,
`position`, `scrapedAt`.

#### How complete is each field?

Measured on a real run: 250 coffee shops in San Francisco, 250 unique places, no duplicates.

| Field | Filled |
|---|---|
| name, address, city, postcode, country code, coordinates | 100% |
| category, categories, timezone, Apple Maps URL | 100% |
| opening hours | 98% |
| photos | 98% |
| website | 95% |
| amenities | 94% |
| phone | 89% |
| star rating + review count | 87% |
| review snippets | 86% |
| price level | 56% |

Ratings, reviews and price levels come from Apple's partners, Yelp among them. Apple doesn't hold
them for every business, so those columns are genuinely blank sometimes rather than missed by the
scraper. A later 200-row sample in a different city came back with ratings on 82% and photos on 81%,
so expect those to move with what you search. The structural fields (name, address, coordinates,
hours, category, contact) are there for essentially every business in both runs.

### Input

```json
{
  "searchQueries": ["coffee shop", "bakery"],
  "location": "San Francisco, CA",
  "maxItems": 200,
  "language": "en-US",
  "countryCode": "US"
}
```

- **searchQueries** — one or more terms. Each is searched across the whole area.
- **location** — a city, neighbourhood or region name. Resolved through Apple Maps itself, so
  "Austin, TX", "Shoreditch, London" and "Bayern" all work.
- **latitude / longitude / searchRadiusKm** — search a point instead of a named place.
- **startUrls** — paste Apple Maps links. Place links
  (`https://maps.apple.com/place?place-id=...`) are looked up individually; search links
  (`https://maps.apple.com/search?query=...&center=lat,lng`) are re-run as searches.
- **maxItems** — hard stop. You are charged per place returned, so this is your budget cap.
- **language** — names, categories and descriptions come back in this language.
- **countryCode** — biases the search, e.g. `GB`, `DE`, `JP`.

### How it gets more than 25 results

Apple caps any single Maps search at 25 places. To go past that the actor splits the area into a
grid and searches each tile, then removes duplicates. A 2x2 grid over San Francisco produced 96
unique coffee shops from 4 requests; the grid keeps widening (3x3, 4x4, 6x6 …) until it hits your
`maxItems` or a whole grid level stops finding anything new. That means the cost of a big run scales
with places found, not with wasted requests.

In practice: 250 unique San Francisco coffee shops took 63 seconds and no proxy at all.

### What this does not do

- **No full review scraping.** You get the star rating, the total review count and up to three
  review snippets that Apple shows on the place card. It isn't a review-export tool.
- **No email addresses.** Apple Maps does not carry them. Nothing here invents one.
- **No driving directions, transit schedules or traffic.**
- **No Look Around imagery or satellite tiles.**
- **Ratings are partner data.** Where Apple has no partner rating, `rating` and `reviewCount` are
  `null`. That's Apple's coverage, not a scraping failure.
- **Very large areas need a big `maxItems`.** Covering an entire country at street level means a lot
  of tiles. Run it city by city.

### Billing

$0.40 per 1,000 places returned, plus a $0.001 start fee per run. One charge per place, and
duplicates removed by the grid search are dropped before anything is billed.

Every row you pay for is a real place. If a location cannot be resolved, a search returns nothing,
or Apple refuses a request, the actor writes a labelled `_diagnostic` row explaining what happened
and charges nothing for it. Running with no input at all writes one labelled `_sample` row, also
uncharged.

### FAQ

**Is there an official Apple Maps API for business listings?**
Apple's MapKit JS and the Apple Maps Server API need a paid Apple Developer account, a signed JWT,
and their terms restrict what you may store. This actor reads the public Apple Maps website
endpoints instead, and hands you plain JSON or CSV with no key to manage.

**Where does the data come from?**
`maps.apple.com`, Apple's own Maps web app. Business ratings, review snippets and some amenity
flags inside that data are supplied to Apple by partners such as Yelp, and are attributed as such in
Apple Maps itself.

**How do I scrape Apple Maps for a whole city?**
Set `location` to the city and `maxItems` to how many places you want. The grid search widens
automatically. For a dense category in a big city, expect a few hundred to a few thousand places.

**Can I get results for a specific business?**
Yes. Open the business in Apple Maps, copy the share link, and paste it into `startUrls`.

**How many places do I get per search term?**
A single Apple search returns 25. The tiling grid goes well past that: 250 unique coffee shops came
back for San Francisco. Where it stops depends on how many places Apple actually holds for that
category in that area, so a dense category in a big city gives you a few hundred to a few thousand
and a narrow one gives you far fewer. Duplicates across tiles are removed before anything is
charged.

**Does it work outside the United States?**
Yes. Set `countryCode` and `language` to match, for example `countryCode: "DE"` and
`language: "de-DE"`.

**What are `placeId` and `muid`?**
`placeId` is the id Apple puts in shareable place links, so `appleMapsUrl` is a link you can open.
`muid` is Apple's internal numeric id for the same place. Both are stable and both can be fed back
into this actor.

**Can I use it for lead generation?**
Yes. Name, address, phone, website, category and opening hours are the columns people build prospect
lists from. There are no email addresses; Apple does not publish them.

**How much does it cost?**
$0.40 per 1,000 places, plus a $0.001 start fee per run. Diagnostic and sample rows are free.

### Output formats

JSON, CSV, Excel, XML and RSS from the Apify dataset, or through the API. Nested fields
(`openingHours`, `amenities`, `photos`, `reviews`, `quickLinks`) flatten into columns on CSV export.

# Actor input Schema

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

What to look for on Apple Maps, for example "coffee shop", "dentist", "hardware store". Each term is searched across the whole area you set below.

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

City, neighbourhood or region to search in, for example "San Francisco, CA" or "Manchester, United Kingdom". Ignored when you set latitude and longitude.

## `latitude` (type: `string`):

Optional. Search around an exact point instead of a named location.

## `longitude` (type: `string`):

Optional. Search around an exact point instead of a named location.

## `searchRadiusKm` (type: `integer`):

Only used with latitude/longitude. Half-width of the square area to cover.

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

Paste Apple Maps links directly. Place links (https://maps.apple.com/place?place-id=...) are looked up one by one; search links (https://maps.apple.com/search?query=...\&center=lat,lng) are run as searches.

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

Stop after this many places. You are charged per place returned, so this is also your budget cap.

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

Language for names, categories and descriptions, as a BCP-47 tag.

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

Two-letter country the search should be biased towards, for example US, GB, DE. Defaults to US.

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

Optional. Apple Maps answers the plain Apify container address, so a proxy is normally unnecessary and only adds cost.

## Actor input object example

```json
{
  "searchQueries": [
    "coffee shop"
  ],
  "location": "San Francisco, CA",
  "searchRadiusKm": 5,
  "maxItems": 50,
  "language": "en-US",
  "countryCode": "US",
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

One row per place in the default dataset: name, address, coordinates, category, phone, website, opening hours, star rating and review count. Empty or blocked runs return a single uncharged row instead.

# 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 = {
    "searchQueries": [
        "coffee shop"
    ],
    "location": "San Francisco, CA"
};

// Run the Actor and wait for it to finish
const run = await client.actor("dami_studio/apple-maps-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 = {
    "searchQueries": ["coffee shop"],
    "location": "San Francisco, CA",
}

# Run the Actor and wait for it to finish
run = client.actor("dami_studio/apple-maps-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 '{
  "searchQueries": [
    "coffee shop"
  ],
  "location": "San Francisco, CA"
}' |
apify call dami_studio/apple-maps-scraper --silent --output-dataset

```

## MCP server setup

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