Google Ads Video Transcript Scraper — Transcription & Hooks
Pricing
from $8.00 / 1,000 video ad transcripts
Google Ads Video Transcript Scraper — Transcription & Hooks
Google Ads Transparency Center video ads as transcripts with the first-3s hook and advertiser metadata, one row per ad — by advertiser, domain, YouTube ad URL, pasted ad link or chained dataset. No start fee. Charged only when a transcript is delivered; removed and music-only ads are uncharged rows.
Pricing
from $8.00 / 1,000 video ad transcripts
Rating
0.0
(0)
Developer
Steadyfetch Team
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
16 hours ago
Last modified
Share
Give it an advertiser name or a domain and get their Google Ads Transparency Center video ads back as text. Every ad comes back with its full transcript (caption track first, speech recognition when there is none), the first-3-seconds hook, and the advertiser and creative metadata, one JSON row per ad. From $8.00 per 1,000 video ad transcripts on the Business plan ($20.00 on the free plan), platform usage included, no start fee. You are charged only when a transcript lands in your dataset: removed videos, music-only creatives and media-less rows cost $0.
Just want to see it work? Click Start with nothing set and the run is a 3-ad sample — HelloFresh's US video ads, transcribed and charged like any run (about $0.06 on the free plan, $0.024 on Business). The sample always runs live against the Transparency Center, so pressing Start again looks those ads up again and charges for them again — a few cents. Changed only a setting — the region, a cap, New ads only, a watchlist name — and named nothing? Those settings are applied to the same sample and the run goes ahead, charged like any run, with one uncharged sample_note row saying which settings were yours; a cap smaller than the sample is honoured exactly, a bigger one never grows it. You can also paste YouTube ad video URLs or ad page links copied from the Transparency Center, or chain it straight after any Google Ads scraper run. Running the same ads again? An ad already delivered to your account is handed back from that run and not charged a second time — automatically, whatever mode found it. Watching competitors over time? Name a watchlist and re-run on a schedule: with New ads only you get only what is new (see Watch competitors on a schedule).
| You give it | You get back |
|---|---|
| Advertiser names (or AR… IDs) | transcript — the ad's full text, caption track first |
…or advertiser domains (e.g. nike.com) | hook3s — the ad's opening line, plus hookStartSeconds |
| …or YouTube ad video URLs / IDs / Transparency Center ad page links | advertiserName, creativeId, language, durationSeconds, timestamped segments |
| …or a Google Ads scraper run's dataset ID | one JSON row per video ad, misses clearly marked and uncharged |
| …plus a watchlist name (optional) | a separate list per advertiser set — and, with New ads only, nothing you already paid for |
| …the same ads again, in any mode | ads already delivered to your account come back from that run, repeat: true, not charged |
Output
One JSON row per ad. Delivered rows carry the transcript and charged: true; anything that could not be delivered carries a status saying why, and charged: false. Real rows from the live example run below (long text trimmed with … for the page):
Picking out the rows you want: filter on the CONTENT — transcript != null, or charged == true — rather than on a status string. status is a human-readable label and this actor adds new ones as it learns to explain more (uncharged notes included), so a filter pinned to one string can quietly go short as the vocabulary grows. Delivered rows are exactly the rows with a transcript, and they are exactly the rows that are charged.
| status | charged | advertiserName | creativeId | hook3s | hookStartSeconds | transcript | transcriptSource | language | durationSeconds |
|---|---|---|---|---|---|---|---|---|---|
no_media | false | HelloFresh SE | CR16734017339195916289 | null | null | null | null | null | null |
transcribed | true | HelloFresh SE | CR05578336217335857153 | Vos soirées ne devraient pas être une | 2.2 | Vos soirées ne devraient pas être une épreuve. Rentrez à la maison et l'eau fraîche vous attend. Des recettes faciles à cuisiner… | captions | fr | 30 |
transcribed | true | HelloFresh SE | CR03965484600783798273 | Rentrez à la maison H et HLE FRE vous attend. | 0.3 | Rentrez à la maison H et HLE FRE vous attend. | captions | fr | 6 |
The first row is a creative that exposes no fetchable video: it ships with its advertiser and creative metadata and is not charged.

