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.
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./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.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.
| Format (title pattern) | Videos, all time | Last 80 videos | Median views, last 80 |
|---|---|---|---|
| Brand breakdown ("A Brilliant $20M/Month Shopify Niche Store") | 268 | 41 | 4,365 |
| Niche or trend ("A Booming $42 Billion Ecommerce Niche Trend") | 113 | 17 | 2,889 |
| Product-research method ("How I Find…", "The 5 Products I'd Dropship") | 38 | 3 | 5,499 |
| Ads and creative strategy | 69 | — | — |
| AI tools | 33 | 6 | 2,938 |
| News, tariffs, Shopify updates | 37 | 1 | 1,103 |
| Podcast or interview | 33 | — | — |
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.
| Tool | What he uses it for | Relationship |
|---|---|---|
| Meta Ad Library | Primary 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 |
| GetHookd | Active-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 |
| Kalodata | TikTok Shop analytics. Linked in 7 descriptions, never used on screen. | Affiliate |
| TeemDrop | Sourcing and discovery: 1688/Taobao search, landed cost and shipping days, WhatsApp screenshot-to-source. | Affiliate |
| Google Shopping tab | Reads the price band other Shopify brands charge (never Amazon or AliExpress); if his exact product is absent, "more untapped". | Free |
| SmartReply | Last step of every breakdown: comment report grade, marketing angles, pain points, customer avatar. | Part owner |
| DraftPie, BGPT, Claude + Higgsfield MCP | Creative generation: swap a competitor's longest-running static for his product; market-sophistication diagnosis from a supplier screenshot; statics and UGC video. | Owner / affiliate |
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.
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.
| Source | Official API | What the public UI exposes | How the pipeline gets it | Cost | Status |
|---|---|---|---|---|---|
| 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.
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.
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__.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)./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.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.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.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.
| # | Signal | Source | Computation and scale | Weight |
|---|---|---|---|---|
| 1 | Gated longevity | Meta snapshots | Days 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 |
| 2 | Scaling volume | Meta page search | Active 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 |
| 3 | Creative iteration | Meta ads | Distinct 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 |
| 4 | Spend proxy | Meta impression band or EU reach | Highest 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 |
| 5 | Momentum | Meta snapshots | New 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 |
| 6 | Paid-social engagement | Creative Center Top Ads; optionally WinningHunter for Meta | Absent = 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 |
| 7 | Google footprint | SearchSuggestions range | Advertiser ad-count range: none = 0 · 1-49 = 1 · 50-199 = 2 · 200+ = 3 · +1 if YouTube video creatives exist (SerpApi, shortlist only). | 5 |
| 8 | TikTok Shop velocity | FastMoss | 7-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 |
| 9 | Demand growth | Google 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 |
| 10 | Price and offer | products.js | Hard 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 |
| 11 | Store evidence | products.json, meta.json, Yotpo/Okendo | Product 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 |
| 12 | Multi-country | Meta country sweeps (US, CA, UK, AU, DE) | Countries where the page runs active ads: 1 = 0 · 2 = 1 · 3-4 = 2 · 5+ = 3. | 5 |
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.
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:
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.
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.
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.-- 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 …
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.
| Workflow | Trigger | Nodes in order | Batch and limits |
|---|---|---|---|
| W1 Meta discovery | Schedule, daily 03:00 | Postgres (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 ads | One 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 discovery | Schedule, daily 03:30 | Loop 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) → W4 | 20 ads per filter page; ~20 industries × 3 sorts = 1,200 rows a day; video URLs expire, so W5 runs the same day |
| W3 Google confirm | Schedule, weekly, plus on new advertiser | Postgres (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 shown | Request interval 3 s, max 50 per execution; HTTP node retry with {{ Math.pow(2,$runIndex)*1000 }} wait |
| W4 Resolve and gate | Sub-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 → W5 | 50 ads per parent batch; 2 requests per store per day cached in stores.checked_at; dead_letter on any non-200 |
| W4b Canonicalise | Sub-workflow after W4 | HTTP 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_key | This 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 Classify | Sub-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_classifications | One ad per execution; Haiku 4.5 default; prompt caching on the system prompt (1 h) |
| W6 Enrich | Schedule, daily 05:00 | Postgres (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 enrichment | FastMoss only for products whose ads mention TikTok Shop or whose brand has a TikTok Shop; Trends once per term per 30 days |
| W7 Score | Schedule, daily 06:00 | Postgres (one SQL statement computing s1…s12 from snapshots, enrichment, products; penalties; tier) → Postgres insert product_scores | Pure SQL, no items in memory |
| W8 Deliver | Schedule, Mon and Thu 08:00 | Postgres (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 Errors | Error Trigger | Error Trigger → Postgres dead_letter → Slack alert | Never 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.
| Item | Low (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 |
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.| Source | Risk | Path | The one mitigation that matters |
|---|---|---|---|
| Meta Ad Library | Medium | Vendor actor on the logged-out public library | No 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 Center | Medium-high | Vendor actor, anonymous bootstrap | TikTok'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 Center | Low | SerpApi (assumes scraping liability contractually on paid plans) plus the anonymous suggestions RPC at low volume | Never sweep SearchCreatives yourself; the per-IP block arrives after a few dozen requests. |
| Shopify stores | Low | Own polite HTTP nodes | Backoff on 429/430 "Security Rejection"; cache per store per day; residential egress if the datacenter 429 report holds. |
| Stored creatives | Low | Internal research use | Fair use / fair dealing covers internal analysis; do not republish other brands' creatives to third parties. Drop EU beneficiary/payer names (can be natural persons). |
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.