Research report · 22 October 2026 · 10 scouts, 15 live-probed verifications, 18 transcripts

Winning Product Radar

Can an n8n automation mine the Meta, TikTok and Google ad libraries for products selling at $60 or more, categorise them, name the marketing angle, and rank them by real signals? Yes, with three corrections to how most people imagine it. This page is the scrape of Arie Scherson's channel, the verdict, and the build blueprint.

Verdict
  1. Buildable in n8n, and worth building. Every stage maps to real nodes: the official Apify node for acquisition, HTTP Request for store data, the Gemini and Anthropic nodes for video and classification, Postgres for scoring, Slack or Gmail for delivery.
  2. Not through the official APIs. Meta's Ad Library API returns commercial ads only if they delivered in the EU, so a US-only Shopify brand is invisible to it. TikTok's Creative Center JSON is gated (probed three times: code 40101 "no permission" to every unsigned or forged-header request; whether the signature itself is validated or a session is required is not proven, and does not matter). Google's Transparency Center has no keyword search at all. Acquisition goes through Apify actors ($0.69 to $2 per 1,000 ads) that replay the public web UIs.
  3. No ad library carries a product price. The $60 gate has to come from the store: Shopify's public /products/<handle>.js and /meta.json (probed on 6 of 9 stores) give price, currency, subscription plans and catalogue size for free. Non-Shopify stores need JSON-LD or an LLM fallback.
  4. "Smart" means penalties, not sorting. Longevity and ad counts are survivorship-biased; the model below gates longevity on an impression band, excludes retargeting zombies and catalog ads, and multiplies by saturation and brand-size penalties so Gymshark and twelve dropshippers of the same ring never top the list.
  5. Cost: about $120 to $620 a month depending on volume (about $700 with the optional WinningHunter feed), plus one week to the first ranked list. The first day proves the whole spine on one keyword. This page is a blueprint with node-level detail, not an importable workflow JSON; the first JSON is the day-1 deliverable.

The channel: what Arie Scherson actually does

Arie Scherson (@aries1) describes himself as "breaking down brands that scale on Meta". The full video list was pulled with yt-dlp (the channel page is a JavaScript shell to a plain fetch). The CSV of all 1,228 videos was delivered alongside this page.

163Ksubscribers
1,228videos
9.3Mtotal views
3,900median views per video
2.1 / wkupload cadence, 2026
3.8%median like rate, last 80
Format (title pattern)Videos, all timeLast 80 videosMedian views, last 80
Brand breakdown ("A Brilliant $20M/Month Shopify Niche Store")268414,365
Niche or trend ("A Booming $42 Billion Ecommerce Niche Trend")113172,889
Product-research method ("How I Find…", "The 5 Products I'd Dropship")3835,499
Ads and creative strategy69——
AI tools3362,938
News, tariffs, Shopify updates3711,103
Podcast or interview33——

Cadence is falling: 22 uploads in January 2026, 3 in September. The two biggest recent videos are both brand breakdowns (62K and 20K views, September 2026). The breakdown is the product; the automation's output should read like one.

His stack, from 18 transcripts and the video descriptions

ToolWhat he uses it forRelationship
Meta Ad LibraryPrimary surface in every 2025-26 breakdown. Keyword search, sorted by impressions ("these are the highest impression ads in this market"), scroll to the oldest still-active ads ("stood the test of time"), open ad details, click Shop now.Free
GetHookdActive-ad count per page and highest-engagement ads ("this brand literally has 33,000 active ads"); newly launched ads; "learn more" to the lander.Affiliate / partner
Pluto Hunt, formerly ShopiLab (his own)Search-demand charts with seasonality, Exploding Topics feed, Similarweb-style monthly-visitor chart with country split (browser extension), ROAS calculator, sourcing tab; since Aug 2026 also TikTok Shop products with GMV, creators and keywords. shopilab.ai now 308-redirects to plutohunt.com (checked 22 Oct 2026). $59 Orbit / $99 Hunter.Owner
KalodataTikTok Shop analytics. Linked in 7 descriptions, never used on screen.Affiliate
TeemDropSourcing and discovery: 1688/Taobao search, landed cost and shipping days, WhatsApp screenshot-to-source.Affiliate
Google Shopping tabReads the price band other Shopify brands charge (never Amazon or AliExpress); if his exact product is absent, "more untapped".Free
SmartReplyLast step of every breakdown: comment report grade, marketing angles, pain points, customer avatar.Part owner
DraftPie, BGPT, Claude + Higgsfield MCPCreative generation: swap a competitor's longest-running static for his product; market-sophistication diagnosis from a supplier screenshot; statics and UGC video.Owner / affiliate

His numbers, verbatim where possible

His breakdown template (the output format to copy)

