Google Ads Transparency
Google Ads Transparency Search API
Every ad Google is running for one advertiser, straight from its own Ads Transparency Center: the creative itself (archived image, rich-media bundle and Google's renderer link, with dimensions), the advertiser id and name, the format, the first and last dates it was seen and the number of days it actually ran. Filter by region, format, surface or political topic, and page through the whole library with next_cursor at 100 ads a page. Costs 1 credit per page. Resolve the advertiser_id with the advertiser lookup endpoint first.
Authorizations
AuthorizationstringheaderrequiredBearer authentication header of the form Bearer <token>, where <token> is your Scavio API key (e.g. Bearer sk_live_your_key).
Body
application/jsondomainstringAdvertiser website, 1-253 characters. Accepts a bare host, a www. host or a full URL — it is reduced to the registrable host Google indexes. One of domain or advertiser_id is required. Querying BY DOMAIN is also the only way to get the domain field back on each row; an advertiser_id query drops it entirely. A domain can span several advertiser entities, so the rows come back under different advertiser_ids.
Example: nike.com
advertiser_idstringGoogle advertiser id, 3-40 characters, as returned by /api/v1/googleads/advertisers and on every row here. One of domain or advertiser_id is required. Its shape is checked before any request goes out, so a typo costs nothing.
Example: AR16735076323512287233
regionstringTwo-letter ISO country code (US, GB, DE), or a Google geo criteria id as a string ("1014044") for a sub-country region. Filters to ads actually shown there AND scopes the deep links on every row — the same advertiser can share ZERO creatives between two countries, so this is a real filter, not a formatting hint. Defaults to no region filter (worldwide).
Example: DE
formatenum<string>Creative format. The three sets are DISJOINT — an advertiser's text, image and video ads share no creatives, so this partitions the library rather than narrowing one list. Defaults to all formats.
Example: video
text— Text ads. These carry an archived image_url plus width/height.image— Display and rich-media ads. Usually preview_url or iframe_url rather than image_url.video— Video ads.
platformenum<string>Google surface the ad ran on. Defaults to all surfaces.
Example: youtube
play— Google Play.maps— Google Maps.search— Google Search.shopping— Google Shopping.youtube— YouTube.
topicenum<string>default:allAd topic. political narrows to ads Google classifies as election advertising — those are the ones carrying a funder disclosure on the creative endpoint.
all— Every ad in scope (the default).political— Election ads only.
limitintegerdefault:40Ads per page, 1-100. 100 is a HARD UPSTREAM CEILING, not a policy of ours: Google answers a larger request with ZERO rows rather than an error, and still bills for it. To read past 100, page with cursor.
Example: 100
cursorstringPagination token, 1-4000 characters. Pass the next_cursor from the previous response to fetch the next page, RE-SENDING THE SAME FILTERS alongside it. Omit for the first page. next_cursor comes back null once the advertiser's ads in scope are exhausted — that is the stop signal.
Request
from scavio import ScavioClient
client = ScavioClient(api_key="sk_live_your_key")
# Walk the whole library: same filters on every page, stop on a null cursor.
cursor = None
while True:
page = client.google_ads.search(
domain="nike.com",
region="DE", # EEA region: impressions and first_shown are published here
limit=100,
cursor=cursor,
)
for ad in page["data"]["creatives"]:
print(ad["creative_id"], ad["format"], ad["days_shown"])
cursor = page["data"]["next_cursor"]
if not cursor:
breakResponse