# Bing Ad Library Scraper — Ad Search & Export (`devilscrapes/bing-ad-library-scraper`) Actor

Export ad records from Microsoft's public Bing/Microsoft Advertising Ad Library — ad id, advertiser, title, description, display and destination URLs, and creative assets. Search by advertiser name, resolved server-side to an AdvertiserId. Deduplicated rows, no login or API key required.

- **URL**: https://apify.com/devilscrapes/bing-ad-library-scraper.md
- **Developed by:** [DevilScrapes](https://apify.com/devilscrapes) (community)
- **Categories:** SEO tools, Automation, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

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

<div align="center">
  <img src=".actor/icon.svg" width="160" alt="Devil Scrapes mark" />

## Bing Ad Library Scraper

**💰 $3.20 / 1 000 ads**  ·  pay only for results  ·  no credit card to try

*We already reverse-engineered Bing's ad library wire format so you don't have to.* 😈

Search Microsoft's public **Bing/Microsoft Advertising Ad Library** by advertiser name and get every matching ad back as clean, deduplicated rows — ad id, advertiser, title, description, display and destination URLs, and creative assets.

</div>

***

> **Quick answer:** The **Bing Ad Library Scraper** searches Microsoft's public Bing/Microsoft Advertising Ad Library by advertiser name and returns every matching ad as structured, deduplicated data — ad id, advertiser, title, description, display URL, destination URL, and creative assets. Filtering works by advertiser name, resolved to a real advertiser id behind the scenes — Bing's own free-text ad search parameter doesn't actually filter anything, so we don't expose it. Pricing is pay-per-result — **$3.20 per 1 000 ads landed**, no subscription, no card required to try it.

### 🎯 What this scrapes

Microsoft's Bing/Microsoft Advertising Ad Library is a public ad-transparency surface — the same class as Meta's, TikTok's, LinkedIn's, and Pinterest's ad libraries, but for ads placed through Microsoft Advertising. This Actor searches it by advertiser name, resolves each name to one or more numeric advertiser ids via Bing's own advertiser-lookup endpoint, then pages through every ad Bing has on file for each matched advertiser.

Per ad you get:

| Field | Type | Notes |
|---|---|---|
| `ad_id` | string | Bing's stable ad id — our dedup key |
| `advertiser_id` | string | The resolved numeric advertiser id |
| `advertiser_name` | string | Advertiser name as Bing reports it |
| `title` | string | Ad headline |
| `description` | string | Ad body copy |
| `display_url` | string | Displayed URL shown on the ad |
| `destination_url` | string | Landing page URL |
| `assets` | array | Creative assets (width, height, type, URL), when present |
| `search_term` | string | The advertiser name your search used |
| `scraped_at` | string | ISO-8601 UTC timestamp |

### 🔥 What we handle for you

- 🕵️ **We use the filter that actually works.** Bing's ad-search endpoint accepts a `query` parameter that looks like a free-text ad-content filter — it silently does nothing and falls back to an unfiltered feed. We resolve your advertiser name to a real advertiser id first, the only parameter Bing's own API honors.
- 🧊 **We deduplicate every row.** Adjacent pages of the same advertiser's ad feed overlap — we track unique ad ids incrementally so your dataset count is always the true unique count, never `pages × page size`.
- 🧱 **We back off when Bing rate-limits.** Sustained requests trigger a `429` — we pause with exponential backoff (up to 5 attempts), honoring `Retry-After`, and resume from the same page rather than failing your run.
- 🎯 **We isolate faults per search term.** One advertiser name that resolves to nothing, or one advertiser with zero ads on file, doesn't sink the other names in your list — each search term succeeds or fails independently.
- 🧷 **We never choke on a bad creative blob.** Bing occasionally serves an empty or malformed asset payload — we record it as an empty list instead of failing the row.
- 💰 **You pay only for results that land.** No data → no charge (only the small `actor-start` warm-up fee).

### 💡 Use cases

- **Competitor ad monitoring** — see what a brand is currently running through Microsoft Advertising.
- **Ad creative research** — pull headlines, body copy, and destination URLs for a market study.
- **Brand protection** — search your own brand name for unauthorized advertisers.
- **Agency reporting** — export a client's (or competitor's) live Bing ad set straight to a spreadsheet.
- **AI / RAG ingestion** — feed ad copy into a vector store for messaging-trend analysis.

