Microsoft Ads Library Scraper - Competitor Ads
Pricing
from $0.96 / 1,000 ads
Microsoft Ads Library Scraper - Competitor Ads
Scrape the Microsoft Ads Library, the public EU archive of ads shown on Bing and the Microsoft Advertising Network, by keyword, advertiser or ad id. One row per distinct ad, charged once: ad copy, landing URL, first and last shown, impressions, countries, payer, targeting. JSON, CSV, API.
Pricing
from $0.96 / 1,000 ads
Rating
0.0
(0)
Developer
Automation Craft
Maintained by CommunityActor stats
0
Bookmarked
3
Total users
2
Monthly active users
4 days ago
Last modified
Categories
Share
Microsoft Ads Library Scraper - Competitor Ads
Get ads from the Microsoft Ads Library, Microsoft's public EU archive of the ads shown on Bing and across the Microsoft Advertising Network, as clean rows. Search by keyword, by advertiser or by ad id; each distinct ad is one row and is charged once per run, however often the archive repeats it in its result pages. With details on, every ad also carries who paid for it, the first and last day it was shown, the impressions band with numeric bounds, 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, and a free row for everything the archive does not have.
Quick start
- Enter searchTerms (keywords such as
running shoes), advertisers (a name such aseBay Marketplaces GmbH, an advertiser id or an advertiser page URL), adIds (ad ids or ad page URLs), or any mix. - Optional filters: countries (EU and EEA), startDate and endDate (the last 30 days when left out), adFormat (text ads or ads with an image).
- Switch on includeDetails for dates, impressions, countries, payer and targeting on every ad.
- Set maxAds (100 when left out) and run. Export the dataset as JSON, CSV or Excel, or call the Actor from the API, n8n, Make or a schedule.
- For a recurring watch give a memoryName: later runs deliver only 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 90 seconds and 5.8 cents.
What you get
One row per distinct ad (type: ad). Fill rates were measured on the live archive on 2026-10-06 (150 detail records from 7 searches, 673 advertisers).
| Field | What it is | Filled |
|---|---|---|
adId, adUrl | The ad's id and its page in the Microsoft Ad Library | 150 of 150 |
advertiserName, advertiserId, advertiserUrl | The advertiser as the archive names it, its id and its library page | 150 of 150 |
title, description | The ad's headline and description text | 150 and 147 of 150 |
displayUrl, landingUrl, landingDomain | The URL the ad displays, the URL it links to and that URL's host | 150 of 150 |
imageUrl, imageUrls, assets, hasImage | Image assets with width and height, when the ad has any | 84 of 150 |
paidBy, paidByDiffers | Who paid for the ad; the archive names a payer only when it is not the account owner (28 of 150), otherwise the advertiser is given | with details |
firstShown, lastShown, daysShown, daysSinceLastShown | First and last day the ad ran in the EU or EEA | 150 of 150 with details |
impressionsRange, impressionsMin, impressionsMax | The archive's band of total impressions (10K - 25K) and its bounds as numbers | 150 of 150 with details |
impressionsByCountry, topCountry, topCountryShare, countriesShown | Impression share per country with ISO codes, largest first | 150 of 150 with details |
targeting, targetingTypes, exclusionTypes | The kinds of targeting used (Location, Age, Gender, MicrosoftAudiences, AdvertiserAudiences) and whether each was used to exclude | 150 of 150 with details |
restricted, restriction | The archive's restriction record for an ad it stopped serving | 0 of 150 with details |
advertiserCountry, advertiserVerified | The advertiser's registered country and identity verification, with includeAdvertiserProfile | 673 of 673 advertisers |
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 archive's own count, pages read, rows read, repeated rows, distinct ads, ads delivered, the stop reason,cappedandcomplete.status, one per miss or refusal: an ad id the archive 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 archive 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.advertiserrows come from the advertiser register search: name, id, country, verified status and library page.
How much does it cost to scrape the Microsoft 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 archive 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 archive does not have, a search term the archive's search refuses, a call the archive 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 archive does not hold meets it after about 20 seconds, and the inputs not started are named in one free
skippedrow 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 (5 pages) cost $0.126; 1,000 ads from searches under the archive's cap (42 pages) cost $1.2208, with details $2.7208; 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 archive 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 archive's search accepts unaccented Latin letters, digits, spaces and `# $ & ' * - . ? @ ^ _ |
advertisers | Names, advertiser ids or advertiser page URLs. 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; 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 archive 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 archive. |
adFormat | all, text (no image asset) or image (at least one image asset). |
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 archive 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 archive answers at most 1,000 results per search, 24 per page, and repeats ads inside a large result: a search at that cap returned 1,008 rows holding 555 to 574 different ads in four measured walks. This Actor delivers each ad once and counts the repeats in the coverage row. The coverage row says capped: true when the archive reported 1,000 results, and complete: true only when every page the archive 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 archive 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 archive counts any impression in a country.
Microsoft limits how often the archive 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 archive answers "too many requests". The Apify platform's address is shared with other runs, and the archive refused about one request in four in our measurements, each cleared by a wait of a few seconds. Measured: 100 ads without details take about 15 seconds; 60 ads with details took just under 4 minutes (about 4 seconds per ad), so 1,000 ads with details take about an hour. Some first answers of the archive take 20 to 30 seconds; a search the archive 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 archive 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 archive did not return is delivered without it (flagged in detailsStatus) and remembered; look it up by ad id in a run without a memory.
What this Actor does NOT do
- No search by landing page domain: the archive 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_matchedrow and the coverage row'sarchiveCount. - 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 Microsoft's archive 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 archive publishes target types and an impressions band only.
- No ads outside the EU and EEA, none before June 2023, and none whose last impression is more than a year old: the archive does not hold them.
- No search terms with accented letters, non Latin scripts or most punctuation: the archive's search refuses them.
FAQ
How can I see my competitors' ads on Bing and Microsoft Advertising?
Search the Microsoft Ads Library by the competitor's name in advertisers, or by a brand or product keyword in searchTerms. Each ad comes back with its headline, description, display URL and landing URL, and with details on also the days it ran, its impressions band and the countries it was shown in. The archive covers ads shown in the EU and EEA.
Does Microsoft have an ad library, and does it have an API?
Yes. The Microsoft Ad Library at adlibrary.ads.microsoft.com is Microsoft's public archive under the EU Digital Services Act, and Microsoft documents a public API for it that needs no key and no login. This Actor reads that API, removes the repeated ads, decodes the detail record into flat columns and gives you one dataset for many searches.
Which ads does the Microsoft Ads Library include?
Ads served on Bing and through the Microsoft Advertising Network that received impressions in the EU or EEA since June 2023. An ad stays in the archive for one year after its last impression, and a new ad appears one to two days after its first one. Ads shown only outside the EU and EEA are not in it.
Can I export ads from the Microsoft Ads Library 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.
Can I sort or filter the ads by impressions?
The archive has no sort order and publishes impressions as a band. With details on, every row carries impressionsMin and impressionsMax as numbers, so you can sort and filter the dataset by them, and topCountry with its share.
Why does this Actor run with limited permissions?
It needs nothing else. It reads the public archive 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
curl -X POST "https://api.apify.com/v2/acts/automation_craft~microsoft-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}'
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: 'YOUR_TOKEN' });const run = await client.actor('automation_craft/microsoft-ads-library-scraper').call({advertisers: ['eBay Marketplaces GmbH'],startDate: '7 days',maxAdsPerSearch: 50,memoryName: 'ebay watch',});const { items } = await client.dataset(run.defaultDatasetId).listItems();console.log(items.filter((row) => row.type === 'ad').length, 'new ads');
from apify_client import ApifyClientclient = ApifyClient("YOUR_TOKEN")run = client.actor("automation_craft/microsoft-ads-library-scraper").call(run_input={"adIds": ["72361740039047", "https://adlibrary.ads.microsoft.com/ad/71056221548107"],})for row in client.dataset(run["defaultDatasetId"]).iterate_items():if row["type"] == "ad":print(row["adId"], row["advertiserName"], row.get("impressionsRange"), row.get("firstShown"), row.get("lastShown"))
Changelog
See the Changelog tab. Version 0.1 (2026-10-06): first release.
More data tools by Automation Craft
Ad libraries: Meta Ad Library Scraper - All Placements, Filters, Facebook Ads Library Scraper - Page Ads, No Login, Instagram Ads Library Scraper - Creatives, Video, Google Ads Transparency Scraper - Decoded Creatives, Google Ads Library Scraper - Competitor Ads, No Login, YouTube Ads Scraper - Video Ads by Advertiser, TikTok Ads Library Scraper: EU Ads, No Login, TikTok Creative Center Scraper: Top Ads, No Login, LinkedIn Ad Library Scraper: Ads by Company, Pinterest Ads Library Scraper - EU Ads Repository.
Other data: LinkedIn Company Scraper - Details, No Login, Trustpilot Reviews Scraper: Coverage Per Company, Google Trends Scraper - Compare and Trending Now, Yahoo Finance Scraper: Quotes, History, Financials, SEC Form D Scraper: Offerings & Funding Leads.