# Bing Ads Library Scraper - Advertisers, Ad Copy (`automation_craft/bing-ads-library-scraper`) Actor

Pull competitor Bing ads from the Bing Ads Library by keyword, advertiser name or ad id: headline, description, display and landing URL, advertiser, image assets, plus dates, impression band, countries and targeting on request. Each distinct ad billed once, misses free. CSV, JSON, API.

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

## Pricing

from $0.96 / 1,000 ads

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

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

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

### Bing Ads Library Scraper - Advertisers, Ad Copy

A **Bing Ads Library scraper** for PPC teams, agencies and brand managers: see which advertisers run Bing ads on a keyword, what their ads say and where they send the click. It reads the Bing Ads Library (Microsoft's public EU archive at adlibrary.ads.microsoft.com, the Microsoft Ad Library, which holds the ads shown on Bing) and returns each distinct ad as one row: advertiser, headline, description, display URL, landing URL and image assets. Switch on details for the payer, the first and last day the ad ran, its impressions band, the impression share per country and the targeting types. Many keywords, advertisers and ad ids in one run, no login, no API key, no proxy. Each ad is billed once per run, however often the library repeats it, and everything the library does not have comes back as a free row.

### Quick start

1. Type the keywords your customers search on Bing into **searchTerms** (`running shoes`, a brand name, a product), or a competitor into **advertisers** (a name such as `eBay Marketplaces GmbH`, an advertiser id, or an advertiser page URL from the library). Ad ids and ad page URLs go into **adIds**.
2. Narrow it if you like: **countries** (EU and EEA), **startDate** and **endDate** (the last 30 days when left out), **adFormat** (text ads, or ads with an image).
3. Switch on **includeDetails** for dates, impressions, countries, payer and targeting on every ad.
4. Set **maxAds** (100 when left out) and run. Download the ads as CSV, Excel or JSON, or call the Actor from the API, n8n, Make or a schedule.
5. To watch a competitor or a keyword every week, give a **memoryName**: later runs deliver only the Bing ads you have not received before.

The prefilled run searches `running shoes` in the last 30 days and delivers 20 ads with their detail records: about 1 to 2 minutes and 5.8 cents.

### What PPC teams use it for

- **Competitor ad copy on Bing**: every headline and description an advertiser ran in the EU and EEA, with the landing page of each ad, for one advertiser or a list of them.
- **Who bids on a keyword or a brand term**: search the term and read the `advertiserName` column, one row per ad, with the advertiser id to go deeper.
- **Landing page and offer research**: `landingUrl` and `landingDomain` on every row show where the click goes; filter the dataset by domain.
- **How long and how wide an ad ran**: with details, `firstShown`, `lastShown`, `daysShown`, the impressions band as numbers and the share per country.
- **A weekly Bing ads watch**: schedule the same input with a memory name and receive only new ads.

### What you get

One row per distinct ad (`type: ad`). Fill rates were measured on the live library on 2026-10-07 from the Apify platform: 1,750 distinct ads from 30 runs (keywords, advertisers, ad ids, a deep search), 194 detail records from three of them and 10 advertiser profiles.

| Field | What it is | Filled |
|---|---|---|
| `adId`, `adUrl` | The ad's id and its page in the Bing Ads Library | 1,750 of 1,750 |
| `advertiserName`, `advertiserId`, `advertiserUrl` | The advertiser as the library names it, its id and its library page | 1,750 of 1,750 |
| `title`, `description` | The ad's headline and description text | 1,750 of 1,750 and 1,707 of 1,750 |
| `displayUrl`, `landingUrl`, `landingDomain` | The URL the ad displays (for some product ads the merchant name), the URL it links to and that URL's host | 1,750 of 1,750 |
| `imageUrl`, `imageUrls`, `assets`, `hasImage` | Image assets with width and height, when the ad has any; the share depends on the keyword (84 of 150 ads on other keywords the day before) | 142 of 1,750 |
| `paidBy`, `paidByDiffers` | Who paid for the ad. `paidBy` is the payer the library names, or the advertiser when it names none; `paidByDiffers` is true when the named payer is another company than the advertiser (an agency, a parent or a reseller account; capitals, accents and punctuation aside). True on 2 of these 194 rows; in a separate sample of 100 detail records from travel, insurance and retail keywords the library named a payer on 29 and another company on 13 | with details |
| `firstShown`, `lastShown`, `daysShown`, `daysSinceLastShown` | First and last day the ad ran in the EU or EEA | 194 of 194 with details |
| `impressionsRange`, `impressionsMin`, `impressionsMax` | The library's band of total impressions (`10K - 25K`) and its bounds as numbers | 194 of 194 with details |
| `impressionsByCountry`, `topCountry`, `topCountryShare`, `countriesShown` | Impression share per country with ISO codes, largest first | 194 of 194 with details |
| `targeting`, `targetingTypes`, `exclusionTypes` | The kinds of targeting used (Location, Age, Gender, MicrosoftAudiences, AdvertiserAudiences) and whether each was used to exclude | 192 of 194 with details |
| `restricted`, `restriction` | The library's restriction record for an ad it stopped serving | 0 of 194 with details |
| `advertiserCountry`, `advertiserVerified` | The advertiser's registered country and identity verification, with **includeAdvertiserProfile** | 10 of 10 ads |
| `detailsStatus`, `source`, `search`, `scrapedAt` | Whether the detail record was read, how the ad was found and by which search | every row |

The free rows next to the ads:

- **`coverage`**, one per search: the library's own count, pages read, rows read, repeated rows, distinct ads, ads delivered, the stop reason, `capped` and `complete`.
- **`status`**, one per miss or refusal: an ad id the library does not have (`ad_not_found`), an advertiser name the register does not have, with the closest register names (`advertiser_not_found`), the register entries a name stood for (`advertisers_matched`), an unusable value (`invalid`), a lookup the library did not answer (`lookup_failed`), a register search without a match (`no_advertisers`), a memory that could not be used (`memory_free`), inputs not started because the run stopped (`skipped`).
- **`summary`**, one per run: counters, requests, what was charged and why the run stopped.
- **`advertiser`** rows come from the advertiser register search: name, id, country, verified status and library page.

### How much does it cost to scrape the Bing Ads Library?

| Event | FREE | BRONZE | SILVER | GOLD |
|---|---|---|---|---|
| **Ad** (one distinct ad found by a search, delivered as a row) | $1.20 per 1,000 | $1.20 per 1,000 | $1.08 per 1,000 | $0.96 per 1,000 |
| **Ad detail record** (the detail record of one ad: payer, dates, impressions, countries, targeting) | $1.50 per 1,000 | $1.50 per 1,000 | $1.35 per 1,000 | $1.20 per 1,000 |
| **Results page** (one page of up to 24 results the library answered) | $0.40 per 1,000 | $0.40 per 1,000 | $0.40 per 1,000 | $0.40 per 1,000 |
| Actor start (once per run) | $0.004 | $0.004 | $0.004 | $0.004 |

Platinum and Diamond plans pay the Gold price. The Store pricing card shows these same prices per 1,000 events: "$1.20 / 1,000" on the Ad row means one ad costs 0.12 cents, "$1.50 / 1,000" on the Ad detail record row means one detail record costs 0.15 cents, "$0.40 / 1,000" on the Results page row means one page costs 0.04 cents.

How the events add up:

- **An ad found by a search** costs one Ad event, and one Ad detail record more when details are on and the record was read.
- **An ad looked up by id** costs one Ad detail record and no Ad event.
- **A Results page** is charged for every page read that holds at least one result, whatever it holds: also a page whose ads were repeats, were delivered by an earlier search of the run, or are already in your memory. Advertiser rows of the register search are not charged one by one; they cost the pages read.
- **Free**: repeated ads, ads an earlier search of the run delivered, ads your memory knows, a search with no result, an ad id or an advertiser name the library does not have, a search term the library's search refuses, a call the library did not answer, coverage rows, status rows and the summary.
- **Misses are free, and bounded in time**: a run may work 20 seconds plus 6 seconds for every event charged. Charged work earns more time than it uses, so it never meets this rule; a list of ids, names or terms the library does not hold meets it after about 20 seconds, and the inputs not started are named in one free `skipped` row to run again.

Worked examples at Bronze: the prefilled run (1 page, 20 ads with details) costs $0.0584; 100 ads without details from one search cost $0.1264 (6 pages in our run: the library repeats ads, so a page often holds fewer than 24 new ones); 1,000 ads from two capped searches cost $1.2336 (74 pages in our run), with details $2.7336; three ad ids looked up cost $0.0085; a scheduled watch with a memory that reads 13 pages and finds nothing new costs $0.0092. The library answers the Apify platform directly, so no proxy traffic is added to your bill.

### Input

| Field | What it does |
|---|---|
| `searchTerms` | Keywords or phrases, one search each. Every word of a term must appear in the ad. The library's search accepts unaccented Latin letters, digits, spaces and `# $ & ' * - . ? @ ^ _ |` only; a term with other characters (an umlaut, a comma, quotes) is not searched and comes back as a free row. |
| `advertisers` | Names, advertiser ids or advertiser page URLs. A name is looked up in the library's advertiser register and every entry with exactly that name is searched (one company often has several advertiser accounts; capitals and spacing do not matter, accents do). The register is read up to 120 names deep for the text; when it lists more, or a page of it fails, the `advertisers_matched` row says the lookup is incomplete. |
| `advertiserMatch` | `exact` (default) or `contains`: every register entry whose name contains your text, up to 24 per text. |
| `searchTermsWithinAdvertisers` | Search each term inside each advertiser's ads only, instead of running terms and advertisers as separate searches. |
| `adIds` | Ad ids or ad page URLs (`https://adlibrary.ads.microsoft.com/ad/<id>`). Each ad found comes back with its detail record. An advertiser page URL pasted here is searched as an advertiser. |
| `advertiserSearchTerms` | Search the advertiser register itself: advertiser rows for every register name containing the text. |
| `countries` | Keep ads shown in at least one of these countries (EU 27 plus Iceland, Liechtenstein and Norway). Several countries are one search. The library counts any impression, so read the share per country in the detail record. |
| `startDate`, `endDate` | The window: `YYYY-MM-DD`, `today`, `yesterday`, or `30 days` back from the end. The last 30 days when left out; send `2023-06-01` as the start for the whole library. |
| `adFormat` | `all`, `text` (no image asset) or `image` (at least one image asset). The filter is applied to the results the library returns, so the pages read are the same and are charged as Results pages also when the filter removes their ads. |
| `includeDetails` | The detail record on every ad: one more request and one Ad detail record per ad. |
| `includeAdvertiserProfile` | The advertiser's country and verified status on every ad: one request per distinct advertiser, no extra charge. |
| `deepSearch` | Split a search the library caps at 1,000 results into date halves, down to single days, and read every part. |
| `maxAds`, `maxAdsPerSearch`, `maxPagesPerSearch`, `maxAdvertisersPerTerm` | Caps for the run, for each search, for the pages of one search (400 when left out) and for the advertiser rows of one register search (24 when left out). |
| `memoryName`, `resetMemory` | A named memory in your account: later runs with the same name deliver only new ads. |
| `proxyConfiguration` | Not needed. With a proxy the run still keeps one address; the residential group is replaced by the datacenter pool. |

These keys are read too, so an existing JSON input can be pasted: `searchQueries`, `queries`, `keywords`, `searchAdText`, `advertiserNames`, `advertiserIds`, `advertiserId`, `advertiserSearchQueries`, `searchAdvertiserText`, `startUrls`, `adUrls`, `countryCodes`, `country`, `dateFrom`, `dateTo`, `includeAdDetails`, `fetchDetails`, `includeAdvertiserDetails`, `maxResults`, `maxItems`, `maxSearchResults`, `maxAdsPerQuery`, `monitorStoreName`. Country names and Microsoft's numeric country codes are accepted through those keys. A target key that is sent but holds nothing usable is refused with a free row; it never turns into another search.

### How a search is read, and what "complete" means

The library answers at most 1,000 results per search, 24 per page, and repeats ads inside a large result: on 2026-10-07 the first six to eight pages of five common keywords held 89 to 114 different ads in 144 to 192 rows, and a whole capped search of 1,008 rows held 555 to 574 different ads in four walks measured the day before. This Actor delivers each ad once and counts the repeats in the coverage row. The coverage row says `capped: true` when the library reported 1,000 results, and `complete: true` only when every page the library offers for the search was read and no part of it was at the cap.

To read a capped search more deeply, shorten the window or switch on **deepSearch**: the window is cut into two halves, newest first, again and again down to single days, and every part is read. A keyword that counted 1,000 for the whole library counted 668 for 30 days, 435 for 7 days and 280 for one day, so mid size searches come back complete this way. Very large searches stay capped even for a single day; the coverage row then says how many parts were still capped (`partsCapped`). A country filter does not help against the cap: the library counts any impression in a country.

Microsoft limits how often the library may be called from one address. This Actor keeps one address per run, sends one request every 2.5 seconds and waits a few seconds when the library answers "too many requests". The Apify platform's address is shared with other runs, and the library refuses some requests at any pace (about one in five in our measurements), each cleared by a wait of a few seconds. Measured on 2026-10-07: 100 ads without details took 17 seconds; 150 ads with details took about 10 minutes (about 4 seconds per ad), so 1,000 ads with details take a little over an hour. Across those test runs 132 of 604 requests met a refusal and waited. Some first answers of the library take 20 to 30 seconds; a search the library does not answer in time ends as a free coverage row with the reason.

### Memory across runs

With a **memoryName**, the ads delivered under that name are remembered in a key value store this Actor creates in your account. Later runs with the same name skip them free and deliver only ads you have not received: an ad delivered once is never delivered or charged again under that name; the pages read to find them are charged as Results pages. The library has no "newest first" order, so a watch reads every page of its searches on each run. Two runs on one name at the same time: the second runs free and remembers nothing. The memory is written before anything is charged; if it cannot be opened, locked or saved, the run delivers its ads free, charges no page, and says so. A run the platform restarts delivers what it still reads free. An ad whose detail record the library did not return is delivered without it (flagged in `detailsStatus`) and remembered; look it up by ad id in a run without a memory. A memory belongs to this Actor: the same name used in another Actor is a separate memory.

### What this Actor does NOT do

- No search by landing page domain: the library has no domain filter. Every row carries `landingDomain`, so search by keyword or advertiser and filter that column.
- No single yes or no row per company with an ad count. An advertiser search answers it in two rows: the `advertisers_matched` row and the coverage row's `archiveCount`.
- No ad count and sample titles on advertiser register rows: they carry name, id, country and verified status.
- The memory reports new ads, not changed ones (an impressions band that moved, a later last shown day).
- No Google ads in the same run: this Actor reads the Bing Ads Library only.
- No input from an uploaded file or a Google Sheet, and no library search page URLs: lists are pasted or sent through the API; ad page and advertiser page URLs are accepted.
- No promo or discount parsing of the ad text.
- No rotation over many addresses to go faster: one address per run, at most 24 requests a minute, about 4 seconds per ad with details.
- No targeting values, spend or exact impressions: the library publishes target types and an impressions band only.
- No split by placement: the library does not say where on Bing an ad appeared, and no row claims it.
- No ads outside the EU and EEA, none before June 2023, and none whose last impression is more than a year old: the library does not hold them. Ad extensions (sitelinks, callouts) are not in the library either.
- No search terms with accented letters, non Latin scripts or most punctuation: the library's search refuses them.

### FAQ

#### Does Bing have an ads library?

Yes. Microsoft keeps the ads shown on Bing in the EU and EEA in a public archive at `adlibrary.ads.microsoft.com`, under the Digital Services Act; Microsoft calls it the Microsoft Ad Library, and its own pages describe it as the ads shown on Bing. This Actor reads it and returns the ads as rows.

#### How do I see my competitors' ads on Bing?

Put the competitor's name into **advertisers**, or a brand or product keyword into **searchTerms**. Each ad comes back with its headline, description, display URL and landing URL; with details on, also the days it ran, its impressions band and the countries it was shown in. The library covers ads shown in the EU and EEA.

#### Is there a Bing Ads Library API?

Yes. Microsoft documents a public API for the library that needs no key and no login. This Actor calls it, removes the repeated ads, decodes the detail record into flat columns and gives you one dataset for many keywords and advertisers, through the Apify API as well as the Console.

#### Which Bing ads are in the library, and for how long?

Ads served on Bing that received impressions in the EU or EEA since June 2023; Microsoft says every ad type is eligible (text, responsive search, dynamic search, product, audience, multimedia, hotel price, app install and vertical ads) and ad extensions are left out. An ad stays in the library for one year after its last impression, and a new ad appears 24 to 48 hours after its first one.

#### Can I export Bing ads to CSV or Excel?

Yes. Every run writes a dataset you can download as CSV, Excel, JSON or XML, or read through the Apify API. The dataset has an Ads view with the main columns, a Coverage and status view and an Advertisers view.

#### Why does this Actor run with limited permissions?

It needs nothing else. It reads the public library and writes only to its own run storage and, when you give a memory name, to a key value store and a lock queue it creates itself in your account. It cannot read your other Actors, tasks or data.

### Use it from the API

```bash
curl -X POST "https://api.apify.com/v2/acts/automation_craft~bing-ads-library-scraper/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"searchTerms": ["running shoes"], "countries": ["DE", "FR"], "startDate": "30 days", "includeDetails": true, "maxAds": 50}'
```

```javascript
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: 'YOUR_TOKEN' });
const run = await client.actor('automation_craft/bing-ads-library-scraper').call({
    advertisers: ['eBay Marketplaces GmbH'],
    startDate: '7 days',
    maxAdsPerSearch: 50,
    memoryName: 'ebay bing watch',
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.filter((row) => row.type === 'ad').length, 'new Bing ads');
```

```python
from apify_client import ApifyClient
client = ApifyClient("YOUR_TOKEN")
run = client.actor("automation_craft/bing-ads-library-scraper").call(run_input={
    "searchTerms": ["yoga mat"],
    "includeDetails": True,
    "maxAds": 30,
})
for row in client.dataset(run["defaultDatasetId"]).iterate_items():
    if row["type"] == "ad":
        print(row["advertiserName"], row["title"], row["landingDomain"], row.get("impressionsRange"))
```

### Changelog

See the Changelog tab. Version 0.1 (2026-10-07): first release.

### More data tools by Automation Craft

The same library under its Microsoft name: [Microsoft Ads Library Scraper - Competitor Ads](https://apify.com/automation_craft/microsoft-ads-library-scraper).

Ad libraries: [Meta Ad Library Scraper - All Placements, Filters](https://apify.com/automation_craft/meta-ads-library-scraper), [Google Ads Transparency Scraper - Decoded Creatives](https://apify.com/automation_craft/google-ads-transparency-scraper), [Google Ads Library Scraper - Competitor Ads, No Login](https://apify.com/automation_craft/google-ads-library-scraper), [LinkedIn Ad Library Scraper: Ads by Company](https://apify.com/automation_craft/linkedin-ad-library-scraper), [TikTok Ads Library Scraper: EU Ads, No Login](https://apify.com/automation_craft/tiktok-ads-library-scraper), [Pinterest Ads Library Scraper - EU Ads Repository](https://apify.com/automation_craft/pinterest-ads-library-scraper), [Snapchat Political Ads Scraper: Spend & Targeting](https://apify.com/automation_craft/snapchat-political-ads-scraper).

Other data: [Google Trends Scraper - Compare and Trending Now](https://apify.com/automation_craft/google-trends-scraper), [Trustpilot Reviews Scraper: Coverage Per Company](https://apify.com/automation_craft/trustpilot-reviews-scraper), [LinkedIn Company Scraper - Details, No Login](https://apify.com/automation_craft/linkedin-company-scraper), [SEC Form D Scraper: Offerings & Funding Leads](https://apify.com/automation_craft/sec-form-d-scraper).

# Changelog

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

# Actor input Schema

## `searchTerms` (type: `array`):

One keyword or phrase per line, searched in the ad text; every word of a term must appear in the ad. Each term is one search; a term the archive has no ad for costs nothing and comes back as a free coverage row. The archive's search accepts unaccented Latin letters, digits, spaces and # $ & ' \* - . ? @ ^ \_ | only: a term with other characters (an umlaut, a comma, quotes) is not searched and comes back as a free row. Up to 200 terms per run.

## `advertisers` (type: `array`):

One advertiser per line: a name (eBay Marketplaces GmbH), an advertiser id (4295009683) or an advertiser page URL of the library (https://adlibrary.ads.microsoft.com/advertiser/4295009683). A name is looked up in the archive's advertiser register and every entry with exactly that name is searched (one company often has several advertiser accounts); a free row lists the entries used. A name the register does not have costs nothing and comes back as a free row with the closest register names. Up to 200 per run.

## `advertiserMatch` (type: `string`):

exact: the register name must equal your text (capitals and spacing do not matter, accents do). contains: every register entry whose name contains your text is searched, up to 24 per text. Through the API send exact or contains. When the field is left out, exact is used.

## `searchTermsWithinAdvertisers` (type: `boolean`):

With search terms and advertisers both given: on, every term is searched inside each advertiser's ads only (ads of eBay that mention shoes); off, terms and advertisers are separate searches. Off when the field is left out.

## `adIds` (type: `array`):

One per line: an ad id (72361740039047) or an ad page URL of the library (https://adlibrary.ads.microsoft.com/ad/72361740039047). Each ad found comes back as one row with its detail record and is charged as one Ad detail record. An id the archive does not have costs nothing and comes back as a free row. Pass long ids as text. Up to 5,000 per run.

## `advertiserSearchTerms` (type: `array`):

Search the advertiser register itself instead of ads: one text per line returns advertiser rows (name, id, country, verified status) for every register name containing it. Advertiser rows are not charged one by one; each page of up to 24 register results read costs one Results page. Up to 50 texts per run.

## `countries` (type: `array`):

Keep ads that were shown in at least one of these countries. Leave empty for every country of the archive (EU 27 plus Iceland, Liechtenstein and Norway). Several countries are one search, not one search per country. Note: the archive counts any impression, so an ad shown almost only in Germany also matches France; the share per country is in the detail record.

## `startDate` (type: `string`):

First day of the window: YYYY-MM-DD, or a number of days, weeks or months back from the end date, such as 30 days. An ad is returned when it was shown on at least one day of the window. When the field is left out, the window starts 30 days before the end date. For the whole archive send 2023-06-01 (the archive records ads since June 2023). A shorter window is also the way to read a large search completely: the archive answers at most 1,000 results per search.

## `endDate` (type: `string`):

Last day of the window: YYYY-MM-DD, today or yesterday. Today when the field is left out.

## `adFormat` (type: `string`):

all: every ad. text: only ads without an image asset (classic text ads). image: only ads with at least one image asset. The filter is applied to the results the archive returns, so the pages read are the same. Through the API send all, text or image; all when the field is left out.

## `includeDetails` (type: `boolean`):

Adds to every ad who paid for it, the first and last day shown, the total impressions band (with numeric minimum and maximum), the impression share per country and the targeting types used. One more request per ad and one Ad detail record charged per ad whose record was read. Off when the field is left out.

## `includeAdvertiserProfile` (type: `boolean`):

Adds advertiserCountry and advertiserVerified from the advertiser register to every ad (one request per distinct advertiser in the run, no extra charge). Off when the field is left out.

## `deepSearch` (type: `boolean`):

The archive answers at most 1,000 results per search, of which about 560 are different ads. With deep search on, a search that hits this cap is cut into two halves of its date window, again and again down to single days, and every part is read. Mid size searches come back complete this way; the coverage row says for each search whether any part was still capped. More Results pages are read and charged. Off when the field is left out.

## `maxAds` (type: `integer`):

The run stops when this many ads were delivered. 100 when the field is left out; up to 100,000.

## `maxAdsPerSearch` (type: `integer`):

A cap for each search term and each advertiser. 0 or empty: no cap per search.

## `maxPagesPerSearch` (type: `integer`):

A search stops after reading this many pages of 24 results. One search without deep search reads at most 42 pages; a deep search of a very large query can read many more. 400 when the field is left out; up to 5,000.

## `maxAdvertisersPerTerm` (type: `integer`):

For the advertiser register search: advertiser rows per text. 24 when the field is left out (one page); up to 240.

## `memoryName` (type: `string`):

A plain name, for example competitor watch. Ads delivered under this name are remembered in a key value store this Actor creates in your account; later runs with the same name skip them free and deliver only new ads. The pages read to find them are still charged as Results pages. A memory belongs to this Actor and to your account. If the memory cannot be opened, locked or saved, the run delivers its ads free.

## `resetMemory` (type: `boolean`):

Forget everything stored under the memory name before the run, so every ad counts as new again.

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

Not needed: the library's API answers the run's own address. If you set a proxy, the run keeps one address for the whole run; the residential group is replaced by the datacenter pool.

## Actor input object example

```json
{
  "searchTerms": [
    "running shoes"
  ],
  "startDate": "30 days",
  "includeDetails": true,
  "maxAds": 20,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `items` (type: `string`):

No description

# 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 = {
    "searchTerms": [
        "running shoes"
    ],
    "startDate": "30 days",
    "includeDetails": true,
    "maxAds": 20,
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("automation_craft/bing-ads-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 = {
    "searchTerms": ["running shoes"],
    "startDate": "30 days",
    "includeDetails": True,
    "maxAds": 20,
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("automation_craft/bing-ads-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 '{
  "searchTerms": [
    "running shoes"
  ],
  "startDate": "30 days",
  "includeDetails": true,
  "maxAds": 20,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call automation_craft/bing-ads-library-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,automation_craft/bing-ads-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/4fT9dOXBRthDpax9b/builds/LBtIxBI6EjxwTknin/openapi.json