### ⚙️ How to use it

1. **Click "Try for free"** at the top of the page.
2. **Add one or more advertiser names** to `advertiserNames` — required, e.g. `["nike", "adidas"]`.
3. *(Optional)* Adjust `maxAdvertisersPerName` (how many resolved advertiser matches to fetch per name) and `maxResultsPerAdvertiser` (cap on unique ads per advertiser).
4. **Run.** Each unique ad is one row in the dataset; export to JSON, CSV, or Excel from the Storage tab.

The first run on a new account uses $5 of free Apify credit — roughly **1 500 ads** at our pricing.

### 📥 Input

The schema lives in `.actor/input_schema.json`. The fields:

| Field | Type | Required | Default | Notes |
|---|---|:--:|---|---|
| `advertiserNames` | array of strings | yes | — | Advertiser names to search, each resolved to a real advertiser id |
| `maxAdvertisersPerName` | integer | no | `3` | Cap on resolved advertiser matches fetched per search term (1-10) |
| `maxResultsPerAdvertiser` | integer | no | `100` | Cap on unique deduplicated ad rows per advertiser (1-1 000) |
| `proxyConfiguration` | proxy config | no | no proxy | Bing's Ad Library API returns 200 with zero proxy by default |

`advertiserNames` must contain at least one non-blank entry — the Actor fails fast before any network call or charge if it's missing or empty.

#### Example input

```json
{
  "advertiserNames": ["nike", "adidas"],
  "maxAdvertisersPerName": 3,
  "maxResultsPerAdvertiser": 100,
  "proxyConfiguration": { "useApifyProxy": false }
}
```

### 📤 Output

Every row is one unique ad. Example:

```json
{
  "ad_id": "73392676478586",
  "advertiser_id": "4295000966",
  "advertiser_name": "Nike - EMEA",
  "title": "Football Jerseys from Nike - The Official Nike Shop",
  "description": "Gear up for the game with official Nike football jerseys.",
  "display_url": "www.nike.com/football/jerseys",
  "destination_url": "https://www.nike.com/w/football-jerseys-1gdj0z6cb3p",
  "assets": [],
  "search_term": "nike",
  "scraped_at": "2026-09-21T06:00:00+00:00"
}
```

Export options once the run finishes:

- **JSON** — full payload
- **CSV / Excel** — for analyst spreadsheets
- **JSONL** — line-delimited, easy to stream into a warehouse
- **API** — fetch programmatically via `GET /v2/datasets/{id}/items`; webhook on `ACTOR.RUN.SUCCEEDED` for live pipelines

### 💰 Pricing

Pay-per-event. You pay for what you get, nothing for what you ask for:

| Event | Price | When charged |
|---|---:|---|
| `actor-start` | **$0.20** | Base fee, once per run (warm-up, input validation) |
| `ad-result` | **$0.003** | Per unique `ad_id` written to the dataset |

**Examples**:

| Pull | Cost |
|---|---:|
| 100 ads | $0.50 |
| 1 000 ads | $3.20 |
| 10 000 ads | $30.20 |

### 🚧 Limitations

- **Filtering is by advertiser name only.** Bing's ad-search endpoint accepts a free-text `query` parameter, but it doesn't actually filter — we don't expose it since it would silently do nothing.
- **Hard wire ceiling of 1 000 ads per advertiser.** Bing's own pagination caps at that point regardless of `maxResultsPerAdvertiser`. Very high-volume advertisers are truncated there.
- **`maxAdvertisersPerName` slices a single advertiser-lookup response** — it doesn't deep-paginate advertiser matches beyond what Bing returns for that search text in one call.
- **No country/market scoping.** Bing's ad-search endpoint ignores a market parameter in testing, so we don't expose an input that would silently do nothing.

### ❓ FAQ

**Is this legal?**

Yes. Microsoft's Bing/Microsoft Advertising Ad Library is a public ad-transparency registry. We search only what the public API exposes, at a paced rate, with no login wall or paywall involved.

