Facebook & Instagram Ad Library ads transcribed in bulk: video transcripts with the first-3s hook, plus image-ad text. Search by keyword or advertiser — no ad IDs needed — or paste ad links or chain any scraper. No start fee; expired links, text-free images and music-only ads are never charged.
A value the two dropdowns do not recognise now stops the search instead of quietly running it on the default filter.
Ad format to search for (mediaType) and Ads to include (activeStatus) used to answer an unrecognised value — a typo, a synonym such as "paused" or "gif" — by searching on the default (video / active) and mentioning it in a free row. That still delivered, and charged for, ads matching a filter you had not chosen. Now the search does not run at all: you get one free row naming the value you sent and the options that field accepts, and nothing is charged. Leaving the field out, or sending null, still means the default exactly as before, silently. Pasted ad links, chained dataset runs and direct video URLs are unaffected — neither dropdown applies to them.
1.0.63 — 2026-09-03
Documentation only — nothing changes in your rows, prices, charges or status line.
The example screenshots on this page now load from Apify's own storage, and the references that used to point off Apify are plain text now. The free n8n workflow templates are unchanged and still listed on our profile website. Your dataset, your charges and the status line are unchanged.
1.0.62 — 2026-09-02
Completes the internal cost-accounting change from the previous build — nothing changes in your rows, prices, charges or status line.
A storage fix on our side: runs started from any account now contribute to our internal cost accounting the same way our own runs do. Your dataset, your charges and the status line are unchanged.
1.0.61 — 2026-09-02
Our cost records are now complete for every run — nothing changes in your rows, prices, charges or status line.
Runs started from any account now contribute to our internal cost accounting the same way our own runs do. Your dataset, your charges and the status line are unchanged.
1.0.60 — 2026-09-02
Internal accounting fix — nothing changes in your rows, prices, charges or status line.
A small correction to how our own cost records count very short clips. Your dataset, your charges and the status line are unchanged.
1.0.59 — 2026-09-02
Internal cost accounting only — nothing changes in your rows, prices, charges or status line.
Runs started from our own account now record the processing they used, so our cost reports are measured instead of estimated. Nothing is added to your run or its storage, and your dataset, your charges and the status line are unchanged.
1.0.58 — 2026-09-02
Start with the default form and get a real 3-ad sample instead of a placeholder row.
Clicking Start with nothing set used to return one free "demo" row and no transcript. It now runs a small real sample — the US Ad Library searched for currently running video ads matching "fitness app", up to 3 ads — transcribed and charged like any run, so the first thing you see is the actual output. The status line says it was the sample and how to run your own search. If the Ad Library cannot be searched at that moment, you still get one uncharged row explaining it, never an empty result.
1.0.57 — 2026-09-02
Text-heavy image ads are now read in full, and watchlist timestamps match exactly between your list and your rows.
An image ad carrying a lot of copy — a report-style creative, a wall of bullet points — used to come back as a free "service problem, please re-run" row, and a re-run gave the same answer. The text reader now gives such a creative a second, larger pass, so it delivers like any other image ad. A creative that still cannot be read reliably ships as a free row that says so, never as a charge.
On a watchlist run, the firstSeenAt your list remembers is now byte-for-byte the value on the row you received. Previously the two could differ by a few milliseconds, so a row from the first run and the same ad on a later run did not read as the same instant. Existing lists keep working; nothing is re-charged.
Screenshots in this description load from the right place again.
1.0.56 — 2026-09-01
Watchlists: re-run the same competitors on a schedule and pay only for the ads that are new.
Until now every run was a one-off. Ask for the same advertiser tomorrow and you got — and paid for — the same ads again. Two new fields change that:
Watchlist name (watchlistId) — give the run a name such as acme-competitors and the actor remembers every ad it has answered under that name, in a key-value store in your own Apify account (fb-ads-watch-<name>, yours to inspect or clear). Every row now says whether the ad is new to that list (isNew) and when it was first seen (firstSeenAt).
New ads only (newAdsOnly) — with a watchlist set, ads already on the list are skipped before anything is downloaded: not fetched, not transcribed, not charged. The run's status line and its OUTPUT summary say how many were new, how many were skipped, and how many the list holds now.
Only an ad the actor actually answered goes on the list — a delivered transcript, image text or on-screen text, or a final free verdict such as no speech. An ad it could not answer (a failed download, a link it could not resolve, an ad your cost cap left out) is not remembered, so the next run tries it again. The list is written the moment a row lands in your dataset and before its charge posts, so an interrupted run can never forget an ad you paid for.
Two safety rules: New ads only without a watchlist name does not run at all (the actor cannot tell a new ad from one you already paid for), and a watchlist the run cannot open stops the run before any spend. Both ship as one free, clearly-labelled row.
Without a watchlist name nothing changes: the two new columns read null, and every input mode, price and charging rule is exactly as before.
The README was rewritten at the same time: what you get and the price up front, a scheduling recipe for the watchlist, a comparison with the other ways to get this data, and the one-click MCP link.
1.0.55 — 2026-09-01
The step that reads text off a creative moves to our provider's current engine.
The engine behind image-ad text extraction — and behind on-screen text on silent video ads, if you have that turned on — is being retired by the provider this month, with runs quietly redirected to its replacement two weeks from now. This build makes that move explicitly instead: charged output should never ride a silent switch.
Both engines were run side by side on the same real Facebook and Instagram creatives before this shipped. The same text comes back, and the rule that matters most is unchanged: a creative with no readable ad copy is still a free, honest miss and is never charged — verified on blank frames, product photos with no copy, and b-roll whose only visible text is a logo. On a text-heavy creative the new engine fills in the headline field a little more often. Nothing about what this actor charges changed.
1.0.54 — 2026-08-31
Completes 1.0.53: videos between two and twenty minutes are now covered too.
1.0.53 gave each creative a time budget earned by its own length. On the first live run of that build the very long ads (twenty-one and sixty-three minutes) transcribed correctly for the first time — but ads in the middle of the range, roughly two to twenty minutes, came back as failed_download instead. The new budget was being computed as a fraction of a millisecond, which the underlying process launcher refuses outright; only durations between the fixed lower and upper bounds could produce a fraction, so the very short and the very long ads were unaffected.
Two things changed:
The budget is always a whole number of milliseconds.
Anything that stops the extraction from starting is now reported as an extraction problem, not as a download problem. Before, it read as a network hiccup, so the ad's media was downloaded again — twice more — before the run gave up. One attempt now, one honest uncharged row.
No row was ever charged for either failure. Ads under two minutes behaved correctly throughout.
1.0.53 — 2026-08-31
Long video ads now transcribe. Until this build, an ad much past ten minutes came back as a failure — and the store page's "any video length" promise was not true.
On a 20-ad run of real Facebook video ads, five came back as failed_processing. Every one of them was a long-form creative: 12, 13, 14, 15 and 63 minutes. Nothing under five minutes ever failed. Short ads were never affected, and nothing was ever charged for a failed row — but a quarter of that run was lost, and the rows said nothing a buyer could act on.
The cause was a fixed ceiling on how long this actor would spend pulling the audio out of one creative. It was set once, for short ads, and never grew with the video. A 63-minute ad needs several minutes of that work; it was being stopped after two and reported as a broken file.
What changed
The time budget is now earned by the video's own length, up to a generous ceiling, and it is also held inside what is left of your run. A long ad gets the minutes it needs; a short ad behaves exactly as it always did.
A silent ad is now recognised before any extraction work starts. A video with no audio track has always shipped as a free no_audio row (and, with on-screen text extraction turned on, as an on-screen-text result instead) — that verdict is now reached from the file's own stream list rather than after a failed attempt, so it is faster and cannot be confused with anything else.
"Ran out of time" and "this file is broken" are now two different answers. A run that ran out of time ships an uncharged failed_processing row marked retryable, and the row tells you what to do: re-run it, and give the run more memory — memory is also CPU on this platform, and CPU is what a long video needs.
A stopped extraction can no longer leave a half-finished audio track behind, so a partial transcript can never be delivered or billed as a whole one.
Verified on the exact ads that were lost, including the 63-minute one: full transcript, 1,765 timed segments.
1.0.52 — 2026-08-31
The two dropdown fields now accept null as well, so a template can null every field it does not set.
1.0.51 made an unset field sendable as null — but it could not cover the two dropdowns, Ad format to search for (mediaType) and Ads to include (activeStatus). A dropdown is validated against its list of allowed values, and null is not on any list, so a template that nulled all its unset fields still had its run refused before it started.
Both dropdowns keep their options and titles in the input form, and both now accept null as "use the default" — video and active respectively. {"searchQueries": ["fitness app"], "mediaType": null, "activeStatus": null, "maxAds": null} now runs exactly like {"searchQueries": ["fitness app"]}. With this, every optional field on this actor takes null.
One consequence worth stating plainly: a value that is not on a dropdown's list is no longer refused before the run. It is caught by the run instead, which uses the documented default and returns a free row telling you which value it did not recognise and what it used — the same way this actor has always answered a country code or a link it could not read. Nothing about what this actor delivers, or charges, changed.
1.0.51 — 2026-08-31
A field you left unset can now be sent as null — the run starts and uses the default.
If you call this actor from a template — n8n, an agent framework, a chained workflow — the tool usually fills in every field it knows about, and writes null into the ones you left blank. Until this build the platform refused those runs before they started, with an error like Field input.maxAds must be integer. Nothing ran, nothing was charged, and the fix was non-obvious: you had to delete the key entirely rather than leave it empty.
Optional fields now accept an explicit null and read it as "use the default" — identical to leaving the field out. {"searchQueries": ["fitness app"], "country": null, "maxAds": null} runs exactly like {"searchQueries": ["fitness app"]}, searching the US with the standard 1,000-ad cap. A null never counts as a value: it will not read as an empty keyword list, and it will not turn on an option that defaults off. (The two dropdown fields still needed null support at this point — 1.0.52 finishes the job.)
Nothing about what this actor delivers, or charges, changed.
1.0.50 — 2026-08-31
The opening hook is never blank again, and it now tells you when it starts.
hook3s is the first 3 seconds of speech in an ad. Plenty of Facebook creatives — brand films, long title cards, anything that opens on music or a logo sting — say nothing at all in those 3 seconds, and until now those ads came back with the hook column empty: exactly what you would see if the actor had simply failed to get the line. On a charged row, that was our headline field arriving blank.
A late opening line is delivered, not dropped. When nothing is spoken in the ad's first 3 seconds, hook3s now carries the first 3 seconds of speech from wherever speech actually begins — the real opening line.
New hookStartSeconds column. The second that line starts, so a late hook can never be mistaken for one that opened the ad. 0 means the ad opens speaking. Sort by it to see which competitors make you wait for the pitch.
No more blank strings. An ad with no speech at all leaves both fields empty (null), so "this ad opens silent" and "we lost the line" can no longer look identical in your spreadsheet.
Nothing about what this actor delivers otherwise, or charges, changed.
1.0.49 — 2026-08-31
A safety belt on what this actor may charge you for. Most of what a run writes is deliberately not charged: the sample row a blank run returns, the guidance row when an input field is missing or misspelled, a keyword search that found nothing, an ad whose creative link had expired, a silent ad, a download that failed, and every ad left over at your maximum cost. Those rows are the product — they are how you can see what happened to each ad you asked for. This build makes that promise structural: the run now reads which billing model it is on before it writes anything, and if this actor were ever moved to a model that charges for each result row, those free rows would be reported in the run summary and status line instead of being written as rows you would be charged for. On such a model a delivered result also can no longer be charged twice, and a run resumed after an interruption cannot re-charge for results the first attempt already billed. Nothing charged today changes: on the current pricing every one of those rows still ships exactly as before, still uncharged.
1.0.48 — 2026-08-31
A run now spends the cost cap you set. When several ads were being processed at once, an ad that turned out to carry no speech released the cost it had reserved — but by then the ads still waiting had already been answered with skipped_budget rows, so that released budget had nobody left to spend it and a run could finish having delivered nothing while charging nothing. Ads now wait for budget that is still in play instead of being skipped past it, so a run delivers as many transcripts as your maximum cost allows. Some ads failed to download with a network error no re-run could fix — the video address the run reached for was not reachable from it, and every retry reached for the same one. Video and image downloads now take a route the run can always reach, so those ads come back as transcripts instead of "please re-run" rows. Ads left over at the cap are now marked retryable: true alongside their skipped_budget status, and the run summary names your maximum cost in dollars instead of just mentioning it. Prices and what is charged are unchanged.
1.0.47 — 2026-08-30
The long-video surcharge is now spelled out in the input form. The Max ads to process field's description now says what the Pricing tab and this page already did: the first 3 minutes of every video are included in the transcript price, and each started minute beyond that is charged as a "Long-video minute (surcharge)" at $0.005 — only on delivered transcripts, never on image ads or on-screen text. The output table on this page now says the same next to its "any length" promise. Prices and what is charged are unchanged.
1.0.46 — 2026-08-30
Every ad you asked for gets a row — including the ones your cost cap could not cover. When a run reaches its maximum cost, each ad left over now ships as a free skipped_budget row with everything already known about it (advertiser, ad text, CTA, archive id), so the dataset always holds one row per ad and the run summary's count matches what you can see. Silent ads now tell you how to read them: a no_audio row and the run's status line both point at the "Read on-screen text on silent ads" option — many music-only ads carry their whole message on screen. Dynamic product ads ship real copy: ads Facebook serves as a {{product.name}} template now deliver the wording of the creative variant that was actually transcribed; in the rare case no variant copy exists, the row says so with adTextIsTemplate: true instead of looking broken. Prices and what is charged are unchanged.
1.0.45 — 2026-08-30
Search for ads by keyword or advertiser — no ad IDs needed. Put something like fitness app, or a competitor's page name, into the new Search keywords field, pick a country, and the ads running there come back already transcribed. Nothing else is required: no links to paste, no other scraper to run first. Each keyword has its own cap on how many ads it may return, you can include stopped ads or image formats, and every delivered row says which keyword found it. A keyword that matches nothing, or a search Facebook declines that minute, ships as a free row saying exactly that — searching itself is never charged, and the price per delivered result is unchanged. Ad Library links, video URLs and dataset chaining all work exactly as before.
1.0.44 — 2026-08-30
An ad with no real speech is never charged. A transcript that is only the speech engine's own filler — including the subtitle-credit lines it sometimes invents on music-only ads — one whose timing does not fit the audio, or one in a language that cannot belong to the ad's own declared country, now ships as a free no-speech row. Genuinely spoken ads, in any language, are delivered and charged exactly as before.
1.0.43 — 2026-08-30
Runs now use a fraction of the memory, especially on long videos and big batches. Downloaded video is processed from temporary storage and removed the moment it is no longer needed, instead of being held in memory for the whole ad — on a 10-ad batch of one-minute videos, peak memory dropped about 4×. Long creatives no longer push a run toward its memory ceiling, and the default memory setting goes further.
Silent-ad on-screen text is read faster. The five-frame strip behind Read on-screen text on silent ads is now produced in one pass — about 20% faster on such runs — and a rare video whose frames could not be sampled near the end of the clip now gets a second, more thorough pass instead of shipping without its on-screen text.
Nothing about what this actor delivers or charges changed.
1.0.42 — 2026-08-30
This page's wording refreshed and a suite link carries a sibling's updated store title. Nothing about what this actor delivers or charges changed.
1.0.41 — 2026-08-30
Clearer release notes — this page's notes now read more plainly. Nothing about what this actor delivers or charges changed.
1.0.40 — 2026-08-30
Suite links now point at the full live shelf — every actor named in this README is a live
store link. Nothing about what this actor delivers or charges changed.
1.0.39 — 2026-08-30
A row can never say it was charged when the charge could not land. When a run reaches its maximum cost just as a result becomes ready, that result now ships as a free skipped_budget row telling you to raise "Maximum cost per run" and re-run for it — and the rest of the run stops buying media it can no longer bill.
The cost ceiling is respected exactly even when several ads finish at once. A batch of long videos completing together near the cap used to be able to tip the run just past its maximum cost, which ends it abruptly; such a run now stops honestly at the cap with a clear status line.
A chained dataset is read in full, and a cut is always announced. Chained datasets larger than one page no longer lose rows beyond the first page: everything up to the 10,000-row ceiling is read, and a dataset holding more than that gets one free row saying how many rows this run processed and how to get the remainder.
During a transcription-service outage, ads that never needed that service keep their real answer. An item with no creative in it, an input pointer, or an image ad with image reading switched off now ships its usual free row instead of a "please re-run later" note that a re-run could not improve.
1.0.38 — 2026-08-30
A mid-run transcription-service outage now answers every remaining ad with its own row. If the speech and text-reading service refuses this actor's access partway through a run, the ads that could not be started no longer vanish into a single count in the status line: each one ships a free asr_unavailable row, marked retryable, saying the problem is on our side and nothing was charged. A re-run picks those ads up exactly as before, the run still stops immediately without buying anything further, and the status line reads as it always has.
1.0.37 — 2026-08-29
Clearer wording on uncharged no-speech rows: an ad whose audio turns out to be music-only or silent now says exactly that in statusReason, in plain language. The same ads are recognised as before, and they are still never charged.
1.0.36 — 2026-08-29
New option: read the text on screen in silent ads. Plenty of Ad Library video creatives carry no speech at all — kinetic typography, a static card, music over b-roll — and their whole message is written on the screen. Turn on Read on-screen text on silent ads and those ads come back with that text as a structured onScreenText field (headline, body, CTA, and the full visible text) instead of an empty row. It is off by default, so nothing changes unless you ask for it. A delivered on-screen extraction is charged exactly like a video transcript — same price, and never a long-video surcharge — and a silent ad with no readable text on screen is still free, as no_onscreen_text. The advertiser name on these rows always comes from the ad's own metadata, never from reading the picture.
Chained image ads that live under an unusual field name now deliver. This page promises the actor reads the creative URL value, not a fixed field name. That had stopped being true for image ads: a scraper exporting its creative under creatives[].url, src, mediaUrl or photos[].url got a free no_creative_found row saying its creative was "a profile picture or preview thumbnail". Those creatives now deliver. Profile pictures and preview thumbnails are still never treated as the ad.
An oversized creative now costs one attempt instead of three. A video or image bigger than this actor will download used to be fetched up to three times before the run gave up and told you to re-run — which could not help, because the file's size is not a temporary problem. It is now a single attempt and one free, clearly-labelled video_too_large / image_too_large row.
A free row can no longer be squeezed out by the "Max ads" cap. The cap counts only ads that can be charged, as the field says. Rows that explain something instead — an unusable input, an item with no creative in it — always ship, and never use up the cap.
An empty input list now gets a pointer instead of the sample row. Submitting "videoUrls": [] (or an empty Ad Library links list) used to look identical to a blank run and came back with the free demo row, as if the empty list had worked. It now returns one free row naming the field to fill in. A genuinely blank run still returns the demo row exactly as before.
If a charge left over from an interrupted run cannot be completed the moment the run resumes, the run now carries on and settles it later, instead of stopping with an error. Nothing is ever charged twice.
1.0.35 — 2026-08-29
Image ads now deliver what they say. The actor covers every ad's content: video ads keep their full transcripts, and image ads now return their on-image text — headline, body, CTA and the complete visible text — as a structured imageText field, plus the ad's imageUrl. A delivered image extraction is charged exactly like a video transcript: same price, and never a long-video surcharge. Honesty contract unchanged — an image with no readable text (a plain photo) ships as a free ocr_no_text_found row, an expired image link as a free image_expired row, and a temporary reading problem as a free row that says to re-run. Prefer the previous behaviour? Set the new "Extract image-ad text" option off (includeImageText: false) and image ads pass through free with their metadata, exactly as before.
With both video and image results in one run, the final status line now reports the split (e.g. "Delivered 12 ad transcripts (7 video, 5 image-ad text)").
1.0.33 — 2026-08-29
A typo in an input field name now gets a helpful pointer instead of sample rows. A field the actor does not recognise is ignored by the platform, so an input like adUrls used to look exactly like a blank run and came back with the free demo row — as if the typo had worked. Such a run now returns one free row that names the field it could not recognise, names the field to use instead (adLibraryUrls, with the shape to paste), and mentions the chaining fields. Nothing is charged, and a genuinely blank run still returns the demo row exactly as before.
1.0.32 — 2026-08-29
An already-expired video link now costs zero run time. Video URLs carry a built-in expiry stamp; when it has already passed, the row comes back instantly as a free unavailable_expired with the decoded expiry time — no download is attempted for a link that can only fail. Links without a readable stamp are fetched as before.
Rate-limit retries now reliably reuse the video already in hand. When the speech service asks the actor to wait, the retry transcribes the bytes it already downloaded instead of fetching the file again — retries are faster and cheaper, and a link that expired mid-run can no longer be misreported as permanently dead when the real cause was a brief wait.
A speech-service access problem now stops the run honestly. If the speech service refuses this actor's access, the remaining ads are no longer processed one by one toward calls that cannot succeed: the run stops cleanly, the status line says how many ads were left and that the problem is on our side, and none of them are charged.
1.0.31 — 2026-08-28
A run interrupted by platform maintenance now resumes exactly where it stopped — nothing is transcribed or charged twice. When Apify moves a running container to another server, or when you resurrect a finished or aborted run, the actor now recognises every ad it has already delivered: it skips them silently, completes any charge that was interrupted mid-settlement, and processes only what is still missing. The final status line says how many ads were carried over without being charged again.
The actor's own one-hour safety ceiling now counts from the run's start instead of restarting after an interruption, so an interrupted run can never quietly extend its own maximum length.
1.0.30 — 2026-08-27
A dataset ID the actor can't read now tells you so. Chaining a run with a mistyped or unreachable Dataset ID used to finish quietly with an empty result and no explanation at all. The run now returns one free row naming the ID and what to do about it — check the ID, pick it with the "Dataset ID" field (which grants read access), or paste the rows into "Dataset items". Nothing is charged either way.
A run started with no input now says it is a demo. The free sample row a blank run returns is labelled demo and explains that no input was provided, instead of reading like an ordinary image-ad result from an advertiser called "Demo Advertiser". It is still free, and the same is now true when the input form's example row is submitted unchanged.
1.0.29 — 2026-08-27
Long ads with quiet stretches now transcribe. A long creative whose speech is spread between music or silent gaps could come back as a free "music only" row; it now returns the full transcript.
A silent video is labelled honestly. A video that carries no audio track ships as a free no_audio row, and a file the actor genuinely cannot process now gets its own free failed_processing row instead of being reported as music-only. Neither is charged.
Stalls and rate limits are handled cleanly. A transcription that hangs is now cut off and retried instead of eating the run's time, and when the speech service asks us to wait, the actor waits exactly that long and re-uses the video it already downloaded rather than fetching it again.
An oversized video is now stopped while it downloads rather than after, so run time and memory stay predictable.
Rows that end in a failure now report the real number of attempts made.
If a run ever stops on an unexpected error, the message no longer claims nothing was charged when transcripts delivered earlier in the same run were.
Very large numeric ad IDs arriving from a chained scraper are no longer reported inaccurately.
1.0.28 — 2026-08-27
Billing accuracy. A run that cannot bill anything no longer says it did: the charged flag and the per-row chargeEvents ledger now read the run's live pricing, so they always match what you were actually charged.
A cut-off download can no longer become a partial transcript. If a video arrives incomplete, the actor now detects it, retries, and — if it still can't get the whole file — ships a free row instead of transcribing (and charging for) half an ad.
A max-charge cap that covers exactly one transcript now returns that transcript instead of stopping with nothing.
Long-video surcharge budgeting was recalculated for yesterday's lower transcript prices, so a batch of long ads stays inside your max charge instead of stopping the run early.
The example ad ID in this page had expired; it now points at a live ad.
1.0.27 — 2026-08-27
Price cut. A delivered transcript now costs $0.020 down to $0.010 depending on your Apify plan tier — the volume price was $0.015. The free-plan price and the $0.005 long-video surcharge are unchanged, and you are still charged only when a transcript lands in your dataset.
The pricing block in this page now shows the new figures.
1.0.25 — 2026-08-27
Added a plain note that this is an independent actor — unofficial, and not affiliated with or endorsed by Meta.
The directory of other steadyfetch actors now covers every family (ad creatives, trends and keywords, YouTube, Instagram, jobs, Amazon, and general media) and names each one exactly as it appears on the Store, so a link always lands where the name says. It now sits at the end of the page, just above support.
1.0.24 — 2026-08-27
The README now opens with an Output section — a sample results table, the output screenshot, and the full JSON row — so you can see exactly what you get back before spending anything.
Added a copy-and-paste block for AI agents and LLM clients: actor id, a complete input example, every output field, and the exact event prices.
The "Related actors" list is now a full directory of the steadyfetch ad-intelligence actors (Google Ads video and creative text, TikTok, LinkedIn, Instagram Reels, Google Trends) with their free n8n templates.
1.0.23 — 2026-08-25
Billing accuracy for pasted Ad Library links: an ad can never be confused with a different ad — a lookup that doesn't find exactly your ad ships as a free row instead of transcribing (and charging) an unrelated one; and the same ad supplied both as a link and inside a chained dataset row is charged once.
Carousel video ads pasted as Ad Library links now transcribe.
Links that merely look similar — other sites' URLs with an ?id= number, or bare numbers inside chained rows — are no longer mistaken for Ad Library ads; they get clear guidance or keep their normal deep-scan behaviour.
1.0.22 — 2026-08-25
Ad Library link lookups now resolve reliably. In 1.0.21 they could come back as free "try again" rows for every link; pasted links now return the ad's video as intended. Nothing about pricing or the other input modes changed.
1.0.21 — 2026-08-25
You can now paste Facebook Ad Library links directly — use the new Ad Library links field (facebook.com/ads/library/?id=…, or a bare ad ID) and the actor finds the ad's video for you. Ad Library links pasted into Ad video URLs or Dataset items are picked up automatically too, instead of returning a "use a video URL" note.
Links that can't be resolved are never charged: an ad that's been taken down ships as a free unavailable_removed row, and a temporary block from Facebook ships as a free resolve_blocked row telling you to re-run — never a wrong "this ad is gone" answer.
The same ad supplied more than once — in two fields, or as both a link and an ID — is transcribed and charged once.
Chaining by video URL, dataset ID, or pasted rows is unchanged.
1.0.20 — 2026-08-21
README: added a Related actors section — the same transcript pipeline on other ad platforms — with links to more free n8n chaining templates. No change to pricing or behavior.
1.0.19 — 2026-08-21
Billing accuracy: transcripts in all languages (Japanese, Arabic, Korean, …) are now billed consistently.
Improved music-only detection — in many languages — so music-only and silent creatives reliably ship as free rows.
Long-video surcharges now always match the per-row ledger exactly, and runs with a tight Maximum cost per run finish cleanly at the cap — a very long video near the cap could previously end the run abruptly.
Runs with a Maximum cost per run now deliver every transcript the cap affords — a rounding quirk could previously stop one transcript early.
1.0.18 — 2026-08-21
README: added a Works with list of compatible Ad Library scrapers (where each one keeps the video URL, and how to chain by dataset ID) and a second free n8n template, Competitor ad teardown (one competitor page → one row per ad with hook, CTA and transcript length). No change to pricing or behavior.
1.0.17 — 2026-07-30
Runs started with memory outside the tuned range (512 MB–4 GB) now exit immediately with clear guidance — nothing charged. Normal runs are unaffected.
Every run now finishes within a bounded time window, regardless of custom timeout settings. Typical runs — including any-length single videos — are unaffected; very large batches stop cleanly with an honest summary and can be split across runs.
1.0.16 — 2026-07-29
Run memory is now clamped to the range this workload actually needs (512 MB–4 GB), and the default run timeout is 30 minutes (was 60). Typical runs are unaffected; both changes just keep run costs predictable.
1.0.15 — 2026-07-05
Pricing is now all-inclusive (effective July 5, 2026): Apify platform usage is included in the event price, with no change to event prices — your total cost drops slightly and becomes fully predictable. Unchanged: any-length videos, no result = no result fee.
The run-budget logic now follows the actor's active pricing model automatically, so runs with a Maximum cost per run deliver the full number of transcripts the cap affords under the new pricing.
1.0.14 — 2026-06-30
Fixed: video ads from chained Ad Library scrapers now transcribe. Real Facebook ad video links were being mis-detected as images and skipped — they're now correctly recognized and transcribed, whether you pass them as video URLs, pasted rows, or a chained Dataset ID.
1.0.12 — 2026-06-30
Chaining by Dataset ID now works reliably. When you pass a scraper run's Dataset ID, the actor now reads your ads correctly instead of stopping before any are processed.
A Dataset ID the actor can't read — a wrong/expired ID, or one not selected via the "Dataset ID" field — now returns a clear, uncharged row explaining how to fix it, instead of failing the run.
1.0.10 — 2026-06-16
Clearer platform-usage cost disclosure: the listing now states the measured range — ~$0.10 per 1,000 ads on average, up to ~$0.60 for long videos — instead of a single rounded figure, so your usage bill never surprises you. (No change to pricing or behavior.)
1.0.9 — 2026-06-14
Bad input no longer fails the whole run. An Ad Library page URL, a non-fbcdn URL, or a non-array datasetItems now ships as an uncharged input_error row with guidance, the run SUCCEEDS, and any valid URLs in the same batch are still transcribed (previously one bad URL failed the run).
Top-level guard: only an unexpected internal error can fail a run now, and it does so with an honest "nothing was charged" message plus an ERRORS record.
Run summary now reports inputModeMix (videoUrls / datasetItems / datasetId / invalidInputs / demo) for input-mode telemetry.
Refreshed listing screenshots (cache-busted).
1.0.8 — 2026-06-12
Zero-input runs (platform automated health checks, bare "Run" clicks) now execute the uncharged demo image-ad row instead of failing. Real inputs are untouched — the demo row is never injected when any input mode is provided.
1.0.7 — 2026-06-11
Friendlier input form: the three input modes are grouped and reordered (URLs → dataset ID → pasted rows), with clearer titles. No behavior change.