# Bing Maps Scraper — Business Data & Leads (`b2b_leads/bing-maps-real-time-data`) Actor

Turn Bing Maps into a clean local-business dataset. Search keywords across cities, map whole markets by category, enrich with addresses, opening hours, emails & socials, and stream results in real time to your dataset or webhook. 10,000+ businesses per run — ready for CRM, Sheets & AI.

- **URL**: https://apify.com/b2b\_leads/bing-maps-real-time-data.md
- **Developed by:** [Emmanuel](https://apify.com/b2b_leads) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 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.

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

## Bing Maps Real-Time Data

**Turn Bing Maps into a clean, structured local-business dataset.** Search many keywords and cities in one run, map a whole local market by category, enrich every business with address breakdown and opening hours, find contact emails, resolve the specific business links you already have, and stream it all to your dataset — and to your own systems — in real time.

Built for lead-generation agencies, sales teams, franchise and market researchers, data engineers, and AI workflows that need **dependable local business data** at scale, without slow or brittle tooling.

> **The downloaded dataset is JSON/CSV/Excel — no code required to get value from it.**
>
> **⚠️ This is a paid Actor.** Apify accounts on the **free plan receive a free sample of 2 businesses** and a clear reminder to upgrade. Paying accounts get the full, uncapped dataset. See [Plans & free tier](#plans--free-tier).

***

### Why teams choose this Actor

| | Bing Maps Real-Time Data | Typical alternatives |
|---|---|---|
| **Speed** | First rows within seconds of starting | Often 5–15 s per business |
| **Memory** | **512 MB** default | 2–4 GB+ |
| **Reliability** | Transient failures are retried automatically, and one bad business never breaks the run | One failure can abort the job |
| **Setup** | Organised input UI with sensible defaults — run it immediately | Frequently fragile and high-maintenance |
| **Cost** | Low compute, efficient proxy use, hard `maxItems` budget cap | Unpredictable |
| **Output** | Flat, normalised rows — ready for Sheets, CRM, or an LLM | Raw pages to post-process |
| **Scale** | **10,000+ businesses** per run, many markets at once | Usually capped much lower |
| **Source** | Bing Maps: strong SMB coverage and ratings aggregated from Yelp/Tripadvisor | Single-provider listings |

***

### Quick start

1. **Add your searches.** In *Search terms & locations*, type a keyword and a city — for example `coffee shops` in `Seattle, WA`. Add more rows for more markets.
2. **Click Start.** Rows stream into the run's dataset as they are found.
3. **Download.** Open the **Output** tab → *All results*, and export as JSON, CSV, or Excel. Or read the dataset over the [Apify API](https://docs.apify.com/api/v2).

Minimal input that returns a rich dataset:

```json
{
  "searchTasks": [
    { "query": "coffee shops", "location": "Seattle, WA", "placesPerQuery": 50 },
    { "query": "bakeries", "location": "Portland, OR", "placesPerQuery": 50 }
  ],
  "fetchEmails": true,
  "maxItems": 200
}
```

***

### What you get — 30+ data points per business

Every row is **flat and normalised** — no nested surprises — and tagged with `featureType` and `scrapedAt` so you can filter, join, and pipe it anywhere.

| Group | Fields |
|-------|--------|
| **Identity** | `title`, `category`, `placeId`, `ypid`, `bingUrl` |
| **Location** | `address`, `addressLine`, `city`, `state`, `postalCode`, `country`, `latitude`, `longitude` |
| **Contact** | `phone`, `website`, `emails[]`, `socials[]` |
| **Reputation** | `rating`, `reviewCount`, `ratingSource` |
| **Operations** | `openStatus`, `permanentlyClosed`, `openingHours` |
| **Media** | `imageUrl` |
| **Traceability** | `featureType`, `searchQuery`, `searchLocation`, `searchTaskIndex`, `searchTaskLabel`, `position`, `sourceUrl`, `linkMatch`, `scrapedAt` |

#### Example record

```json
{
  "featureType": "search",
  "scrapedAt": "2026-09-22T01:05:19.015Z",
  "title": "Storyville Coffee Pike Place",
  "category": "Cafe",
  "address": "94 Pike St Suite 34, Seattle, WA 98101",
  "addressLine": "94 Pike St Suite 34",
  "city": "Seattle",
  "state": "WA",
  "postalCode": "98101",
  "country": "US",
  "phone": "(206) 780-5777",
  "website": "https://storyville.com/pages/pike-place-market",
  "emails": ["info@storyville.com"],
  "socials": [
    "https://x.com/storyville",
    "https://www.facebook.com/StoryvilleCoffee",
    "https://www.instagram.com/storyville"
  ],
  "rating": 4.5,
  "reviewCount": 2616,
  "ratingSource": "Yelp",
  "openStatus": "Closed",
  "permanentlyClosed": false,
  "openingHours": {
    "Monday": "06:59-17:00",
    "Friday": "06:59-18:00",
    "Saturday": "06:59-18:00",
    "Sunday": "06:59-18:00"
  },
  "latitude": 47.60890579223633,
  "longitude": -122.3405532836914,
  "imageUrl": "https://www.bing.com/th?id=...",
  "placeId": "ypid:YN6230075ECE10B067",
  "ypid": "YN6230075ECE10B067",
  "bingUrl": "https://www.bing.com/maps?q=Storyville%20Coffee%20Pike%20Place&filters=ypid%3A%22YN6230075ECE10B067%22",
  "searchQuery": "coffee shops",
  "searchLocation": "Seattle, WA",
  "searchTaskIndex": 0,
  "searchTaskLabel": "coffee shops | Seattle, WA",
  "position": 1,
  "sourceUrl": null,
  "linkMatch": null
}
```

Every row carries exactly these fields — nothing extra, and no placeholder keys.

***

### Features

#### 🔎 Keyword search — multi-market in one run

Run **many keyword + location pairs at once** — *coffee shops in Seattle* next to *plumbers in Austin* next to *dentists in Miami*. Give each task its own result limit, and Bing's own listing data fills in name, category, address, phone, website, star rating, review count, rating source, coordinates, photo, open/closed status, and a direct Bing Maps link.

Use it for long-tail local lists: niche × city matrices, brand and competitor names, service categories, "everything in this metro" sweeps.

#### 🎯 Precise area targeting — neighbourhoods, postal codes and exact circles

A city name is often too blunt: "dentists in Austin, TX" pulls listings from across the whole metro. Two levels of precision are available, and they combine well.

**1. Target the location text (no setup).** The location can be a city, a **neighbourhood**, or a **postal code**. Postcodes are the sharpest text option because they concentrate results in that area:

| Location used | Plumbers returned | Spread around downtown Austin |
|---|---|---|
| `Austin, TX` | 17 | 3.8 km – 25 km (metro-wide) |
| `downtown Austin` | 9 | 3.8 km – 11.5 km |
| `78701` (ZIP) | 5 | 0.8 km – 6.6 km |

So for a tighter list, `"plumbers"` + `"78701"` beats `"plumbers"` + `"Austin, TX"` — no coordinates needed at all.

**2. Draw an exact circle (optional).** Add `latitude`, `longitude` and `radiusMeters` to a search task and only businesses inside that circle are exported. Every kept row carries `distanceMeters`, so you can sort by proximity or verify the filter:

```json
{
  "searchTasks": [
    {
      "query": "dentists",
      "location": "Austin, TX",
      "latitude": 30.2672,
      "longitude": -97.7431,
      "radiusMeters": 5000,
      "placesPerQuery": 60
    }
  ]
}
```

```
[INFO] Task 1/1: "dentists | Austin, TX" (within 5 km)
[INFO] Task "dentists | Austin, TX": 12/60 business(es) within 5 km
[INFO] 34 business(es) were outside the requested radius (or had no coordinates) and were excluded.
```

Notes to get good results:

- **Supply both coordinates.** `radiusMeters` without `latitude`/`longitude` fails immediately with a clear message — we never guess a centre from a place name, so the circle you ask for is the circle you get. Coordinates are easy to copy from Bing Maps or any map tool.
- **Coordinates alone don't scope the search** — the location text does. That is why a radius task also needs a location (use a postcode or neighbourhood for the tightest results).
- **A radius narrows, it doesn't expand.** It can only filter the businesses the source returns for that query, so pair a small radius with a tight location and a generous `placesPerQuery` to give it enough to work with.
- Businesses with no coordinates are excluded when a radius is active (they cannot be verified as inside it), and the count is reported in the log and run summary.

#### 📡 Nearby discovery

Don't want to write keywords? Pick from **18 business categories** (restaurants, hotels, cafes, gyms, dentists, doctors, lawyers, real estate, auto repair, and more) plus a city or area, and the Actor runs one search per category. Results are tagged `featureType: "nearby"`.

Ideal for mapping a market, territory planning, franchise gap analysis, and competitor density studies.

#### 🔗 Scrape by URL — enrich the businesses you already have

Already know which businesses you want? Paste their **Bing Maps links** (the `bingUrl` value from a previous run, or the address bar of a business page) and get the **same field-rich record** for each one — no keyword needed.

- Every link becomes one row tagged `featureType: "scrape_by_url"`, keeping the link in `sourceUrl`.
- `linkMatch` tells you how precisely each link was matched:
  - **`business_id`** — the business matches the identity carried by the link. Fully verified.
  - **`business_name`** — resolved by name only: the link carried no identity, or the exact location could not be confirmed (common with multi-location brands, where Bing may return another same-name branch). Check `address` if you need a specific branch.
- Lead filters are deliberately **not** applied to links — every business you list is exported.
- A link that cannot be resolved is reported in the log and in the run summary's `errors` — never silently turned into a junk row.

#### 🏢 Address breakdown & opening hours

With `fetchPlaceDetails: true` (default), each business also gets a structured address (`addressLine`, `city`, `state`, `postalCode`, `country`) and a weekly `openingHours` schedule such as `{ "Monday": "08:00-18:00" }` — ready for routing, CRM enrichment, and "is this place open?" logic.

Enrichment is best-effort: a business that cannot be enriched is still exported, never dropped.

#### 📧 Email & social discovery

Turn listings into **contactable leads**. With `fetchEmails: true` each business website is mined for the contact details it publishes, and you get:

- **`emails[]`** — the reachable addresses behind the business (for example `info@`, `sales@`, or an owner's address), de-duplicated and cleaned.
- **`socials[]`** — Facebook, Instagram, X/Twitter, LinkedIn, YouTube, TikTok, Pinterest, and more.

Placeholder and junk addresses (`noreply@`, analytics and CDN domains, template defaults) are filtered out automatically.

#### 🎯 Lead & quality filters

Shape the list exactly how you sell it:

- **Minimum rating** — skip low-reputation businesses
- **Require phone / require website** — only contactable businesses
- **Category contains** — one niche at a time (`dentist`, `hotel`, `plumb`)
- **Exclude closed** — drop permanently/temporarily closed listings (on by default)

Combined with email discovery, `requirePhone: true` + `minRating: 4` + `fetchEmails: true` produces an outreach-ready list in a single run.

#### ⚡ Real-time delivery and webhooks

- **Streaming dataset** — rows appear as they are collected, so you can read while the run is still working.
- **Webhooks** — set `webhookUrl` and every record is also **POSTed immediately** to your endpoint (JSON or Slack-formatted): CRM, Zapier, Make, Google Sheets, a Slack channel, or your own pipeline. See [Webhooks](#webhooks).
- **Apify integrations & schedules** — trigger runs on a schedule and connect the dataset to Sheets, Airtable, HubSpot, and the rest of the Apify ecosystem.

***

### Use cases

**Sales & lead generation**

- Build outreach lists with phone, email, and website for niches by city
- Refresh an old lead list with current addresses, hours, and contact details
- Find `permanentlyClosed` businesses to clean up a CRM
- Trigger a CRM push per new lead with webhooks
- **Proximity campaigns** — everyone within a few kilometres of a store or clinic, sorted by `distanceMeters`

**Market & location research**

- Count businesses per category per city for saturation and demand studies
- Compare average ratings and review counts between regions and competitors
- Map every dentist, gym, or cafe in a metro area with nearby discovery
- Drill into one neighbourhood or ZIP code instead of an entire metro
- Franchise expansion: find cities that are underserved in your category

**Data & engineering**

- Backfill a warehouse with local business dimensions (stable `placeId` for joins and diffs)
- Enrich an existing account list from the links you already store (scrape by URL)
- Feed clean JSON into RAG, scoring models, or territory-planning tools
- Schedule weekly snapshots and diff new vs. closed locations

**AI & agents**

- Give an LLM structured local business records for outreach drafts and summaries
- Run the Actor from an AI assistant through Apify MCP and read results in chat
- Score and segment leads with your own model on top of the dataset

***

### Plans & free tier

This is a **paid Actor**.

| Your Apify plan | What happens |
|---|---|
| **Free** | You get a **free sample of 2 businesses** plus a clear reminder to upgrade. Everything works exactly as documented — you just receive the sample instead of the full dataset, and nothing extra is charged. |
| **Any paid plan** (Bronze, Silver, Gold, Platinum, Diamond) | Full output — every feature, with no plan-level caps beyond the `maxItems` you set. |

The log always tells you exactly where you stand:

```
[INFO] Paying user — full output, tier: BRONZE.
```

```
[WARN] Free plan detected, tier: FREE — a free sample of 2 business(es) was exported.
       Upgrade to a paid Apify plan for the full dataset.
```

A limited free run is **not an error**: it exits successfully, the sample is delivered like any other run, and the decision is recorded in the run summary (see [Run summary](#run-summary-output)).

**Owner option.** The sample size and behaviour are tunable from Apify Console → Actor → **Source → Environment variables**, with no code change:

| Variable | Default | Effect |
|---|---|---|
| `FREE_TIER_MODE` | `limit` | `limit` = hand out the sample below. `block` = export nothing for free accounts. |
| `FREE_TIER_MAX_ITEMS` | `2` | Total businesses in a free run. |
| `FREE_TIER_MAX_USERS_PER_ACCOUNT` | `2` | Businesses per search task in a free run. |

***

### Input reference

Every input has a sensible default — the pre-filled example runs as-is. Enable only what you need.

| Input | Type | Default | Description |
|-------|------|---------|-------------|
| **Keyword search** | | | |
| `enableSearch` | boolean | `true` | Turn keyword search on/off |
| `searchTasks` | object\[] | 2 examples | Primary input: `{ query, location, placesPerQuery?, latitude?, longitude?, radiusMeters? }` per row |
| `searchTasks[].location` | string | — | City, **neighbourhood**, or **postal code** — postcodes are the most precise text option |
| `searchTasks[].latitude` / `longitude` | number | — | Optional exact centre. Enables `distanceMeters` on rows, and is required for a radius |
| `searchTasks[].radiusMeters` | integer | — | Keep only businesses within this many metres of that centre |
| `placesPerQuery` | integer | `20` | Default max businesses per task |
| `maxPagesPerQuery` | integer | `50` | Safety cap on depth per task — lower it for quick tests |
| **Nearby discovery** | | | |
| `enableNearby` | boolean | `false` | Discover businesses by category around one location |
| `nearbyCategories` | enum\[] | 5 defaults | 18 categories: restaurants, hotels, cafes, gyms, dentists, doctors, lawyers, real estate, … |
| `nearbyLocation` | string | — | City or area for nearby discovery |
| `placesPerCategory` | integer | `20` | Max businesses per category |
| **Scrape by URL** | | | |
| `enableScrapeByUrl` | boolean | `false` | Resolve specific business links into full records (turns on automatically when links are pasted) |
| `scrapeUrls` | string\[] | `[]` | One Bing Maps business link per row |
| **Filters** | | | |
| `minRating` | number | — | Minimum star rating (0–5) |
| `requirePhone` | boolean | `false` | Only businesses that list a phone |
| `requireWebsite` | boolean | `false` | Only businesses that list a website |
| `categoryFilter` | string | — | Keep categories containing this text |
| `excludeClosed` | boolean | `true` | Skip permanently/temporarily closed businesses |
| **Enrichment** | | | |
| `fetchPlaceDetails` | boolean | `true` | Structured address + weekly opening hours |
| `fetchEmails` | boolean | `false` | `emails[]` + `socials[]` from the business website |
| **Output & limits** | | | |
| `language` | string | `en` | Language for names and addresses |
| `country` | string | `us` | Country code for localised results and proxy routing |
| `maxItems` | integer | `10000` | Hard cap on dataset rows for the run |
| `webhookUrl` | string | — | Additional real-time POST destination (the dataset is always written) |
| `webhookFormat` | enum | `json` | `json` (full record) or `slack` (message payload) |
| **Connection** | | | |
| `proxyConfiguration` | object | Apify residential, US | Change country or supply your own proxy URLs |

Advanced (API only): `maxConcurrency`, `requestDelayMs`.

#### Input examples

**High-intent B2B list with emails**

```json
{
  "searchTasks": [
    { "query": "dentists", "location": "Austin, TX", "placesPerQuery": 60 },
    { "query": "dentists", "location": "Dallas, TX", "placesPerQuery": 60 }
  ],
  "minRating": 4,
  "requirePhone": true,
  "fetchEmails": true,
  "maxItems": 200
}
```

**Map a whole market by category**

```json
{
  "enableSearch": false,
  "enableNearby": true,
  "nearbyCategories": ["cafes", "gyms", "beauty_salons"],
  "nearbyLocation": "Denver, CO",
  "placesPerCategory": 40,
  "fetchPlaceDetails": true
}
```

**Enrich businesses you already have links for**

```json
{
  "enableSearch": false,
  "scrapeUrls": [
    "https://www.bing.com/maps?q=Storyville%20Coffee%20Pike%20Place&filters=ypid%3A%22YN6230075ECE10B067%22",
    "https://www.bing.com/maps?q=Ghost%20Note%20Coffee&filters=ypid%3A%22YN73759BB6B824CAC4%22"
  ],
  "fetchEmails": true
}
```

**Push every new lead into your CRM as it is found**

```json
{
  "searchTasks": [{ "query": "roofing companies", "location": "Phoenix, AZ" }],
  "fetchEmails": true,
  "webhookUrl": "https://your-crm.example.com/leads/incoming?token=YOUR_TOKEN",
  "webhookFormat": "json"
}
```

**Alert a Slack channel**

```json
{
  "searchTasks": [{ "query": "new restaurants", "location": "Miami, FL" }],
  "webhookUrl": "https://hooks.slack.com/services/YOUR/WEBHOOK/URL",
  "webhookFormat": "slack"
}
```

***

### Output reference

Every dataset row is one business.

| Field | Type | Description |
|-------|------|-------------|
| `featureType` | `"search"` | `"nearby"` | `"scrape_by_url"` | Which feature produced the row |
| `title` | string | Business name |
| `category` | string | Primary category |
| `address` | string | Full address as listed |
| `addressLine`, `city`, `state`, `postalCode`, `country` | string | null | Structured address (with `fetchPlaceDetails`) |
| `phone` | string | null | Phone number when listed |
| `website` | string | null | Website URL when listed |
| `emails` | string\[] | Contact emails (with `fetchEmails`) |
| `socials` | string\[] | Social profile URLs (with `fetchEmails`) |
| `rating` | number | null | Star rating (0–5) |
| `reviewCount` | integer | null | Number of reviews |
| `ratingSource` | string | null | Rating provider when Bing attributes it (e.g. Yelp, Tripadvisor) |
| `openStatus` | string | null | e.g. `Open`, `Closed`, `Permanently closed` |
| `permanentlyClosed` | boolean | Convenience flag for closed listings |
| `openingHours` | object | null | Weekly schedule, e.g. `{ "Monday": "08:00-18:00" }` |
| `latitude`, `longitude` | number | null | Coordinates |
| `imageUrl` | string | null | Listing photo |
| `placeId` | string | Stable business identifier — the dedupe and join key |
| `ypid` | string | Local business identifier |
| `bingUrl` | string | Direct link to the business on Bing Maps |
| `searchQuery` | string | Keyword or category that produced the row |
| `searchLocation` | string | Location used for the row |
| `searchTaskIndex` | integer | 0-based task index (for `scrape_by_url`, the index in `scrapeUrls`) |
| `searchTaskLabel` | string | e.g. `"dentists | Austin, TX"`; for URL rows, the link |
| `position` | integer | null | 1-based rank within the task |
| `distanceMeters` | integer | null | Distance from the task's centre when `latitude`/`longitude` are supplied — use it to sort by proximity |
| `sourceUrl` | string | null | The link a `scrape_by_url` row came from |
| `linkMatch` | `"business_id"` | `"business_name"` | null | How a supplied link was matched (null for search/nearby rows) |
| `scrapedAt` | string | ISO timestamp |

**Deduplication:** results are deduplicated by `placeId` across the whole run — search, nearby, and links combined. The same business is never exported twice.

#### Run summary (OUTPUT)

Each run also writes a summary to the key-value store, shown in the run's **Output** tab:

```json
{
  "features": ["search", "scrape_by_url"],
  "searchTasks": 1,
  "nearbyTasks": 0,
  "scrapeUrls": 5,
  "recordCount": 42,
  "scrapeUrlsResolved": 5,
  "filteredOutCount": 3,
  "duplicateCount": 6,
  "detailsFetched": 41,
  "detailsFailed": 1,
  "emailsFound": 27,
  "pagesFetched": 4,
  "webhooksDelivered": 42,
  "webhooksFailed": 0,
  "errors": [],
  "spendingLimitReached": false,
  "paywall": {
    "detected": true,
    "isPaying": true,
    "pricingTier": "BRONZE",
    "mode": "limit",
    "limited": false,
    "blocked": false,
    "freeTierMaxItems": null,
    "freeTierMaxPlacesPerTask": null
  },
  "startedAt": "2026-09-22T01:05:06.771Z",
  "finishedAt": "2026-09-22T01:07:09.006Z",
  "durationMs": 122235
}
```

The `paywall` object documents the plan decision transparently: `detected` (whether Apify plan signals were present), `isPaying`, `pricingTier`, `mode` (`block`/`limit`), `blocked`, and `limited`. `errors[]` lists anything that could not be collected (a bad link, an unreachable page) without failing the run.

***

### Webhooks

Records are **always** written to the run's dataset first. Setting `webhookUrl` adds a **second, real-time delivery channel**: each record is POSTed to your endpoint the moment it is collected — ideal for pushing leads into a CRM or database while the run is still working.

| Setting | Value |
|---|---|
| `webhookUrl` | Your HTTPS endpoint. Leave empty for dataset-only delivery. |
| `webhookFormat` | `json` — the full record object. `slack` — a Slack-ready message payload. |

**JSON payload** (abridged):

```json
{
  "featureType": "search",
  "title": "Sport Clips Haircuts of Downtown Boise",
  "category": "Hair salon",
  "phone": "(208) 345-1823",
  "website": "https://sportclips.com/us-id-boise-id106",
  "emails": ["info@example.com"],
  "rating": 3.5,
  "reviewCount": 118,
  "address": "1749 W State St, Boise, ID 83702",
  "bingUrl": "https://www.bing.com/maps?q=...",
  "scrapedAt": "2026-09-22T01:28:35.167Z"
}
```

**Slack payload** (`webhookFormat: "slack"`) — POST this to a Slack incoming webhook and every lead appears in your channel:

```json
{
  "text": ":round_pushpin: *Sport Clips Haircuts of Downtown Boise*\n*Category:* Hair salon  •  *Rating:* 3.5\n*Phone:* (208) 345-1823\n*Website:* https://sportclips.com/us-id-boise-id106\n1749 W State St, Boise, ID 83702\n<https://www.bing.com/maps?q=...|View on Bing Maps>"
}
```

#### Delivery semantics

- **Best-effort and non-blocking.** A failed webhook never stops the run, never delays the dataset, and never loses a record.
- **Retried once** on transport errors before the record is counted as failed.
- **Delivery is reported.** The run summary carries `webhooksDelivered` and `webhooksFailed`, and the log prints a single line, e.g. `Webhook: 42 record(s) delivered in real time.` or `Webhook: 40 delivered, 2 failed — every record is still saved in this run's dataset.`
- **Authenticating your endpoint.** There is no separate header field: put a token in the URL (`https://your-api.com/leads?token=SECRET`) — it is sent with every POST.
- **Ordering.** Records are delivered in collection order, but treat delivery as at-least-once: deduplicate on `placeId` in your receiver.
- **Free-plan runs deliver only their capped sample** (2 businesses by default, or nothing when the owner sets `FREE_TIER_MODE=block`).

Webhooks are optional. If you would rather pull data after the run, use the dataset via the Apify API or an Apify integration instead.

***

### API usage

#### Run via the Apify API

```bash
curl -X POST "https://api.apify.com/v2/acts/YOUR_ACTOR_ID/runs?token=YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "searchTasks": [
      { "query": "coffee shops", "location": "Seattle, WA", "placesPerQuery": 50 },
      { "query": "bakeries", "location": "Portland, OR", "placesPerQuery": 50 }
    ],
    "minRating": 4,
    "requirePhone": true,
    "fetchEmails": true,
    "maxItems": 500
  }'
```

#### Read the results

```bash
## JSON
curl "https://api.apify.com/v2/datasets/DATASET_ID/items?token=YOUR_API_TOKEN&format=json" -o results.json

## CSV
curl "https://api.apify.com/v2/datasets/DATASET_ID/items?token=YOUR_API_TOKEN&format=csv" -o results.csv
```

#### JavaScript

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

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

const run = await client.actor('YOUR_ACTOR_ID').call({
    searchTasks: [{ query: 'plumbers', location: 'Austin, TX', placesPerQuery: 50 }],
    requirePhone: true,
    fetchEmails: true,
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.length, items[0]);
```

#### Python

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_API_TOKEN")

run = client.actor("YOUR_ACTOR_ID").call(run_input={
    "searchTasks": [{"query": "lawyers", "location": "Chicago, IL", "placesPerQuery": 50}],
    "minRating": 4,
    "fetchEmails": True,
})

for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["title"], item.get("phone"), item.get("emails"))
```

***

### LLM & MCP integration

The output is **JSON-Lines-friendly structured data** — ideal for ChatGPT, Claude, Gemini, LangChain, LlamaIndex, and custom agents.

#### Recommended workflow

1. Run the Actor with the features you need.
2. Fetch dataset items via the [Apify API](https://docs.apify.com/api/v2) or export JSON/CSV.
3. Pass the records to your model with a system prompt, or index them into a vector store.

#### Apify MCP (Model Context Protocol)

Use the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp) so AI assistants can **run** this Actor from natural-language instructions, **read** the results in chat, and **chain** it with other Actors.

```
User: "Find 40 highly rated dentists in Austin that have a phone number and a website, then draft an intro email for each."
→ MCP runs the Actor with searchTasks=[{ query: "dentists", location: "Austin, TX" }],
  minRating=4, requirePhone=true
→ MCP reads the dataset
→ LLM segments the list, scores it, and drafts outreach
```

***

### Performance & cost tips

- **Start small.** Run one task with `placesPerQuery: 10` to confirm the shape of the output before launching a large job.
- **Use `maxItems` as your budget guard.** The run stops the moment the cap is reached.
- **`fetchPlaceDetails`** adds a small amount of extra work per business. It is worth it for address breakdown and opening hours — turn it off if you only need listing counts and contact fields.
- **`fetchEmails`** is the heavier option — it looks up each business's own website, so allow more time. Pair it with `requireWebsite` to avoid wasted lookups, and enable it only when you need contactability.
- **Split very large jobs.** Several medium runs (one metro each) are more resilient and easier to debug than one enormous run.
- **Schedule repeat snapshots.** `placeId` is stable, so you can diff runs to find new, changed, or closed businesses.
- **Search first, then enrich by URL.** Use search to build a list, then feed the specific links you care about back in through *Scrape by URL* for targeted enrichment.

***

### Proxy & geo

An **Apify residential US proxy** is enabled by default, because local business results vary by location and IP reputation. Change the country in **Proxy settings** if you are collecting outside the US, or supply your own proxy URLs.

`country` and `language` also influence the locale of names and addresses — set both when collecting outside the US (for example `de` / `de` with a German proxy country).

***

### FAQ

**Do I need a Bing account or API key?**
No. The Actor collects publicly available local business listing data. There is nothing for you to configure beyond the input.

**Why did my free-plan run only return 2 businesses?**
Because this is a paid Actor: free accounts receive a free sample of 2 businesses so you can judge the data quality, along with a reminder to upgrade. Paid plans return the full dataset you asked for — nothing else about the run changes. (The owner can switch the sample off entirely with `FREE_TIER_MODE=block`, in which case a free run exports nothing and says so in the log.)

**Why are some fields `null`?**
Not every listing exposes every detail. Most businesses have phone, website, rating, and hours, but small or new listings may be missing them. Rows are always exported, and enrichment fills in as much as the source has.

**Why is there a `ratingSource` field?**
Bing aggregates ratings from third-party directories, so a rating may be attributed to Yelp, Tripadvisor, or Bing itself. We preserve the source rather than hiding it.

**How do I get emails for the businesses?**
Set `fetchEmails: true`. Emails and social profiles are added as `emails[]` and `socials[]`.

**Can I scrape specific businesses I already have links for?**
Yes — paste them into *Scrape by URL*. Each link returns the same field-rich record. If a link points at one branch of a multi-location brand, check `linkMatch`: `business_name` means Bing resolved the brand by name and the returned `address` may be a different branch than the one in your link.

**Can the same business appear twice in one run?**
No. Rows are deduplicated by `placeId` across search, nearby, and links.

**How do I get only businesses within a few kilometres of a point?**
Add `latitude`, `longitude` and `radiusMeters` to a search task. Only businesses inside that circle are exported, each with `distanceMeters`. Both coordinates are required — the run tells you clearly if they are missing, because we never guess a centre from a place name.

**Why did my radius search return far fewer businesses than I asked for?**
Because a radius can only filter what the source already returns for that query — it cannot discover businesses the source does not return. Combine a small radius with a tight location (a postcode or neighbourhood rather than a whole city) and a generous `placesPerQuery`/`maxPagesPerQuery` so there are enough candidates to filter. The log shows exactly how many were excluded.

**Can I target a neighbourhood or a ZIP code instead of a whole city?**
Yes — put it in the task's location: `"downtown Austin"` or `"78701"`. Postcodes are the most precise text option (in a measured test, `plumbers` in `78701` returned results clustered within ~7 km of downtown, versus ~25 km for the city name).

**Can I get results in another language or country?**
Yes — set `language` and `country` (e.g. `de` / `de`) and match the proxy country.

**My run hit the spending limit.**
That is Apify's per-run spending limit. The Actor stops collecting and exits gracefully so you are not charged for work it did not finish; the run summary sets `spendingLimitReached: true`.

**Can I receive results in real time instead of downloading them?**
Yes — set `webhookUrl` and every record is POSTed to your endpoint as it is collected. See [Webhooks](#webhooks).

***

### Legal

This Actor collects publicly available business listing information. You are responsible for how you use the data, including compliance with applicable data-protection and marketing regulations in your jurisdiction.

***

### Support

Open an issue from the Actor's **Issues** tab with the run ID and the input you used, and we will take a look.

# Actor input Schema

## `enableSearch` (type: `boolean`):

Search Bing Maps by keyword and location. Enabled by default.

## `searchTasks` (type: `array`):

Add one row per search: a keyword plus a city or area. Note: this is a paid Actor — free (non-paying) Apify accounts receive a free sample of 2 businesses with an upgrade reminder, while paid plans get the full dataset.

## `placesPerQuery` (type: `integer`):

Default maximum businesses per search task. Override per task in the list above.

## `maxPagesPerQuery` (type: `integer`):

How deep this Actor collects for each task. The default is generous — lower it for quick test runs.

## `enableNearby` (type: `boolean`):

Discover businesses by category around one location, without typing keywords yourself.

## `nearbyCategories` (type: `array`):

Business categories to discover around the location below. All are used when none are selected.

## `nearbyLocation` (type: `string`):

City or area for nearby discovery (e.g. "Miami, FL").

## `placesPerCategory` (type: `integer`):

Maximum businesses to collect for each selected category.

## `enableScrapeByUrl` (type: `boolean`):

Resolve specific Bing Maps business links into the same enriched records as a search. Turns on automatically when you paste links below — untick to ignore the list.

## `scrapeUrls` (type: `array`):

One link per business, e.g. https://www.bing.com/maps?q=Business+Name\&filters=ypid:"…". Links that are not Bing Maps business links are skipped with a note in the log.

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

Keep only businesses rated at least this high (0–5). Leave empty for no rating filter.

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

Only keep businesses that list a phone number.

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

Only keep businesses that list a website.

## `categoryFilter` (type: `string`):

Keep only businesses whose category contains this text (e.g. "dentist", "hotel", "plumb").

## `excludeClosed` (type: `boolean`):

Skip permanently and temporarily closed businesses.

## `fetchPlaceDetails` (type: `boolean`):

Add structured street/city/state/postal-code fields and the weekly opening-hours schedule to each business. Adds a little extra time and cost per business.

## `fetchEmails` (type: `boolean`):

Visit the business website and discover contact emails plus social profiles (adds emails\[] and socials\[]). Best opt-in for lead generation and outreach lists.

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

Language for business names and addresses (e.g. en, es, de).

## `country` (type: `string`):

Two-letter country code used for localized results and proxy routing (e.g. us, uk, ca).

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

Global cap on dataset rows for this run. Note: on the Apify free plan only a small free sample is exported (2 businesses) — a paid plan is required for the full dataset. Paying users are never capped by the plan; only this setting applies.

## `webhookUrl` (type: `string`):

Optional. Every record is always saved to the run's dataset — this webhook is an ADDITIONAL real-time push to your own system (CRM, Slack incoming webhook, Zapier, Make, Google Sheets).

## `webhookFormat` (type: `string`):

json = full record object; slack = Slack-friendly message payload.

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

Apify residential proxy (US) is enabled by default for reliable Bing Maps collection.

## Actor input object example

```json
{
  "enableSearch": true,
  "searchTasks": [
    {
      "query": "coffee shops",
      "location": "Seattle, WA"
    },
    {
      "query": "plumbers",
      "location": "Austin, TX"
    }
  ],
  "placesPerQuery": 20,
  "maxPagesPerQuery": 50,
  "enableNearby": false,
  "nearbyCategories": [
    "restaurants",
    "hotels",
    "cafes",
    "dentists",
    "gyms"
  ],
  "nearbyLocation": "",
  "placesPerCategory": 20,
  "enableScrapeByUrl": false,
  "scrapeUrls": [
    "https://www.bing.com/maps?q=Storyville%20Coffee%20Pike%20Place&filters=ypid%3A%22YN6230075ECE10B067%22"
  ],
  "requirePhone": false,
  "requireWebsite": false,
  "categoryFilter": "",
  "excludeClosed": true,
  "fetchPlaceDetails": true,
  "fetchEmails": false,
  "language": "en",
  "country": "us",
  "maxItems": 10000,
  "webhookUrl": "",
  "webhookFormat": "json",
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  }
}
```

# Actor output Schema

## `allResults` (type: `string`):

Complete dataset with every field collected in this run.

## `search` (type: `string`):

Businesses from keyword + location search.

## `nearby` (type: `string`):

Businesses from nearby category discovery.

## `byUrl` (type: `string`):

Businesses resolved from the Bing Maps links you supplied.

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

Per-run metadata: features, tasks, record counts, enrichment counts, webhook deliveries, errors, spending-limit flag, and the free-tier gate result (paywall object).

# 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 = {
    "enableSearch": true,
    "searchTasks": [
        {
            "query": "coffee shops",
            "location": "Seattle, WA"
        },
        {
            "query": "plumbers",
            "location": "Austin, TX"
        }
    ],
    "placesPerQuery": 20,
    "excludeClosed": true,
    "fetchPlaceDetails": true,
    "fetchEmails": false,
    "language": "en",
    "country": "us",
    "maxItems": 10000,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "US"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("b2b_leads/bing-maps-real-time-data").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 = {
    "enableSearch": True,
    "searchTasks": [
        {
            "query": "coffee shops",
            "location": "Seattle, WA",
        },
        {
            "query": "plumbers",
            "location": "Austin, TX",
        },
    ],
    "placesPerQuery": 20,
    "excludeClosed": True,
    "fetchPlaceDetails": True,
    "fetchEmails": False,
    "language": "en",
    "country": "us",
    "maxItems": 10000,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "US",
    },
}

# Run the Actor and wait for it to finish
run = client.actor("b2b_leads/bing-maps-real-time-data").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 '{
  "enableSearch": true,
  "searchTasks": [
    {
      "query": "coffee shops",
      "location": "Seattle, WA"
    },
    {
      "query": "plumbers",
      "location": "Austin, TX"
    }
  ],
  "placesPerQuery": 20,
  "excludeClosed": true,
  "fetchPlaceDetails": true,
  "fetchEmails": false,
  "language": "en",
  "country": "us",
  "maxItems": 10000,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  }
}' |
apify call b2b_leads/bing-maps-real-time-data --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,b2b_leads/bing-maps-real-time-data"
        }
    }
}
```

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/7bcfWfO1b6HasJ962/builds/7z7Hx5kjyCDuJhKNQ/openapi.json