**Do I need a Microsoft Advertising account or API key?**

No. This Actor works entirely against the public Ad Library API — no app registration, no OAuth, no access token.

**Why does my result count sometimes look lower than expected?**

A few reasons: your `maxAdvertisersPerName` / `maxResultsPerAdvertiser` caps, an advertiser name that only resolves to a handful of matches, or an advertiser with fewer than 1 000 ads on file. A genuine zero-match search still succeeds — it just returns zero rows with a status message saying so.

**Can I search by ad content instead of advertiser?**

No — Bing's only proven working filter is advertiser name, resolved to an advertiser id. A raw keyword-search parameter exists on the wire but doesn't filter anything, so we don't build a feature on top of a parameter we verified is dead.

**How do I schedule this to run automatically?**

Apify Console → Schedules → attach this Actor → set your cron. Weekly is plenty; ad libraries don't turn over fast enough to need more.

**What integrations does this Actor support?**

- **Schedule** — Apify Console → Schedules for recurring monitoring.
- **Webhooks** — register `ACTOR.RUN.SUCCEEDED` to fire a downstream pipeline.
- **API** — `POST /v2/acts/DevilScrapes~bing-ad-library-scraper/run-sync-get-dataset-items` for a single synchronous pull.
- **Make / Zapier** — every Apify Actor surfaces as a node out of the box.

### 💬 Your feedback

Spotted a bug, missing field, or want a new feature? Open an issue on the [Apify Store listing](https://apify.com/DevilScrapes/bing-ad-library-scraper) — we read every one.

Built by **Devil Scrapes** — Apify Actors with attitude. PPE, transparent pricing, no junk fields.

# Changelog

This Actor's version history is a separate document: https://apify.com/devilscrapes/bing-ad-library-scraper/changelog.md

# Actor input Schema

## `advertiserNames` (type: `array`):

One or more advertiser names to search, e.g. <code>nike</code>. Each is resolved via Bing's own advertiser lookup to one or more numeric AdvertiserIds — the only filter that actually works (Bing's free-text ad-content <code>query</code> parameter silently does nothing). Required, at least one non-blank entry.

## `maxAdvertisersPerName` (type: `integer`):

Cap on how many resolved advertiser matches per search term to fetch ads for — a search term can match multiple advertiser entities (e.g. different regional arms of the same brand). This slices Bing's single advertiser-lookup response; it never triggers a second lookup call.

## `maxResultsPerAdvertiser` (type: `integer`):

Cap on unique deduplicated AdId rows collected per resolved advertiser. Bing's own wire format hard-caps any single advertiser's walk at 1,000 ads.

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

Apify Proxy. Bing's Ad Library API returns 200 with zero proxy and zero auth, so this defaults to no proxy at all.

## Actor input object example

```json
{
  "advertiserNames": [
    "nike",
    "adidas"
  ],
  "maxAdvertisersPerName": 3,
  "maxResultsPerAdvertiser": 100,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

Full set of scraped ads — one item per unique AdId. Streamed JSON via the Apify dataset API.

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

Same data, CSV export. Drop straight into a spreadsheet or warehouse loader.

## `datasetView` (type: `string`):

Open the run's dataset in the Console with the Overview view applied.

# 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 = {
    "advertiserNames": [
        "nike",
        "adidas"
    ],
    "maxAdvertisersPerName": 3,
    "maxResultsPerAdvertiser": 100,
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("devilscrapes/bing-ad-library-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 = {
    "advertiserNames": [
        "nike",
        "adidas",
    ],
    "maxAdvertisersPerName": 3,
    "maxResultsPerAdvertiser": 100,
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("devilscrapes/bing-ad-library-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 '{
  "advertiserNames": [
    "nike",
    "adidas"
  ],
  "maxAdvertisersPerName": 3,
  "maxResultsPerAdvertiser": 100,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call devilscrapes/bing-ad-library-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,devilscrapes/bing-ad-library-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/HV2uihJ9AkeywPZx3/builds/M4Ab3ua5FU5xqBqDH/openapi.json