The same sequence appears in every 2025-26 breakdown: (1) active-ad count in the Ad Library or GetHookd; (2) traffic chart, monthly visitors, country split, revenue estimate; (3) oldest and longest-running ads, statics first, open ad details, read headline, bullets, offer, guarantee; (4) click Shop now to the landing page or advertorial; (5) above-the-fold images and buy box; (6) offer construction: bundles, free gift, timer, BOGO, guarantee; (7) social proof counts; (8) cart and upsells; (9) best sellers; (10) comment report: angles, pain points, avatar; (11) what to source and at what price. His creative vocabulary: statics, UGC, VSL, carousel, catalog, partnership, theme-page, advertorial, quiz funnel, retargeting testimonial. His angle vocabulary: problem-aware callouts ("yeasty ears, itchy paws"), "selling the mechanism, not the object", gift angle, counterfeit warning, competitor price callout.

What each library really gives you

Every row below was either probed live with curl on 21-22 October 2026 or taken from the platform's own documentation fetched the same day. Verification status marks what an independent agent reproduced.

SourceOfficial APIWhat the public UI exposesHow the pipeline gets itCostStatus
Meta Ad Library ads_archive v26: commercial ads only when they delivered in the EU (the reference says non-EU ads return "only if they are about social issues, elections or politics"). Needs government-ID verification, an app and a user token. No landing URL, no media URL, no collation count. Political-only spend and impressions. Per card: Library ID, "Started running on", Active/Inactive, platforms, CTA, page, "N ads use this creative and text", "Total active time", a "Low impression count" badge (since Dec 2025). Arie's May and June 2026 screen recordings show results sorted by impressions, which implies an impression band per ad; one vendor guide disagrees, so confirm on day 1 from the actor's fields. EU panel: reach by country, age, gender. Inactive non-EU ads vanish, so longevity must be built by daily polling. Apify curious_coder/facebook-ads-library-scraper via the official Apify node: keyword and page inputs, returns link_url, collation_count, start/end dates, video URL and, with ad details on, EU reach. The UI's "Total active time" field comes from ScrapeCreators' endpoint, not this actor, so the snapshot table keeps it nullable. The raw UI answers curl with a 403 JavaScript challenge (/__rd_verify_), and the GraphQL replay needs sticky residential IPs, so buy the actor. $0.75 per 1,000 ads (alternatives $0.30 to $5.80) API scope confirmed 403 reproduced
TikTok Commercial Content API: EU, UK, CH, TR only, researcher access, 10 results per query. No US ads anywhere official. Creative Center Top Ads: CTR, likes, comments, shares, cost tier 0-2, 2s/6s view-rate sort, industry, objective, landing URL, brand, 20 ads per filter page, no timestamps, video URLs expire in hours. Top Products and Keyword Insights tabs are behind a free business login and have no known actor. Apify dltik/tiktok-creative-center (no login: a headless browser captures the signature, HTTP replays it; 248 users, 100% success) or parsebird/tiktok-top-ads-scraper. Longevity comes from snapshotting the leaderboard daily; nexascout/tiktok-winning-creative-radar does exactly that and labels NEW / RISING / PERSISTENT_WINNER. The most-used actor, doliz, needs cookies extracted from a logged-in browser and is excluded for that reason. TikTok Shop GMV: FastMoss OpenAPI, the only self-serve documented one. $0.69 to $2 per 1,000 ads; FastMoss from $0.01 per call 40101 reproduced ×3 ToS risk (see limits)
Google Ads Transparency Center None. SerpApi and SearchAPI.io wrap it; n8n has an official SerpApi node. Per advertiser (search by advertiser or domain only, never keyword or product): verified name, region, format (Search, YouTube, Shopping, Display), first/last shown, up to 5 variations. Unverified advertisers appear only for EEA and Türkiye regions. Spend and impressions for political ads only. Shopping creatives render a price, the only ad surface that does. Two layers. Cheap: the anonymous SearchService/SearchSuggestions RPC returns an advertiser's ad-count range in one request (probed: Gymshark USA Inc, 7,000-8,000 ads). Rich: SerpApi google_ads_transparency_center for creatives, dates and formats on short-listed brands only. Per-IP blocks after roughly 60-80 raw requests, so never sweep it yourself. SearchSuggestions free; SerpApi 250 free a month then $25 per 1,000; Apify silva95gustavo/google-ads-scraper $0.90 per 1,000 Fields confirmed Unverified advertisers US-absent
Shopify stores (the price source) Public by design, undocumented. /products.json?limit=250&page=N (title, handle, product_type, tags, created_at, published_at, variants with price and compare_at_price; no currency); /products/<handle>.js (price in cents, price_min/max, selling_plan_groups = subscription); /meta.json (currency, published_products_count, shop id as an age proxy); /.well-known/ucp maps a custom domain to its myshopify domain. Reviews: Yotpo and Okendo answer without auth; Judge.me and Loox need store tokens. n8n HTTP Request nodes. Probed: 6 of 9 stores answered immediately; bombas (Vercel challenge, 429), ridge (Cloudflare 403), ruggable (headless, 404) did not. One scout reports Shopify's Web Bot Auth returning 429 to datacenter crawlers since late July 2026, so route these fetches through a residential proxy or a scraping API. Free; Zyte from $0.13 per 1,000 requests if a proxy is needed Reproduced on 4 stores Bot-auth 429 unverified

