ad-spy — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited ad-spy (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Pull a competitor's live Meta / Facebook ads from the public Ad Library (via the Apify actor curious_coder/facebook-ads-library-scraper) and return a clean, ranked teardown. The official Meta Ad Library API only returns EU commercial ads, so scraping is required.
/ad-spy <brand>NOT for: owned-account performance (use METAAGENT performance.py), single-reel IG teardowns (/analyze-reel), site/offer/revenue recon (/gambit), or LinkedIn / lead-gen prospect lists (/linkedin-scraper, /lead-scraper).
US) — ISO code for the Ad Library country filter.150) — max ads per page pull. Each page is hard-capped at 150 by the Ad Library; for advertisers with multiple pages, iterate page IDs.scripts/resolve_page_id.py does a keyword scrape, groups results by page_name, picks the best name match, and returns (page_id, matched_name, confidence). If brand is already a numeric ID, skip resolution. Confidence is high (substring match), med (prefix overlap), or low (no name overlap → flag in output).scripts/pull.py builds the view_all_page_id + search_type=page Ad Library URL and posts to the Apify actor. Saves raw JSON to out/<brand-slug>-<country>-page.json. Never pull by keyword for primary results — keyword returns mostly noise + mis-attributes generic names (Cadence → "Cadence OTC", Maple Diamonds → "Maple Cottage Designs", JAXXON → "JAXXON For Her").scripts/parse.py walks each ad and extracts:days_live from start_date → end_date (or now if active)is_video — True if snapshot.videos or any cards[].video_*_url is set, else staticbody_text — DCO-unwrap: prefer snapshot.cards[].body and .title over snapshot.body (which is the {{product.brand}} template). Also pull from extra_texts.angle — slug from link_url (e.g. brand.co/pages/<slug> → pcos, perimenopause, gut-health). Single richest persona signal.cta, display_format, collation_count, snapshot_url, page_name, ad_archive_iddays_live desc — long-runners = proven winners, 3-day clusters = active testing churn.angle (landing-page slug) — count, median days, max days per angle.video % vs static %.out/<brand-slug>-<country>-page.jsonout/<brand-slug>-<country>-parsed.jsonresolve_page_id.py first.total_pulled >= 150, flag capped: true in output and recommend paginating.snapshot.body for DCO ads is the literal template string {{product.brand}}. ~98% of Grüns ads were DCO/SHOP_NOW.snapshot.videos OR any of video_preview_image_url / video_hd_url / video_sd_url / watermarked_video_sd_url on cards. If none → static (cards or images).(end_date or now) - start_date) / 86400. Long-runners (60+ days) are the winning creatives; copy their angle/format.link_url slug; the slug names the avatar Meta optimised toward.med/low OR the matched name looks wrong, read all_pages_seen — the dominant count is usually the real brand — and re-resolve with that exact page name (or pass --page-id directly). Some brands won't resolve at all via keyword scrape (e.g. "Our Place" / "From Our Place" → only ad-noise, never the brand page); substitute or get the page ID by hand rather than burning pulls.--quiet to pull.py makes argparse error out and the pull silently fails. pull.py flags are --country --count --page-id --no-resolve --out-dir + the brand positional. To pull without resolution, use pull.py "<name>" --page-id <id> --no-resolve.Reads APIFY_API_TOKEN (or APIFY_TOKEN) and META_AD_LIBRARY_ACTOR from ~/Desktop/AGENTS/METAAGENT/.env. Never echo the token value.
~/Desktop/AGENTS/METAAGENT/out/
<brand-slug>-<country>-page.json # raw Apify items
<brand-slug>-<country>-parsed.json # cleaned + rankedReadout printed to stdout includes:
Output is consumed by the (future) /ad-brief skill, which distils hook + angle + persona into a structured creative brief and writes it into the ExampleBrand Notion META tracker at Status=Brief.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.