TikTok Shop Hot Products ranked a men's outdoor sports football shoes SKU at #1 as of 2026-08-09T06:01:37Z. That board has no keyword time series, so the practical next step is to score the category term soccer cleats on Amazon, Google Shopping, and Google Search from Node.js. On the 2026-08-09 pull, Amazon sat at score 70.0 with volume 908,435 on 2026-07-31 (+11.29% over 30D, +112.12% over 6M). Google Shopping sat at 14.0 on 2026-08-08 (-39.13% over 30D). Google Search sat at 47.0 on 2026-08-08 (-18.97% over 30D). One Bearer POST, three sources, double-parsed JSON.
Shared Node client
Reuse the same helper as the Node.js Trends API integration. Native fetch on Node 18+, Bearer auth, second JSON.parse on body.
const API_URL = "https://api.trendsapi.ai/api";
const API_KEY = process.env.TRENDSAPI_API_KEY;
async function trendsApi(payload) {
const res = await fetch(API_URL, {
method: "POST",
headers: {
Authorization: `Bearer ${API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify(payload),
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const envelope = await res.json();
// body is a JSON string; parse it a second time
return JSON.parse(envelope.body);
}
Free tier is 100 successful requests per month. Starter 5,000. Pro 25,000. Business 100,000. Only HTTP 200 counts.
Step 1: read the live board
get_top_trends needs a type and no keyword. Limit 15 returned the football shoes listing at rank 1, salmon oil at rank 2, and LUMIFY eye drops at rank 3 on 2026-08-09.
const board = await trendsApi({
mode: "get_top_trends",
type: "TikTok Shop Hot Products",
limit: 15,
offset: 0,
});
console.log(board.as_of_ts, board.data.slice(0, 3));
Plain TikTok hashtag time series for soccer cleats returned no_data on this pull. Absence is a finding: the Shop board can surface a SKU while the hashtag series for the category term is empty. Keep the board timestamp and move to commerce sources that do resolve.
Step 2: Amazon product search volume
Amazon keywords are descriptive product phrases. Short presets can fail with collapsed_range when history is coarse. On 2026-08-09, 7D and 14D both returned collapsed_range for soccer cleats. 30D and longer windows succeeded. Recent Amazon points land on 2026-07-31.
| Period | Status | Recent value | Baseline date | Baseline value | Growth | Recent volume | Volume growth |
|---|---|---|---|---|---|---|---|
| 7D | collapsed_range | ||||||
| 14D | collapsed_range | ||||||
| 30D | success | 70.0 | 2026-06-30 | 62.9 | 11.29% | 908,435 | 11.24% |
| 3M | success | 70.0 | 2026-04-30 | 76.1 | -8.02% | 908,435 | -7.97% |
| 6M | success | 70.0 | 2026-01-31 | 33.0 | 112.12% | 908,435 | 112.27% |
| 12M | success | 70.0 | 2025-07-31 | 55.6 | 25.9% | 908,435 | 26.0% |
const amazon = await trendsApi({
mode: "get_growth",
source: "amazon",
keyword: "soccer cleats",
percent_growth: ["7D", "14D", "30D", "3M", "6M", "12M"],
});
for (const row of amazon.results) {
if (row.status === "error") {
console.log(row.period, row.error);
continue;
}
console.log(row.period, row.growth, row.recent_volume);
}
Amazon is up over 30D and 6M while 3M is slightly down. That pattern matters for inventory: short bounce inside a softer quarter, still well above the 6M baseline of 33.0. Source field notes: Amazon trends.
Step 3: Google Shopping interest
Google Shopping resolved every requested preset. Recent points land on 2026-08-08.
| Period | Recent value | Baseline date | Baseline value | Growth |
|---|---|---|---|---|
| 7D | 14.0 | 2026-08-01 | 12.0 | 16.67% |
| 14D | 14.0 | 2026-07-25 | 17.0 | -17.65% |
| 30D | 14.0 | 2026-07-11 | 23.0 | -39.13% |
| 3M | 14.0 | 2026-05-09 | 36.0 | -61.11% |
const shopping = await trendsApi({
mode: "get_growth",
source: "google shopping",
keyword: "soccer cleats",
percent_growth: ["7D", "14D", "30D", "3M"],
});
Shopping is weak on the longer windows: -39.13% over 30D and -61.11% over 3M, with only a small 7D bounce of 16.67% from 12.0 to 14.0. Docs: Google Shopping trends.
Step 4: Google Search as the demand sanity check
Search interest for the same keyword sits higher than Shopping on the 0-100 scale and softens more slowly.
| Period | Recent value | Baseline date | Baseline value | Growth |
|---|---|---|---|---|
| 7D | 47.0 | 2026-08-01 | 49.0 | -4.08% |
| 14D | 47.0 | 2026-07-25 | 55.0 | -14.55% |
| 30D | 47.0 | 2026-07-11 | 58.0 | -18.97% |
| 3M | 47.0 | 2026-05-09 | 76.0 | -38.16% |
Amazon volume up 11.24% over 30D while Google Search is down 18.97% and Google Shopping is down 39.13% over the same labeled window. Do not collapse those into one score without noting the units: Amazon returns absolute volume; Shopping and Search are relative 0-100 series. For Google Search field notes see Google Search trends.
Assemble a one-screen scorecard
async function cleatsScorecard() {
const sources = [
{
source: "amazon",
percent_growth: ["30D", "3M", "6M", "12M"],
},
{
source: "google shopping",
percent_growth: ["7D", "14D", "30D", "3M"],
},
{
source: "google search",
percent_growth: ["7D", "14D", "30D", "3M"],
},
];
const out = {};
for (const cfg of sources) {
// one source per call; multi-source strings can time out
out[cfg.source] = await trendsApi({
mode: "get_growth",
source: cfg.source,
keyword: "soccer cleats",
percent_growth: cfg.percent_growth,
});
}
return out;
}
const card = await cleatsScorecard();
console.log(JSON.stringify(card, null, 2));
Call sources sequentially. Comma-separated multi-source growth calls have timed out in prior sessions; single-source posts stay under the same contract and are easier to retry.
Map board titles to keywords carefully
The #1 TikTok Shop title on this pull was a long men's football shoes string, not the phrase soccer cleats. Searching Amazon for the full SKU title is usually worse than a clean category keyword: titles change, sizes and colorways pollute matches, and absolute volume dilutes. Start with the category term, then optionally add a second keyword for a brand or material if the board listing names one.
const candidates = ["soccer cleats", "football cleats", "soccer shoes"];
for (const keyword of candidates) {
const rowset = await trendsApi({
mode: "get_growth",
source: "amazon",
keyword,
percent_growth: ["30D", "3M", "6M"],
});
const ok = (rowset.results || []).filter((r) => r.status === "success");
console.log(keyword, ok.map((r) => [r.period, r.growth, r.recent_volume]));
}
Budget the free 100 requests: three keywords times three sources times one board call burns quota quickly. Cache the board response for the day. Persist growth rows with data_date so a later rerun can diff 30D without guessing.
Persist rows for cron diffs
A useful Node cron writes one JSON line per successful period. The next run loads yesterday and prints only periods whose growth moved by more than a threshold.
import fs from "node:fs";
function flatten(source, payload) {
return (payload.results || [])
.filter((r) => r.status === "success")
.map((r) => ({
pulled: "2026-08-09",
source,
keyword: "soccer cleats",
period: r.period,
recent_date: r.recent_date,
recent_value: r.recent_value,
growth: r.growth,
recent_volume: r.recent_volume ?? null,
volume_growth: r.volume_growth ?? null,
}));
}
// after cleatsScorecard()
const lines = [
...flatten("amazon", card.amazon),
...flatten("google shopping", card["google shopping"]),
...flatten("google search", card["google search"]),
];
fs.appendFileSync("cleats-signals.ndjson", lines.map((l) => JSON.stringify(l)).join("\n") + "\n");
On 2026-08-09 the Amazon 30D growth of 11.29% and Shopping 30D growth of -39.13% would both clear a 10-point absolute threshold. Search at -18.97% would clear a 15-point threshold. Pick thresholds per source; Amazon volume percent and Shopping score percent are not interchangeable.
How to read the 2026-08-09 result
- TikTok Shop board: football shoes SKU #1 at 2026-08-09T06:01:37Z; hashtag series for
soccer cleatsempty. - Amazon: score 70.0, volume 908,435 on 2026-07-31; 30D volume growth 11.24%; 6M volume growth 112.27%; 12M volume growth 26.0%.
- Google Shopping: score 14.0 on 2026-08-08; 30D -39.13%; 3M -61.11%.
- Google Search: score 47.0 on 2026-08-08; 30D -18.97%; 3M -38.16%.
- Amazon short presets:
7Dand14Dreturnedcollapsed_rangebecause recent and baseline resolved to the same point.
A buyer script can treat Amazon volume growth as the supply-side watchlist signal and Shopping or Search declines as a caution on paid discovery. Do not average the three 30D growth figures into one number; the units and calendars differ (Amazon recent date 2026-07-31 vs Shopping and Search on 2026-08-08). For the reusable Node client pattern (auth, double parse, npm volumes), start with Node.js Trends API. For a Python ecommerce research twin, see the earlier portable coffee maker product research post.