The three consequences for the design. (1) Longevity is a time series you build, not a field you read: snapshot every tracked ad daily. (2) Google is a confirmation signal per brand, never a discovery feed. (3) The product, not the ad, is the unit of ranking: an ad resolves to a landing URL, the URL to a store, the store to a handle and a price, and many ads (and many advertisers) collapse onto one canonical product.

Architecture

01 · DISCOVER
Meta + TikTokApify actors, daily. Keywords per category plus tracked pages. Upsert ads, append a snapshot row.
02 · RESOLVE
Ad → store → productDecode l.facebook.com, strip UTMs, follow redirects, detect Shopify, fetch products.js and meta.json; collapse the same item sold by many stores into one canonical product (image hash + title embedding).
03 · GATE
Price ≥ $60Convert to USD. Below $60 is archived, never scored. Unknown price is held, not dropped.
04 · ENRICH
Confirm elsewhereGoogle ad-count range per advertiser; TikTok Top Ads presence; FastMoss GMV; reviews; store age and catalogue size.
05 · CLASSIFY
Category + angleGemini transcribes the video; Claude returns the structured classification (14×108 categories, 21 angles, hooks, format, offer).
06 · SCORE
Signals − penaltiesNightly SQL over the snapshots: 12 signals, saturation and brand-size multipliers, zombie exclusion.
07 · DELIVER
The digestTwice weekly: new entrants and risers, each as an Arie-style breakdown card with evidence links.

Everything runs as small sub-workflows (one advertiser or one batch of 50 ads per execution) so n8n Cloud's 640 MiB on Starter never holds a whole dataset. Video bytes never pass through n8n when the Apify transcript actors are used; if the Gemini node is used instead, it is one video per execution.

The $60 gate: ad to price

  1. Landing URL. Meta actors return link_url; TikTok detail enrichment returns landing_page. Facebook's l.php shim returns 400 without its hash, so decode the u parameter locally. Strip utm_*, fbclid, ttclid, gclid. Follow 301s (Shopify Markets redirect, for example gymshark.com to a regional checkout host). Linktree pages carry their links in __NEXT_DATA__.
  2. Store detection. The old x-shopid header is gone. Use powered-by: Shopify, the link: </.well-known/ucp> header, Shopify.shop and Shopify.currency globals in the HTML, or cdn.shopify.com/s/files/1/ paths (headless stores still carry these).
  3. Price. If the landing path is /products/<handle>, fetch /products/<handle>.js: price (cents, the default variant), price_min, price_max, compare_at_price, selling_plan_groups. If the landing page is a collection, advertorial or quiz, fetch /products.json (250 a page) and match the ad's headline or image to a product with an LLM, falling back to the catalogue's best-seller collection. Currency comes from /meta.json (products.json has none); convert with a daily FX table in Postgres.
  4. Non-Shopify. JSON-LD Product.offers.price and og:price:amount (present on some stores, absent on custom themes); WooCommerce's public /wp-json/wc/store/v1/products (prices in minor units with currency); otherwise the fetched HTML (via a scraping API such as Zyte or Firecrawl when the store blocks plain fetches) through an extraction prompt in the Anthropic node. Jina's free reader is licensed for non-commercial use only, so it is not in the chain. If nothing yields a price, the product is held with price_status = unknown and surfaces in a weekly "needs a look" list rather than being ranked.
  5. The rule. The advertised offer must be at or above $60 in USD: take the larger of the default-variant price and the price of the bundle or variant the ad's landing page sells (price_max of the advertised product). A $59 single sold through a $107 duo offer passes; a $59.99 single with no bundle is gated out. Store price_min, price_max and the advertised price separately. Expected coverage on day one: roughly two thirds of Shopify landers automatically, the rest via fallbacks.

Scoring model

Weights sum to 100. Each signal is scored on its own scale and normalised (points = weight × score / max). Signals with no data (a product not on TikTok Shop, say) are nulled and the total is rescaled to the weight that was available, so a Meta-only product is not punished for TikTok silence. Then the multiplicative penalties apply. All time windows are computed from the daily snapshot table, never from a single read.

