DEV Community

Devil Scrapes
Devil Scrapes

Posted on

Bing's ad-library search box accepts any query and returns the exact same ads

Quick answer

Microsoft's Bing/Microsoft Advertising Ad Library exposes a query parameter on its /Ads endpoint that looks exactly like a free-text ad-content filter — the kind you'd expect on any ad-transparency search box. Pass it anything. Pass it nothing. The results are identical. We verified this directly against the live endpoint with chrome131 and firefox133 fingerprints, byte-identical both ways: query is silently ignored, and the endpoint just falls back to whatever unfiltered feed it was already going to serve. The only parameter that actually constrains the result set is advertiserId, a numeric id you have to resolve first through a separate lookup, GET /Advertisers?searchText=<name>. The Bing Ad Library Scraper is built on that two-step path — resolve, then page — and deliberately doesn't expose query as an input at all.

What would happen if you built on the obvious parameter?

You'd ship something that looks correct in every quick test and is wrong for every real customer query. Search Bing's public UI for "nike," get a page of ads. Search for "adidas," get a different-looking page. It reads like filtering is happening — until you notice both searches, and every other search, would be pulling from the same underlying default feed regardless of what you typed, and the apparent "difference" between them is just page order or timing, not a query actually being applied. A scraper wired to query wouldn't error, wouldn't 4xx, wouldn't log anything unusual. It would just return the same set of rows for "nike" and "totally-unrelated-brand-xyz," and the only way anyone finds out is by noticing two supposedly different searches produced suspiciously overlapping data.

This is the class of bug that survives a demo. The endpoint answers, the JSON parses, the fields are all present — every signal you'd normally check says "working." The only thing that catches it is deliberately trying to break it: sending a query that should return nothing, or two unrelated queries that should return disjoint results, and checking that they actually do.

So what's the real filter?

A resolve-then-paginate path, not a search-string. GET /api/v1/Advertisers?searchText=<name> is what actually narrows anything — it returns one or more numeric AdvertiserId matches for a name. Only then does GET /api/v1/Ads?advertiserId=<id>&top=24&skip=<n> return ads that are genuinely scoped to that specific advertiser, paginated 24 at a time. The Actor's input is an advertiser name, not free text against ad content, because free text against ad content is the one thing this API doesn't actually support — no matter how convincingly its own parameter name suggests otherwise.

An API parameter that accepts your input without complaint isn't proof it's doing anything with that input. The only way to know a filter is real is to send it two inputs that should produce different results and check that they do.

What the Actor gives you

Pass one or more advertiser names; each is resolved to its real advertiser id(s) behind the scenes, capped by how many matches per name you want fetched, then every matching ad is paged and deduplicated by ad id — because Bing's own paginated feed overlaps between adjacent pages, so a naive page-count-times-page-size total would overcount. Each row carries the ad id, advertiser id and name, title, description, display and destination URLs, and creative assets. One advertiser name that resolves to nothing, or has zero ads on file, doesn't sink the rest of your list — each name succeeds or fails independently, and a malformed or empty creative-asset payload is recorded as an empty list rather than failing the row.

Honest limitations 🚧

This is advertiser-name search only — there is no working ad-content keyword filter on this API, and we don't expose one that would silently do nothing. Coverage is whatever Bing's own ad library has on file for a matched advertiser; there's no separate discovery mode for advertisers you don't already have a name for.

FAQ

Can I search by ad content or keyword instead of advertiser name?
No — Bing's own query parameter on this API doesn't filter anything regardless of input, so we don't expose it. Advertiser name, resolved to a real advertiser id, is the only working filter.

What happens if an advertiser name doesn't resolve to anything?
That name succeeds with zero rows and is named in the run's status; other names in the same batch are unaffected.

Does the dataset ever contain duplicate ads?
No — every ad is deduplicated by ad id even when adjacent pages of the same advertiser's feed overlap.

Do I need a Microsoft Advertising account?
No — this uses Bing's own public ad-transparency API, keyless and login-free.

Pricing

$0.20 per run plus $0.003 per unique ad — $3.20 per 1,000 ads. A run that matches nothing costs only the start fee.

Bing Ad Library Scraper on Apify


Built by Devil Scrapes. We rotate fingerprints, back off on rate limits, and — as this one shows — we test whether a filter actually filters before we build a product around it.

Top comments (0)