00 / How to read thisConfidence is labelled. Don't skip it.
Pulled live from the Apify API on 28 Aug 2026 — actor IDs, per-event pricing, and 30-day success rates. These are measured, not quoted from a listing page.
Actor input schemas are not public — readable only with a token. Field names marked are the expected shape. Do not code against them until the real schema is read.
Model decision — locked
(a) Accept 2–4 week China→NZ ship times. No capital tied up, lower conversion. Every margin figure below therefore carries a refund provision, and the landing page must show an honest ETA. Under this model that provision is not optional — margin that ignores it is fiction.
Live test 28 Aug — the kill rule was wrong
The 1688 cost × 3.5 gate passes products that lose money. On the tested product (US$11.19 retail) the cheapest 1688 match was CNY 2.89 = NZ$0.68 — ×3.5 clears easily, yet the real net margin is −NZ$12.85 once freight is costed properly. Product cost is nearly irrelevant: a 40× swing in supplier price moves net by NZ$27, while CAC and freight move it far more. The gate is now a US retail price floor plus a weight ceiling, applied before the growth ranking. Full walkthrough in §01–03.
01 / The economicsWhy there has to be a price floor
The gate isn't about finding a cheap enough supplier. It's about whether the sale price can carry the costs that don't shrink when the product is cheap. Same business, same supplier country, same ad spend — two different products.
A — the product we testedUS$11.19 · 500g · CNY 2.89 from 1688
B — identical, but a US$45 productUS$45 · 500g · CNY 50 — 17× dearer to buy
B uses a product that costs 17× more to buy and still makes NZ$22 while A loses NZ$13. The only thing that changed is what you sell it for.
Now set product A's supplier cost to zero — the product is free. It still loses NZ$12.17. There is nothing left to cut, because the supplier was never the problem.
The two amber lines are why. Shipping and ads come to NZ$27.39 per sale whether you sell a $10 item or a $100 one. On an NZ$18.80 sale that is more than the entire sale. On an NZ$75.60 sale it is barely a third.
02 / FreightNZ$6.50 was wrong — it's weight-driven
My original NZ$6.50 was a placeholder and it was too low by between 1.3× and 7×. China Post ePacket publishes 23 CNY per parcel + 85 CNY/kg, capped at 2kg. That makes freight a function of weight, so weight becomes a selection criterion in its own right.
| Weight | Freight NZ$ | vs my placeholder | Net on a US$45 item (CNY 15 product) |
|---|---|---|---|
| 150 g | NZ$8.40 | 1.3× | +NZ$36.28 |
| 300 g | NZ$11.40 | 1.8× | +NZ$33.29 |
| 500 g | NZ$15.39 | 2.4× | +NZ$30.29 |
| 1 kg | NZ$25.38 | 3.9× | +NZ$20.30 |
| 1.5 kg | NZ$35.37 | 5.4× | +NZ$10.31 |
| 2 kg (hard cap) | NZ$45.35 | 7.0× | +NZ$0.33 |
03 / The floorWhat this makes the actual rule
Break-even and 25%-net retail price, at CAC NZ$12 with a CNY 15 product, by parcel weight:
| Weight | Freight | Break-even US retail | US retail for 25% net |
|---|---|---|---|
| 150 g | NZ$8.40 | US$17.75 | US$25.50 |
| 300 g | NZ$11.40 | US$20.00 | US$28.75 |
| 500 g | NZ$15.39 | US$22.75 | US$33.00 |
| 1 kg | NZ$25.38 | US$30.25 | US$43.75 |
The working rule
US retail ≥ US$30 AND under 500g. Applied before the growth ranking, not after — otherwise the whole pipeline burns discovery, creative capture, reverse-image search and supplier lookup on products that were dead on arrival. A heavy cheap product is dead twice over.
What is still an estimate
CAC NZ$12 is invented — it is what Meta ads cost per sale, and nobody knows it until the first campaign runs. The ePacket rates are published but are the US lane; NZ is lower-volume and typically dearer, so treat these as a floor rather than a quote. Get a real number from CJ Dropshipping's calculator or a forwarder before committing. Every table above moves with those two inputs — but the shape holds: cheap or heavy products cannot carry a fixed per-sale cost.
04 / What the first real run foundThe keyword list is the whole ballgame
Same filters, same snapshot size, two keyword sets — and a 4× vs 72 outcome. Scanning "trending" broadly walks you straight into blocked categories.
| Keyword set | Cleared price floor | Survived S4 |
|---|---|---|
| Battery-heavy (led face mask, smart ring, massage gun) | 72 | 4 — 68 blocked as battery |
| Non-electronic (wig, hair extensions, silk pillowcase, leather bag, sunglasses) | 74 | 72 — 1 blocked |
The standout category: wigs and human hair
High AOV, high volume, light, no battery, no liquid. Live on TikTok Shop US: OQ HAIR wear-and-go glueless US$106.60 with 72,212 sold; 360 Wig US$213.33 / 19,957; Recool US$104.90 / 10,208. 1688 human-hair comparables run CNY 29–200 → net NZ$74–115 (42–64%). Supply concentrates in 浙江 and 河南/许昌 — Xuchang is the global wig manufacturing hub. Blissy silk pillowcase (US$79.96, 74,036 sold) is the other clean one.
Sourcing method depends on the photo, not the product
Reverse-image works on isolated objects — an LED mask product shot matched 30/30. It fails on model-worn goods: a wig photo returned ear stickers, hair dye and a comb, because the image is a person, not the product. For apparel, hair, anything worn — source by Chinese keyword via zen-studio/1688-wholesale-scraper instead.
And "cheapest" has now misled three times running: a US$299 LED mask matched a WiFi smart lock; price-ascending on wigs surfaced 化纤 synthetic party wigs at CNY 4.65 as if comparable to a US$106 human-hair HD lace front. A keyword or image hit is a candidate, never an equivalent — filter on spec, and use the median, never the minimum.
05 / S6bThe stage the funnel was missing
The original funnel filtered on price, weight, battery and margin — and never once asked whether the NZ market was already taken. That is the actual thing being looked for: overseas traction plus thin NZ supply.
NZ search volume is a lagging indicator. It measures what people here already know exists. Something trending overseas and not yet sold here shows low NZ volume precisely because the arbitrage is still open — so low volume is not evidence against a product, it is the signal.
So TikTok US stays the demand signal, and NZ SERP becomes the supply check. The kill signal is not low search volume — it is generic imported listings already ranking in NZ, which means someone has already run the play.
| Verdict | Meaning | Result |
|---|---|---|
| GREEN | no NZ sellers ranking | window open |
| AMBER | NZ specialty retailers only | contested, still viable |
| RED | major NZ retailer, or ≥2 generic-import listings | play already run — kill |
Run live on the non-electronic shortlist — 11 checks, 6 cents of DataForSEO:
| Candidate | NZ sellers | Verdict |
|---|---|---|
| wear and go wig | 1 — wigs.co.nz | 🟠 thinnest supply tested |
| glueless wig human hair | 1 — wigs.co.nz | 🟠 thin |
| hd lace front wig | 5 specialty | 🟠 contested |
| hair topper women | 5 specialty | 🟠 contested |
| clip in hair extensions | 10 specialty | 🟠 contested |
| leather crossbody bag | 14 specialty | 🟠 crowded |
| led face mask | generic imports + Caci, Sephora, Harvey Norman | 🔴 shut |
| silk pillowcase | Kmart, Peter Alexander, Ecosa | 🔴 shut |
| polarized sunglasses | Rebel Sport, Hunting & Fishing, Specsavers | 🔴 shut |
| silk pajamas | Trade Me, Papinelle, Peter Alexander | 🔴 shut |
| silk sleep mask | Sephora, Chemist Warehouse | 🔴 shut |
⚠ This result was a FALSE POSITIVE — wigs are out
S6b flagged wear-and-go wigs: 1 NZ seller as the thinnest supply of anything tested. It read as an open window. It was not — the stage matched the keyword, not the market.
The demand driver doesn't exist here. The US lace-front market is largely driven by Black women's haircare. NZ's 2023 Census MELAA group — Middle Eastern, Latin American and African combined — is 92,760 people, 1.9% of the population, and the African subset is a fraction of that.
And the lone "competitor" is a different business. wigs.co.nz is a medical hair-loss retailer — cancer and alopecia, wig-subsidy grants, survivor testimonials — selling at NZ$509–799 synthetic and NZ$1,799–2,599 human hair against a US$106 TikTok product. Different customer, different reason to buy, roughly 20× the price, and a fitting-and-consultation service business that an Instagram-ad funnel cannot serve.
Thin supply because the market is not worth serving, not because nobody got there yet. That is the one thing S6b cannot see on its own.
The fix — a demand-driver check at the human gate
This is the fourth variant of the same bug: a WiFi smart lock matched by image, synthetic wigs matched by keyword, cheapest-match treated as equivalent, and now a keyword matched to the wrong market. The TikTok signal carries US population composition with it.
So before trusting any amber, ask: why do Americans buy this, and does that reason exist in New Zealand?
Does not transplant: demographically driven (protective-styling haircare), regulation driven (firearms, supplements), culturally driven (US sports, Thanksgiving), climate driven.
Does transplant: universal-need categories — pets, babies, sleep, fitness, home organisation, kitchen, outdoor.
06 / Stage detailWhat goes in, what comes out
Each stage names its API, its inputs, its outputs and where the result is stored. Amber stages spend money; green stages are logic we own and can build today.
Config
No API| In | APIFY_TOKEN, DATABASE_URL, FX rate, thresholds — MARGIN_MULTIPLE=3.5, KILL_SPEND_USD, KILL_CPA_NZD, REFUND_PROVISION_PCT |
|---|---|
| Out | — |
| Store | .env — never committed |
Discover
$0.0045/item · 2k ≈ $9Sweep TikTok Shop US by category. Runs on Apify's cloud with US proxies — this is why the NZ geo-block is irrelevant. It only ever blocked you, not the pipeline.
| API | unseenuser/TikTok-Shop-Scraper — 100% success / 10,197 runsPOST api.apify.com/v2/acts/{actor}/run-sync-get-dataset-items |
|---|---|
| In | category or keyword list · market US · maxItems |
| Out | mode enum shop_search·shop_catalog·product_details·product_reviews·creator_showcase · region US GB ID MY PH SG VN TH DE FR (no NZ) · getRelatedVideos — may return S3's videos itself, collapsing two stages |
| Store | upsert Product; append ProductSnapshot — never update |
The velocity trap
This response is cumulative lifetime units — "12.5k sold" says nothing about the last seven days. There is no trend in this data. Trending is a delta between two snapshots, which is the entire reason S2 exists and why snapshots are append-only.
Velocity
Free · oursThe one piece of genuine IP in the pipeline. Diff our own snapshot history rather than trusting an actor to compute it.
| In | ≥2 ProductSnapshot rows for the same productId |
|---|---|
| Out | units24h units7d growthPct7d velocityScore |
| Math | units7d = snap(t).unitsSoldTotal − snap(t−7d).unitsSoldTotal |
| Rank | Growth % off a low absolute base — not volume. A 50k-unit product is already saturated and its 1688 price is bid up. |
Cold start
Day one has no history, so this stage produces nothing until the second run. That is what S2b is for — and it is also why building the store comes before the analysis.
Cold-start bridge
$0.002 + $0.004 / productThe only actor that keeps its own daily history — so it has a trend on day one, while ours is still building.
| API | memo23/tiktok-shop-sales-scraper — 100% success |
|---|---|
| In | product URLs or IDs |
| Out | soldCount (cumulative) unitsSoldInWindow unitsSoldPerDay salesGrowthPercent trendWindowDays firstSeenAt — verified live |
| Proof | Ranked by cumulative: 136,585 units, 0.3% growth — dead. Ranked by growth: 71 units, 491.7% — just moving. Two different businesses, for 13 cents. |
| Trap | trendWindowDays is capped by their history depth — min 0.9 / mean 5.4 / max 6.9, all first seen within 8 days. Reject rows under 5 days; 491% over 0.9d is noise. |
| Then | Wean off it. lurkapi was the best-fitting actor in this category and went UNDER_MAINTENANCE mid-research. When an actor dies, our snapshot history is the only thing that survives. |
Creative capture
Per productThe hook that made the product trend is already public and already proven. Capture it here, while the product URL is in hand — not at S10 when you'd otherwise write ads cold.
| API | thenetaji/tiktok-shop-creator-videos-scraper — 100% / 163 runs |
|---|---|
| In | product URL |
| Out | every showcase + affiliate video promoting it — videoUrl creatorName followers views likes |
| Store | Creative → feeds S10 directly |
Shippability filter
Free · kills ~halfA pure text filter that costs nothing and runs before any sourcing spend. Never pay to source something that can't fly.
| In | Product.title · category · description |
|---|---|
| Out | shipVerdict: pass | reject + shipReasons[] |
| Rejects | BATTERY — blocked outright (decision, 28 Aug) · liquids & aerosols · oversized · branded or counterfeit-risk · dead-season |
| Why blocked | NZ Post: lithium-metal prohibited; batteries sent alone prohibited; Li-ion in equipment allowed but max 2 batteries / 4 cells PER PARCEL, ≤100Wh, International Express Courier only — not ePacket, ADV550 declaration, and a UN38.3 Test Summary is law from 1 Jan 2026. Margin survived (~50%) — but freight per unit never improves with volume, so it cannot scale. |
| Measured | The naive filter had a 78% false-negative rate — "LED Face Mask", "Massage Gun", "Smart Ring" contain no battery word. Regex now catches coin cells and the electronics vocabulary that hides them. |
Reverse-image source
$0.005 / resultThis is the bridge. It is the single step that turns "trending" into "sourceable" — everything upstream is signal, everything downstream is commerce.
| API | devcake/scraper-by-image — 100% / 11,911 runs |
|---|---|
| In | Product.imageUrl |
| Out | 30 matches, 50+ fields — price_min price_max moq sold_90d month_sold years_as_gold_supplier factory_inspected delivery_hours service_score |
| Gotchas | provider takes one value per run — all three platforms = 3 runs, 3× cost. maxProducts has a minimum of 30. Matches are a candidate set, not an identification — an LED-strip image also returned car ambient lights, so a relevance filter belongs before S7. |
| Store | SourceMatch — this returns so much supplier data it may make S6 partly redundant; re-scope S6 to finalists only |
Supplier enrichment
$0.005 + $0.003 / product| API | zen-studio/1688-wholesale-scraper — 9 failures / 35,357 runs |
|---|---|
| In | 1688 product URLs from S5 |
| Out | tiered wholesale pricing · MOQ · supplier verification · factory stats · dropship channels · certifications |
| Store | SourceMatch — enriched in place |
Margin gate
FX onlyThe gate is a number, not an availability check. Something being purchasable is not the same as it being worth buying.
| API | FX rate CNY→NZD — still to be chosen. Cache daily; never call per product. |
|---|---|
| In | unitCostCny · FX · freight/unit · target sell price · NZ GST 15% · Meta CAC estimate · refund provision |
| Out | landedCostNzd netMarginNzd marginPct marginVerdict |
| Math | net = sellNzd/1.15 − landedCost − metaCac − (sellNzd × refundProvision) |
| Kill | US retail price FLOOR — applied before the growth ranking. Freight and CAC are near-fixed per sale, so a cheap product cannot carry them — see §01–03. At 500g (freight NZ$15.39, CAC NZ$12): break-even US$22.75, 25% net at US$33.00. Working rule: US retail ≥ US$30 AND under 500g. Heavy or cheap is dead however well it trends. |
| Note | CAC and freight are estimates until the first real campaign — the table moves with them, but the shape holds. |
Human gate
Greg · weeklyOne list, once a week. Nothing past this point is free — this is the last stop before ad spend.
| In | Candidate where status = pending, ranked by velocityScore |
|---|---|
| Out | status → approved | rejected · decidedAt · decidedNote |
Landing page
Shopify Admin API| API | Shopify Admin API (product + page create), or SettledLoop for a single-product funnel |
|---|---|
| In | approved Candidate + Creative |
| Out | live URL |
| Must | Honest delivery ETA on the page. This is the model (a) tax — skipping it converts into chargebacks, not sales. |
Meta ads
Meta Marketing API| API | Meta Marketing API — campaign / adset / ad create |
|---|---|
| In | LP URL + Creative from S3 |
| Out | campaign + ad IDs |
| Reuse | meta-ads · meta-creative-engine · meta-ads-copy skills already exist — assembly, not greenfield |
Monitor
Meta Insights + Shopify Orders| API | Meta Insights (spend, CPA) + Shopify Orders (actual sales) |
|---|---|
| In | campaign IDs · KILL_SPEND_USD · KILL_CPA_NZD |
| Out | continue | kill |
Kill criteria set before spend
"Monitor" is not a stage until it carries a number. Kill at $X spend with zero purchases, or CPA > $Y. Without thresholds fixed in advance, monitoring is just watching it bleed.
Scale
DecisionWinners only — and this is where the model (b) question returns. Pre-buying a validated winner into NZ stock cuts delivery to three days and lifts conversion, but that is a capital decision taken per product, on evidence, not a strategy switch.
07 / InventoryEvery API, and what's blocking it
| API | Stages | Auth | Status |
|---|---|---|---|
| Apify — 5 actors | S1 · S2b · S3 · S5 · S6 | APIFY_TOKEN | No token — hard blocker |
| FX rate CNY→NZD | S7 | tbd | Not chosen |
| Shopify Admin | S9 · S11 | store token | Available — Coy precedent |
| Meta Marketing + Insights | S10 · S11 | existing access | Available |
The only hard blocker
The Apify token. Everything at S2, S4, S7 and S8 is internal logic that can be built and tested against fixtures right now — that is a third of the pipeline, and the third that holds the actual IP.