On "engagement". Public like, comment and share counts exist only on TikTok's Top Ads. No Meta ad library exposes engagement, so for Meta the model uses what the library does show (impression band, collation count, longevity, ad volume) as the engagement proxy. If the optional WinningHunter API is subscribed, its per-ad Meta like, comment and share counts slot into signal 6 alongside the TikTok metrics; without it, Meta engagement is out of scope and the report says so.

#SignalSourceComputation and scaleWeight
1Gated longevityMeta snapshotsDays since start of the product's longest still-active ad, counted only for ads whose impression band is 10K+ (or EU reach 10K+, or collation_count ≥ 3 when no band exists). <14 d = 0 · 14-29 = 1 · 30-59 = 2 · 60-89 = 3 · 90+ = 4.15
2Scaling volumeMeta page searchActive ads on the page pointing at the product's domain. 1-5 = 0 · 6-19 = 1 · 20-49 = 2 · 50-99 = 3 · 100+ = 4. (Practitioner buckets: 3-5 maintenance, 20-40 testing, 60-100+ scaling.)12
3Creative iterationMeta adsDistinct hooks (first 60 characters of body or headline) plus total collation_count, catalog ads with {{product placeholders excluded. 1 = 0 · 2 = 1 · 3-5 = 2 · 6-10 = 3 · 11+ = 4.10
4Spend proxyMeta impression band or EU reachHighest band among the product's ads: <10K = 0 · 10K-50K = 1 · 50K-100K = 2 · 100K-500K = 3 · 500K+ = 4. EU fallback: spend ≈ reach × 1.8 ÷ 1,000 × market CPM.12
5MomentumMeta snapshotsNew ads in the last 7 days versus the prior 7, and active-count change over 30 days. Flat = 0 · +20% = 2 · doubled = 4 · negative = 0 and flag "fading".8
6Paid-social engagementCreative Center Top Ads; optionally WinningHunter for MetaAbsent = 0 · present in the 30-day list for its industry = 1 · plus CTR above the industry median = 2 · plus likes+comments+shares in the top quartile = 3. With WinningHunter: the product's best Meta ad's like+comment+share count in the top quartile of its category counts as the "3" tier.8
7Google footprintSearchSuggestions rangeAdvertiser ad-count range: none = 0 · 1-49 = 1 · 50-199 = 2 · 200+ = 3 · +1 if YouTube video creatives exist (SerpApi, shortlist only).5
8TikTok Shop velocityFastMoss7-day GMV flat or falling = 0 · rising = 2 · rising with 50-300 creators = 3 · top-decile SKU (>$100K/month) = 4. Null if not on TikTok Shop.7
9Demand growthGoogle Trends via SerpApi's google_trends engine (not probed here)90-day slope for the product term, not the brand: declining = 0 · flat = 1 · rising = 2 · breakout = 3.5
10Price and offerproducts.jsHard gate ≥ $60, then $60-99 = 1 · $100-149 = 2 · $150+ = 3 · +1 for a bundle, quantity break or subscription (selling_plan_groups, multi-pack variants, compare_at discount).8
11Store evidenceproducts.json, meta.json, Yotpo/OkendoProduct published within 180 days AND shop older than 90 days = 2 · focused catalogue (≤ 50 products, this product carries most ads) +1 · 100+ public reviews +1.5
12Multi-countryMeta country sweeps (US, CA, UK, AU, DE)Countries where the page runs active ads: 1 = 0 · 2 = 1 · 3-4 = 2 · 5+ = 3.5

Penalties and exclusions

Ranking. Tiers: 70+ launch-grade, 55-69 watch, under 55 skip. The digest shows new entrants to launch-grade, products whose score rose 8+ points in 7 days, and the top five of the watch tier. Ties break on momentum, then on price headroom.

Worked example

Numoya · Go Brush Pro (cordless heated brush for short hair) from Arie's Jan 2026 breakdown; starred values assumed
Signals
Oldest still-active ad started 2 Oct 2025, 92 days before the 2 Jan 2026 breakdown, band 100K-500K* → 4/4 → 15 · 420 active ads → 4/4 → 12 · 14 distinct hooks* → 4/4 → 10 · spend band 100K-500K* → 3/4 → 9 · momentum +20%* → 2/4 → 4 · in TikTok Top Ads with CTR above median* → 2/3 → 5.3 · Google 50-199 ads with YouTube video* → 3/4 → 3.8 · TikTok Shop: null (weight 7 unavailable) · Trends rising* → 2/3 → 3.3 · advertised $107 duo (single $59), bundle → 3/4 → 6 · store: product older than 180 days (0), focused catalogue (+1), 100+ reviews (+1) → 2/4 → 2.5 · 2 countries* → 1/3 → 1.7
Raw
72.6 points on 93 available weight → 78.0
Penalties
8 other advertisers of heated short-hair brushes* → ×0.90 · page likes under 100K → ×1.00
Score
70 · Launch-grade, on the line. A ninth-to-fifteenth competitor keeps it there; the sixteenth drops the multiplier to ×0.75 and the score to 58, Watch.

Category and marketing angle

The classification spec was delivered as ad_classifier_v1.json: system prompt, user-message template, the JSON schema (validated against Claude's structured-output rules: no min/max, additionalProperties:false everywhere, zero unions), and the taxonomies. The summary:

Category, two levels

14 top-level verticals (beauty & skincare, health & supplements, wellness devices, fitness & sports, pet, home & living, kitchen & food, fashion & jewelry, tech & gadgets, baby & kids, outdoors / garden / travel, hobbies / crafts / toys / gifts, and two more) over 108 sub-niches, each mapped to a Shopify Standard Product Taxonomy vertical so a Shopify gid and a Google product category come free. The model picks from a closed enum; an n8n Code node checks that the L2 belongs to the L1.

Note: Shopify is removing the committed dist/ folder from the taxonomy repo on 31 October 2026; load the gzipped release assets (categories.en.json.gz) from the v2026-08 release instead.

Angle, hook, format, offer

21 angles with detection cues (problem-agitate-solve, new mechanism, failed-solution reframe, before/after transformation, demo/education, testimonial social proof, volume social proof, authority/clinical, founder story, us-vs-them, identity/aspiration, fear/warning, curiosity/pattern interrupt, gift/occasion, offer/value/bundle, scarcity/urgency, humour/skit, sensory/ASMR, trend-native viral, AI avatar, feature-benefit rational), 13 hook types (question, bold claim, "if you have X" callout, POV scenario, text-on-screen statement, shocking stat, warning, confession, "stop scrolling", visual-only, price-first, trend sound, none), 15 formats (UGC selfie, founder-to-camera, expert explainer, static, carousel, product demo, before/after split, podcast clip, mash-up, skit, screen recording, street interview, green-screen reaction, unboxing/ASMR, AI avatar), 14 offer mechanics, 13 emotional drivers. Angle is judged over the whole ad; the hook is the first 3 seconds. Arie's "counterfeit warning" and "advertorial" live in the hook (warning) and landing-page fields, not as angles.

Pipeline per ad

  1. Video to text. Claude takes no video. Either the Apify transcript actors (templates 19027 for Meta, 19153 for TikTok) or the n8n Gemini node's Analyze Video (~100 tokens per second of video, about $0.003 to $0.006 per 30-second ad) produce transcript, on-screen text and a visual description in one call. No separate OCR step.
  2. Classify. Anthropic Chat Model node (Haiku 4.5 at about $0.006 per ad, Sonnet 5 at about $0.012 when a dispute needs a tie-break) with the Structured Output Parser and the schema from the spec. Input: ad copy fields, transcript, visual description, thumbnail, and the context line SOURCE / PAGE / FIRST_SEEN / ACTIVE_DAYS / ADS_FROM_THIS_PAGE. Output fields: evidence, is_physical_product_ad, product_name, product_description, category_l1, category_l2, price_seen, primary_angle, secondary_angle, hook_type, format, offer_mechanics, target_customer, claims_made, emotional_driver, confidence, needs_review (all required). Non-product ads (apps, services, lead-gen, brand awareness) come back with is_physical_product_ad = false, category_l1 = other and needs_review = true; needs_review is what feeds the weekly "needs a look" list. Set the model explicitly: the Anthropic node defaults to claude-sonnet-5. Put the few-shot examples inside the cached system block so it clears the minimum cacheable length, and confirm caching from cache_read_input_tokens in the node output.
  3. Aggregate to the product. The product's angle is the dominant primary_angle weighted by each ad's collation_count and active days; secondary angles are listed with their share. This is what the digest prints.
  4. Evaluate before trusting. Hand-label 100 ads (stratified by source and vertical, two labellers, 30 hostile cases), freeze model + prompt + schema, report per-field agreement. Below about 90% on clear-cut cases, the field is not ready for ranking.
-- classification prompt, condensed (full text in ad_classifier_v1.json)
SYSTEM: You are a performance-creative analyst for DTC e-commerce brands. You classify ONE paid-social ad
at a time from the evidence supplied (ad copy fields, transcript, on-screen text, keyframe descriptions,
thumbnail). Pick category_l1 and category_l2 ONLY from the enums. primary_angle is the argument the whole
ad makes; hook_type is the first 3 seconds only. Non-product ads (apps, services, lead-gen, brand awareness
without a product): is_physical_product_ad=false, category_l1='other', needs_review=true. Never infer a price
that is not shown; price_seen is only what the ad or its text states.
USER: AD_ID {{ad_id}} SOURCE {{meta|tiktok|google}} PAGE {{page_name}} FIRST_SEEN {{start}} ACTIVE_DAYS {{days}}
ADS_FROM_THIS_PAGE {{n}} AD_COPY primary_text=… headline=… cta=… TRANSCRIPT … ON_SCREEN_TEXT … VISUAL …

Storage and the digest

create table advertisers (id bigserial primary key, source text, page_id text, page_name text,
  page_likes int, domain text, country_set text[], first_seen date, last_seen date, unique(source,page_id));
create table ads (id bigserial primary key, source text, ad_key text, advertiser_id bigint references advertisers,
  landing_url text, resolved_url text, body text, headline text, cta text, media_url text, format text,
  started_at date, first_seen date, last_seen date, is_catalog bool default false, unique(source,ad_key));
create table ad_snapshots (ad_id bigint references ads, day date, is_active bool, collation_count int,
  active_hours int, impression_band text, eu_reach int, likes int, comments int, shares int, ctr numeric,
  primary key(ad_id,day));
create table stores (id bigserial primary key, domain text unique, platform text, myshopify_domain text,
  currency text, product_count int, shop_id bigint, review_count int, review_source text, checked_at timestamptz);
create table products (id bigserial primary key, store_id bigint references stores, handle text, title text,
  canonical_key text, image_phash text, price_usd numeric, price_min_usd numeric, price_max_usd numeric,
  has_subscription bool, has_bundle bool, published_at date, product_type text, tags text[],
  price_status text check (price_status in ('ok','below_gate','unknown')), unique(store_id,handle));
create table ad_products (ad_id bigint references ads, product_id bigint references products, primary key(ad_id,product_id));
create table ad_classifications (ad_id bigint primary key references ads, is_physical_product_ad bool,
  product_name text, product_description text, evidence text, category_l1 text, category_l2 text,
  primary_angle text, secondary_angle text, hook_type text, format text, offer_mechanics text[],
  target_customer text, claims text[], emotional_driver text, confidence numeric, needs_review bool,
  model text, classified_at timestamptz);
-- canonical products: products.canonical_key groups the same item sold by many stores (see W4b);
-- store title embeddings in a pgvector column (title_embedding vector(1536)) on the products table.
create table enrichment (product_id bigint references products, day date, google_ads_low int, google_ads_high int,
  google_has_video bool, tiktok_top_ads bool, tiktok_ctr numeric, tiktok_engagement int, fastmoss_gmv_7d numeric,
  fastmoss_creators int, trends_slope numeric, primary key(product_id,day));
create table product_scores (product_id bigint references products, day date, s1 int, s2 int, s3 int, s4 int,
  s5 int, s6 int, s7 int, s8 int, s9 int, s10 int, s11 int, s12 int, available_weight int, raw numeric,
  saturation_mult numeric, brand_mult numeric, score numeric, tier text, distinct_advertisers int, flags text[],
  primary key(product_id,day));
create table dead_letter (id bigserial primary key, workflow text, item jsonb, error text, at timestamptz default now());

Idempotency: every write is an INSERT … ON CONFLICT DO UPDATE through the Postgres node ("Insert or Update"); the Supabase node has no upsert, so point the Postgres node at the Supabase connection string. Keys are (source, ad_key) for ads and (store, handle) for products.

What lands in Slack or your inbox

#2 this week · Numoya Go Brush Pro Score 70 · Launch-grade · new entrant Beauty & skincare › Hair tools example; starred values assumed
Price
$107 duo advertised (single $59.00; compare-at $149*). Subscription: no. Bundle: yes. Gate: passes on the advertised offer.
Ads
420 active on Meta (US); oldest still running since 2 Oct 2025 (92 days); 14 distinct hooks*, 3-5 versions each*; spend band 100K-500K*. Google: 50-199 ads, YouTube video present*. TikTok Top Ads: present, CTR above the industry median*.
Angle
Problem-aware callout to older women with short hair ("If your mom has short hair…") → advertorial bridge page → duo offer with 100% money-back guarantee and a "2025 Hair Awards" badge. Secondary: before/after (31%), volume social proof "over 40,000 women" (22%).
Why it works
A direct-response offer and storytelling images aimed at an avatar nobody else targets; the ad copy is reused across many videos (variable isolation).
Risks
8 other advertisers on heated short-hair brushes (×0.90). Seasonal: no. Policy: none.
Evidence
Ad Library page link · 3 longest-running ads · landing URL · products.js · Google Transparency advertiser · TikTok detail page

n8n workflows

WorkflowTriggerNodes in orderBatch and limits
W1 Meta discoverySchedule, daily 03:00Postgres (read keyword and page seeds) → Code (build Ad Library search URLs: …/ads/library/?active_status=active&ad_type=all&country=US&q=<keyword>, or page URLs) → Loop Over Items → Apify "Run Actor and get dataset" (curious_coder actor input: urls[], count, scrapeAdDetails, scrapePageAds{activeStatus, countryCode}) → Code (normalise, decode l.php, strip UTMs, flag catalog placeholders) → Postgres upsert ads + advertisers → Postgres insert ad_snapshots → Execute Sub-workflow W4 for new adsOne actor run per seed. Keywords: count 300. Tracked pages (advertisers that passed the gate): count up to 1,000 so the dataset length is the page's active-ad total for signal 2. Weekly, the same page runs with countryCode CA, UK, AU, DE for signal 12. Cap volume with the actor's count: the Apify node fetches the whole dataset with no limit of its own. Apify Trigger webhook instead of wait-for-finish once past day 1
W2 TikTok discoverySchedule, daily 03:30Loop over industries × sort orders (ctr, like, for_you) → Apify (dltik actor: US, period 30, detail enrichment on for the landing URL) → Code (normalise) → Postgres upsert ads + snapshots (likes, comments, shares, ctr) → W420 ads per filter page; ~20 industries × 3 sorts = 1,200 rows a day; video URLs expire, so W5 runs the same day
W3 Google confirmSchedule, weekly, plus on new advertiserPostgres (advertisers without a Google check in 7 days) → HTTP Request POST SearchSuggestions (f.req={"1":"<name>","2":10,"3":10}) → Code (parse ad-count range) → Postgres enrichment; for tier ≥ watch: SerpApi node google_ads_transparency_center → formats and first/last shownRequest interval 3 s, max 50 per execution; HTTP node retry with {{ Math.pow(2,$runIndex)*1000 }} wait
W4 Resolve and gateSub-workflow (run once per item)HTTP GET resolved_url (follow redirects, proxy credential) → Code (Shopify detection) → IF Shopify → HTTP products/<handle>.js + meta.json → ELSE JSON-LD / WooCommerce / Jina reader + Anthropic extraction → Code (USD conversion, price_status) → Postgres upsert stores + products + ad_products → IF price_status = ok → W550 ads per parent batch; 2 requests per store per day cached in stores.checked_at; dead_letter on any non-200
W4b CanonicaliseSub-workflow after W4HTTP GET product image → HTTP POST to a tiny perceptual-hash service (a Supabase Edge Function or Cloudflare Worker computing a 64-bit dHash; the Cloud Code node has no image library) → Embeddings node (OpenAI or Cohere) on the product title + first 300 characters of description → Postgres: nearest neighbours in pgvector among products in the same price band → Code (same product if hash Hamming distance ≤ 8 or cosine ≥ 0.92 and price within 25%) → Postgres set products.canonical_keyThis is the step the saturation and brand-collapse penalties stand on; template 19326 already dedupes Shopify products in Supabase and is a starting point. Self-hosted n8n can do the hash in a Code node with sharp
W5 ClassifySub-workflow (run once per item)IF video → Apify transcript actor (or Gemini node Analyze Video) → Anthropic Chat Model + Structured Output Parser (schema from the spec) → Code (enum check: L2 ∈ L1) → Postgres upsert ad_classificationsOne ad per execution; Haiku 4.5 default; prompt caching on the system prompt (1 h)
W6 EnrichSchedule, daily 05:00Postgres (products gated ok) → FastMoss OpenAPI product search (HTTP Request, Bearer token) → Yotpo / Okendo review counts (HTTP) → SerpApi google_trends for new product terms → Postgres upsert enrichmentFastMoss only for products whose ads mention TikTok Shop or whose brand has a TikTok Shop; Trends once per term per 30 days
W7 ScoreSchedule, daily 06:00Postgres (one SQL statement computing s1…s12 from snapshots, enrichment, products; penalties; tier) → Postgres insert product_scoresPure SQL, no items in memory
W8 DeliverSchedule, Mon and Thu 08:00Postgres (new launch-grade entrants, risers of 8+ points in 7 days, top 5 of the watch tier, needs-a-look list from needs_review and price_status = unknown) → Anthropic (write the "why it works" line from the classifications and copy) → Slack block kit or Gmail HTML → Notion database page per product (optional)Top 15 per digest; each card links its evidence rows
W0 ErrorsError TriggerError Trigger → Postgres dead_letter → Slack alertNever fires for manual runs; test with a scheduled run

Hosting. n8n Cloud Starter (€20, 2,500 executions, 5 concurrent, 640 MiB) runs W1-W8 only if sub-workflow executions stay under the plan's execution count; at 1,200 TikTok rows plus a few thousand Meta ads a day the per-item sub-workflows exceed 2,500 executions in days. Realistic options: n8n Cloud Pro (€50, 10,000 executions, 20 concurrent) with W4 and W5 batching 25 items per execution, or self-hosted Docker on a $12-24 VPS with Postgres and Redis in queue mode, which also removes the memory ceiling and lets the Code node use packages. The Cloud execution-time cap is contested (pricing page says 5 min on Starter; n8n staff said in July 2026 that no hard cap is enforced), so confirm with support before relying on long waits.

Cost and build order

ItemLow (lean: 60K Meta ads, 15K TikTok ads, 2K classified a month)High (300K Meta ads, 40K TikTok ads, 10K classified)
Apify Meta actor ($0.75 per 1K)$45$225
Apify TikTok actor ($0.69-2 per 1K)$15$80
Google: SearchSuggestions free; SerpApi beyond 250 free$0$50
FastMoss ($0.01 per call)$10$30
Transcription + classification (Gemini Flash + Haiku 4.5, about $0.012 per ad)$24$120
Residential proxy or Zyte for store fetches$10$30
n8n (self-hosted VPS or Cloud Pro)$20$60
Supabase Postgres$0$25
Optional: WinningHunter Standard API (Meta like/comment/share counts, store revenue estimates; the only tested API source of Meta engagement)$0$79
Total per month$124$620 · $699 with WinningHunter

First week

  1. Day 1, the spine. Supabase schema above; n8n with the Apify node installed; one actor run with a single Ad Library search URL (q=heated brush, country US, active status) and count 300; W4 on the result. Done when 20+ products sit in products with a USD price and a price_status. This also answers the open question about impression bands: look at the actor's raw fields.
  2. Day 2, time. Daily schedule, the snapshot table, page tracking for every advertiser found; signals 1-5 computable after 48 hours of snapshots.
  3. Day 3, meaning. W5 on ad copy and thumbnail only (no video yet). Category and angle in the table; spot-check 30 by hand.
  4. Day 4, the other two libraries. W2 for TikTok Top Ads joined on landing domain; W3 SearchSuggestions for every advertiser.
  5. Day 5, the score. W7 SQL with penalties; the first ranked list. Read the top 20 and the bottom 20.
  6. Day 6, video and delivery. Transcript actor or Gemini node; W8 digest into Slack in the card format above.
  7. Day 7, the hostile suite. Gymshark and Temu must score as "brand, not product". A 60-day ad with the low-impression badge must be excluded. A $59.99 single with no advertised bundle must be gated; a $59 single sold through a $107 duo must pass. Twelve dropshippers of one ring must collapse to one product with a ×0.75 saturation. A catalog ad must not count as a hook. If any of these passes without a fight, the check is wrong.

Limits, legal, unverified

What the pipeline cannot do

Legal and operational posture

SourceRiskPathThe one mitigation that matters
Meta Ad LibraryMediumVendor actor on the logged-out public libraryNo account, no cookies, separate IP: Meta v. Bright Data (N.D. Cal., Jan 2024, pulled from govinfo) held Meta's terms do not bind logged-off scraping of public data; Meta rewrote its terms from Jan 2025 to say "whether or not logged in", which weakens but does not erase that. Never let a scraping profile touch a personal account.
TikTok Creative CenterMedium-highVendor actor, anonymous bootstrapTikTok's consumer terms ban automated collection and one April 2026 guide says Creative Center harvesting breaches them; keep it to leaderboard reads, never a logged-in account, and treat it as the first feed to drop if challenged.
Google Transparency CenterLowSerpApi (assumes scraping liability contractually on paid plans) plus the anonymous suggestions RPC at low volumeNever sweep SearchCreatives yourself; the per-IP block arrives after a few dozen requests.
Shopify storesLowOwn polite HTTP nodesBackoff on 429/430 "Security Rejection"; cache per store per day; residential egress if the datacenter 429 report holds.
Stored creativesLowInternal research useFair use / fair dealing covers internal analysis; do not republish other brands' creatives to third parties. Drop EU beneficiary/payer names (can be natural persons).

Still unverified (and what resolves each)

Downloads: aries1_videos.csv (all 1,228 videos) · ad_classifier_v1.json (classifier prompt, schema and taxonomies)

Method: ten parallel research agents (one per ad library, store extraction, n8n, commercial tools, signal science, taxonomy, legal, Arie's transcripts) with live curl probes on 21-22 Oct 2026; fifteen independent re-verifications; the pipeline design synthesised from their findings. Two artefacts accompany this page: the channel CSV (1,228 rows) and ad_classifier_v1.json. Workflow run wf_cce26993-c56.