Full row, as JSON:
{"status": "transcribed","charged": true,"advertiserName": "HelloFresh SE","creativeId": "CR17857019577632817153","videoId": "ONzIwHyQGJs","videoUrl": "https://www.youtube.com/watch?v=ONzIwHyQGJs","hook3s": "What if your food was smarter?","hookStartSeconds": 0,"transcript": "What if your food was smarter? If your meals were packed with the nutrition you need...","transcriptSource": "captions","language": "en","durationSeconds": 15,"segments": [{ "start": 0, "end": 2.1, "text": "What if your food was smarter?" }],"chargeEvents": { "transcript": 1, "surchargeMinutes": 0 }}
Browse the full live example dataset → — real output from a verified run, including the uncharged rows. Export with clean=true for spreadsheet-ready CSV; reconcile your invoice from the per-row charged flags immediately, or from the run's charged-event counter after it settles (~12 s).
Why this US-region sample carries French creatives: HelloFresh has no US-registered advertiser in the Transparency Center — the entity running the ads is HelloFresh SE, registered in Germany. Advertiser names resolve among advertisers registered in your region first; only when none exists (as here) does the run fall back to the global match — and its status line tells you so. Ordinary spellings of region work (US, us, USA, United States); a region this actor cannot search is never swapped for another market — the advertiser and domain walks stop with uncharged rows naming the field and every target they did not search, while ad links you pasted are transcribed as asked. maxItems counts attempted ads, an uncharged miss included; rows answered before any attempt (removed ads, media-less creatives) never count against it.
A name matches advertiser names, not ad text. Google's Transparency Center has no ad-text or topic search, so "Advertisers" is matched against advertiser names — and a big brand shares its name with unrelated advertisers. Nike matches ten advertisers registered in the US alone, one of which runs ~9,000 ads and the rest a handful each. The name resolves to the one running the most ads in your region; set maxAdvertisersPerName above 1 to also cover the next-largest namesakes (largest first), and the status line names exactly which advertisers were opened and how many ads each has. Finding advertisers is never charged. For full coverage of a brand that runs under several accounts, search by domains instead — a domain spans every advertiser verified for it.
Every field is always present (explicit null over silent omission), and title is filled on uncharged rows too whenever YouTube exposes it — a miss is never a bare video ID. Non-delivered rows carry status + statusReason instead — no_speech (music-only or silent ad — common for big-brand creatives that carry their message in on-screen text, which this actor does not read; see the FAQ), unavailable_removed, unavailable_expired (a signed media link expired), no_media (the creative exposes no fetchable video), audio_too_long_for_speech (no caption track, and the ad is too long for the speech-to-text route — see the FAQ), no_videos_found, no_creative_found, advertiser_not_found, domain_not_listed, input_error, blocked (YouTube served a bot challenge — re-run later), source_blocked (the Transparency Center stopped answering — wait and re-run), skipped_too_large (per-item data cap), blocked_by_robots (robots.txt kill-switch), asr_unavailable (our speech-to-text service refused this actor's access mid-run — that is on us; captioned ads still deliver, try again later), or failed_* (also listed in the run's ERRORS record). None of them are charged. Rows a re-run can genuinely retry — blocked, source_blocked and the failed_* rows that say so — also carry retryable: true; a permanent verdict never does, so you can filter on that field to feed a re-run.
Every row carries "isNew": true for an ad your account had never answered before, "isNew": false for one it had, and "firstSeenAt", the moment it was first answered (this run's clock for a new ad, the original sighting for an old one). An ad you already had comes back with "repeat": true and "firstSeenRunId" naming the run that first delivered it, "charged": false, and its statusReason saying so.
Watch competitors on a schedule — pay only for new ads
You never pay for the same ad twice (the default sample aside — see the end of this paragraph). Every run remembers the ads it answered for your account — in a key-value store called google-ads-video-watch-account in your own Apify account — under the ad's identity (the YouTube video ID, or the Transparency Center creative ID). Run the same links, advertisers, domains or chained rows again and the ads you already have come back from the run that delivered them: repeat: true, firstSeenRunId, not looked up, not transcribed, not charged. The status line counts them (2 of these you already had: handed back, not charged) and OUTPUT.repeats holds the number. Delete that store to forget everything; entries older than 90 days are no longer treated as repeats. The one exception is the default sample — a Start with nothing set, or with only settings set: it always runs live, so it is neither read from nor written to that memory, and a second bare Start pays for its ads again. Every run that names its own advertisers, domains, ad links, dataset or video URLs is covered exactly as above.
Competitor research is rarely a one-off: the question is usually what did they launch since last week? A watchlist answers that with only the new ads.
- Put the advertisers you follow in Advertisers (or their domains, pasted ad links, or a chained scraper run — any input mode works).
- Give the run a Watchlist name, e.g.
acme-competitors, and turn on New ads only. - Save the input as a Task, put the Task on an Apify Schedule (daily works well for active advertisers), and add an integration or a webhook on run succeeded — a Google Sheet, Slack, n8n, Make, or your own endpoint.
{"advertisers": ["HelloFresh", "Blue Apron"],"region": "US","watchlistId": "meal-kits","newAdsOnly": true}
Each scheduled run then delivers only the ads the list has not answered before, each with isNew: true and its firstSeenAt. Ads already on the list are skipped before anything is fetched — not downloaded, not transcribed, not charged — and the run's status line says exactly what happened, for example: watchlist "meal-kits": 3 new ads this run, 27 already on the list were skipped (not fetched, not charged); 30 on the list now. The same numbers sit in the run's OUTPUT record under watchlist, newAds and skippedSeen.
Some details worth knowing:
- The list lives in your account, in a named key-value store called
google-ads-video-watch-<your name>(one record,SEEN). Open it in Storage → Key-value stores to see every ad the actor has answered under that name, or delete the record to start the list over. This actor runs with limited permissions and touches nothing else in your account. - Only answered ads go on the list. A delivered transcript counts, and so does a final uncharged verdict such as no speech. A failed download, a page that could not be fetched this minute, or an ad your cost cap left out is not remembered, so the next run tries it again.
- Leave New ads only off and every ad runs as usual — you still get
isNew/firstSeenAton each row, which is the way to build a full "what is running now" picture while still knowing what is fresh. - New ads only without a watchlist name compares against your account's memory instead: ads already delivered to your account are skipped before anything is fetched. Only when neither a list nor the memory can be read does the run stop with one uncharged row, instead of charging you for ads you may already have. A named watchlist it cannot open stops the run the same way, before any spend.
- If your account's memory cannot be read, the run still runs: it delivers and charges as usual, and the status line and the charged rows say the repeat check was unavailable (
repeatCheck: "unavailable"). - One ad, one sighting, however it arrives — the same video surfaced under two advertiser lanes, or pasted as a link and found by name, is one entry and one charge.
For AI agents & LLMs — copy this block
Actor: steadyfetch/google-ads-video-transcript-scraper (Apify Store)Does: Google Ads Transparency Center video ads -> full transcript +first-3-seconds hook + advertiser/creative metadata. One JSON row per ad.Caption-track first, speech recognition as fallback. No Google login.Run: POST https://api.apify.com/v2/acts/steadyfetch~google-ads-video-transcript-scraper/run-sync-get-dataset-items?token=APIFY_TOKENInput (any ONE of the first four fields is enough):{"advertisers": ["HelloFresh"], // advertiser names or AR… advertiser IDs, or an// adstransparency.google.com/advertiser/AR… page link"domains": ["nike.com"], // every advertiser verified for the domain"videoUrls": ["https://www.youtube.com/watch?v=ONzIwHyQGJs"],// YouTube watch URLs, bare 11-char video IDs, or// .../advertiser/AR…/creative/CR… ad page links"datasetId": "<dataset id of any Google Ads Transparency scraper run>","watchlistId": "meal-kits", // optional: remember answered ads under this name (your own account)"newAdsOnly": false, // with a watchlistId: skip ads already on the list before any lookup"region": "US", // optional, default US"maxItems": 100, // optional cap"maxAdvertisersPerName": 1 // optional; >1 also opens the next-largest// same-name advertisers, ranked by ad count}Nulls every optional field above also accepts an explicit null, meaning "use thedefault" — so a template that renders unset fields as null (n8n, agentframeworks, chained workflows) runs exactly like one that omits them.Note: names are matched against ADVERTISER NAMES — the Transparency Centerhas no ad-text or topic search.Output one row per ad: status, charged, isNew, firstSeenAt, advertiserName, creativeId, videoId, videoUrl,title, hook3s, hookStartSeconds, transcript, transcriptSource, language,durationSeconds, segments[]Pricing pay per event, platform usage INCLUDED, no start fee:video-ad-transcript $0.020 -> $0.008 per delivered transcript (falls with your Apify plan tier)long-video-surcharge $0.005 per started minute past the first 3 minutesCharged only on delivery — removed ads, music-only ads and failures cost $0.Ads skipped by a watchlist (newAdsOnly) are never charged.Cap any run with maxTotalChargeUsd (minimum $0.05).MCP: pin this actor in any MCP client with https://mcp.apify.com?tools=steadyfetch/google-ads-video-transcript-scraper(or ask Apify's MCP server for "google ads video transcripts").
Why this Google Ads transcript scraper?
- One hop from advertiser to transcripts. No other tool goes name → video ads → what they actually say in a single run. Most Ads Transparency Center scrapers stop at metadata and video links.
- Charged only on delivery. A transcript either lands in your dataset or the row is uncharged. Removed video → an uncharged row that says so. Music-only ad → an uncharged row that says so.
- Old datasets still work. YouTube video IDs don't expire the way ad-CDN links on other platforms do — you can transcribe a dataset scraped weeks ago. Ads that have since been removed come back as uncharged
unavailable_removedrows. - Any length on the caption route. Long-form ads transcribe fine — videos past 3 minutes add a small per-started-minute surcharge instead of failing or being cut off. The speech-to-text fallback, which only runs when an ad has no caption track at all, works on short ads: past a few minutes YouTube stops releasing the audio to anything but its own player, so a long caption-less ad comes back
audio_too_long_for_speechand uncharged rather than half-transcribed. Ads that carry captions — the large majority, and nearly every spoken one — are unaffected at any length. - Hook as data, and never blank.
hook3sis the ad's first 3 seconds of speech — the line media buyers study — as its own field, with full segment timestamps beside it. Ads that open on music or a logo sting are common, so when nothing is said in the first 3 seconds you get the first 3 seconds of speech from wherever speech actually begins, andhookStartSecondstells you which second that is (0when the ad opens speaking). An ad with no speech at all leaves bothnull— this field is never an empty string, so "the ad opens silent" and "the ad opened talking, we lost it" can't be confused. Sort byhookStartSecondsto see which competitors make you wait for the pitch. - Works with any scraper's output. The dataset input deep-scans your rows for YouTube ad video links — no field mapping, no glue code.
This actor may fail when the platform changes things — failed items are never charged.
How to transcribe Google video ads (no code)
- Type an advertiser name (e.g.
HelloFresh) into Advertisers — or paste your Google Ads scraper run's dataset ID. - Click Start. The actor finds the advertiser's video ads in the Ads Transparency Center and transcribes each one.
- Each video ad comes back as one JSON row: transcript + hook + advertiser metadata.
- Export as JSON/CSV, or read it via API.
Prefer URLs? Paste YouTube watch URLs or bare 11-character video IDs into videoUrls directly. Browsing the Ads Transparency Center and found an ad worth transcribing? Copy the page link from your browser (adstransparency.google.com/advertiser/AR…/creative/CR…, any ?region= value) and paste it into videoUrls — the ad is looked up and transcribed directly. An advertiser page link (without /creative/…) goes in Advertisers and transcribes that advertiser's video ads.
Came from a sister transcript actor and kept its field name? A list sent as urls is read as videoUrls, so the run goes ahead instead of turning you away, and one uncharged alias_note row says which field name was used and which one this actor actually has. Nothing is renamed and nothing is charged for that row — using videoUrls simply leaves it out.
How much does it cost to transcribe Google ads?
One result = one ad's full transcript payload (transcript + hook + metadata + segments). The first 3 minutes of each video are included; longer videos add a small per-started-minute surcharge. No start fee, no subscription, no third-party API key needed.
| Job | Approx. cost |
|---|---|
| 50 competitor video ads | ≈ $1.00 |
| 500 ads (a serious creative teardown) | ≈ $10 |
| Apify free plan ($5 credit) | ≈ 250 ad transcripts |
Platform usage (compute + transfer) is included in the event price — the per-transcript price you see is the whole price. Failed or empty fetches charge no result fee.
Works with — Google Ads scrapers you can chain from
The dataset input reads the YouTube video link value in each row (watch URLs, youtu.be links, or a videoId field), not a fixed field name — so it works with any Ads Transparency Center scraper that exports video links, including:
| Scraper | Chain by |
|---|---|
| silva95gustavo/google-ads-scraper | dataset ID or pasted rows |
| lexis-solutions/google-ads-scraper | dataset ID or pasted rows |
| solidcode/ads-transparency-scraper | dataset ID or pasted rows |
| Any scraper whose rows carry the ad's YouTube link | dataset ID or pasted rows |
How to chain: run the scraper with video ads in scope → copy the run's dataset ID into this actor's Dataset ID field (or use Apify's Connect Actor integration) → Start. Rows without a YouTube link come back as uncharged no_creative_found. You can also skip the other scraper entirely — this actor enumerates advertisers itself.
Want the text of an advertiser's image and text ads too? Pair this with Google Ads Creative Text Scraper — same input, extracts headlines, body copy, and CTAs from the non-video creatives.
Use it via API, MCP, and integrations
- API: standard Apify run API —
POST .../acts/steadyfetch~google-ads-video-transcript-scraper/runswith{ "advertisers": ["HelloFresh"] }. Python/Node clients work as with any actor. - MCP: one link pins this actor in Claude, Cursor, or any MCP client —
https://mcp.apify.com?tools=steadyfetch/google-ads-video-transcript-scraper— or ask Apify's MCP server for "google ads video transcripts". - n8n: a free ready-made template, no community nodes needed — Google Ads video transcripts (one advertiser or domain → hooks & transcripts → spreadsheet-shaped rows, 3-minute setup), listed on our profile website (the link on our Apify profile page).
- Make / Zapier: call it as a regular Apify actor step right after your scraper node, or run it standalone with advertiser names.
- Schedules + webhooks: save any input as a Task, schedule it, and use the Task's Integrations tab to push each run's new rows wherever they need to go.
FAQ
How does the watchlist know what I have already seen? Every ad the actor answers under a watchlist name is written to a key-value store in your own account (google-ads-video-watch-<name>), keyed by the ad's YouTube video ID (or its Transparency Center creative ID for an inline video). The next run with that name reads the list first. Delete the store's SEEN record to start over. Nothing is stored anywhere else.
Does a watchlist run charge for the ads it skips? No. With New ads only on, an ad already on the list is dropped before anything is fetched; the status line and OUTPUT.skippedSeen count them, and no row and no charge is produced for them.
Where do the videos come from? Google's own Ads Transparency Center — the public surface Google publishes so anyone can see who runs which ads. Most Google video ads are hosted on YouTube; this actor transcribes those, plus inline video creatives where the media is fetchable.
What's transcriptSource? captions means the ad's own caption track was used (fast, exact); asr means the audio was transcribed with speech recognition. Both count as one transcript — same price, same fields.
An ad came back audio_too_long_for_speech? That ad has no caption track, so the only route left was speech-to-text on its audio — and past a few minutes YouTube stops releasing a video's audio to anything but its own player. We can't reach enough of it for a complete transcript, and a partial transcript is never sold as a whole one, so the row is uncharged and says so plainly instead of telling you to re-run something that can never succeed. Short caption-less ads transcribe as before, and captioned ads are unaffected at any length.
Music-only ads? Speech-recognition models hallucinate filler on music. We detect that and return an honest, uncharged no_speech row instead of selling you a fake transcript. Expect these often from big brands: their video ads frequently carry the whole message as music plus on-screen text with no voice-over at all. This actor transcribes speech and does not read on-screen text, so such an ad comes back as an uncharged row with its title, advertiser and video link — and the run's status line says how many of your misses had that shape.
Some video creatives come back no_media? A minority of Ads Transparency Center video creatives expose no fetchable video stream (they render as static previews or script-only embeds). Those rows ship uncharged with their advertiser/creative metadata and are clearly marked.
An advertiser returned no_videos_found? Video inventory is lumpy — many large advertisers run only text and image ads in a given region. Try another region code or advertiser. The row is uncharged.
Typos in bare video IDs? A mistyped 11-character ID is indistinguishable from a real one, so it surfaces as an honest unavailable_removed row rather than an input error.
Is it legal to transcribe Google ads? The Ads Transparency Center is public by design (ad transparency). As always, how you use the data is on you.
Is this an official Google product? No. This is an independent actor from steadyfetch — unofficial, and not affiliated with, endorsed by, or sponsored by Google LLC. "Google", "Google Ads" and "YouTube" are trademarks of their respective owners, used here only to name the public Ads Transparency Center surface this actor reads.
Steadyfetch ad-intelligence suite
One actor per ad surface, one job each, the same contract everywhere: all-inclusive pay-per-event, no start fee, charged only on delivery — and the same row shape, so a workflow built for one platform ports to the next.
Ad creatives → transcripts & on-creative text
| Ad surface | Actor | Free n8n template |
|---|---|---|
| Facebook & Instagram Ad Library | Facebook Ads Transcript Scraper — Ad Library Video & Image Ads | Facebook ad transcripts · Competitor ad teardown |
| Google Ads Transparency Center | this actor | Google Ads video transcripts |
| TikTok Creative Center Top Ads | TikTok Ads Transcript Scraper — Creative Center Top Ads & Hooks | TikTok Top Ads transcripts |
| LinkedIn Ad Library | LinkedIn Ads Scraper — Ad Library Transcripts & Image Ad Text | LinkedIn Ad Library transcripts |
| Instagram Reels (organic posts) | Instagram Reel Transcript Scraper — Reels to Text with 3s Hooks | Instagram Reel transcripts |
Ad creatives → text (image & text ads, read by OCR)
| Ad surface | Actor |
|---|---|
| Google Ads Transparency Center | Google Ads Transparency Scraper — Ad Copy, CTAs & Image OCR |
Free n8n workflow templates for this actor are listed on our profile website (link on our Apify profile).
The rest of the steadyfetch shelf — same contract everywhere: all-inclusive pay per event, no start fee, charged only on delivery.
| Family | Actors |
|---|---|
| Ad creative intelligence | Facebook · Google Ads video · TikTok · LinkedIn · Google Ads text & OCR |
| Trends & keywords | Google Trends · Trends Now · Breakout keywords · Autocomplete keywords · Keyword volume & CPC · Social trends |
| YouTube transcripts | YouTube videos · YouTube channels |
| Reel transcripts · Profile posts | |
| Jobs | Indeed · Career sites by domain · Glassdoor · Multi-board · Google Jobs |
| Amazon | Products · Search · Bestsellers · Sellers |
| Any media file | Speech to Text · any link or file |
Feedback & support
Found an issue? Open it on the Issues tab — we usually reply within a couple of hours, always within a day. Feature requests welcome. And if this actor earned its keep, a rating helps other buyers find it, and saving it keeps it one click away.