# TikTok Product Research & Video Growth Radar (`hirepulse/viral-product-radar`) Actor

Research TikTok product videos with source links, creator breadth and saved history. First run builds a baseline; repeat after 6+ hours to measure growth. JSON and HTML reports for people and agents. $0.02 per report; optional Clockworks collection costs extra.

- **URL**: https://apify.com/hirepulse/viral-product-radar.md
- **Developed by:** [HirePulse](https://apify.com/hirepulse) (community)
- **Categories:** Social media, Videos
- **Stats:** 2 total users, 1 monthly users, 50.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$20.00 / 1,000 product research reports

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## TikTok Product Research & Video Growth Radar

Rank product topics for further research using growth measured on the **same video
IDs at different collection times**. Show supporting videos, added views,
creator-account breadth, concentration, repeat growth and reasons to abstain.
Large lifetime counts and newly discovered old videos do not establish growth.

Direct anonymous collection failed in two Apify cloud checks. The analysis accepts
existing exports or an explicit, capped Clockworks opt-in. Importing a file never
starts an upstream Actor, subscription, proxy or schedule.

### First run in Apify

1. Start with one product keyword; the form uses `mini projector` as an example.
2. For live data, keep **Live videos via Clockworks**, review the total source cap,
   and check **I allow the separately capped Clockworks collection charge**.
   The checkbox is off until you consent. Click Start after reviewing the inputs.
3. One delivered report costs **$0.02**. With the default **$0.04 total collection
   cap**, one product permits at most **$0.06 in report plus source events**.
   Ten collected videos at the checked $0.003 rate usually make that $0.05.
4. Open REPORT for the readable report and OUTPUT for source evidence. First
   collection creates a baseline. Repeat the same `state_id` after 6+ hours.

For an existing export, select **Existing video export** (`upstream_source=public`)
and supply `video_snapshot` with its original timestamp. No collection provider
is started. Direct anonymous collection is not supported in the Apify cloud
product-video workflow. Missing consent/export produces `configuration_required`
with setup steps before any source request or report billing event.

API callers may omit `upstream_source` when supplying an export. For live data,
`allow_paid_upstream=true` is required; an omitted source then selects Clockworks.
Explicit `public` never silently falls back to a paid source. Local direct-public
research and the separate legacy mode remain available.

### For people and agents

Every observation has a research-priority score, classification, next action,
per-video comparison windows, source URLs and blockers. `agent_decision` includes
rationale, missing evidence and next checks for relevance, sourcing, landed cost
and fulfilment. Attention does not establish sales, conversion or margin.
Creators are distinct public accounts; their business independence is unknown.

### Pricing: $0.02 per delivered product research report

The Actor charges one `apify-default-dataset-item` event at $0.02 for each
product report written to the default dataset. No startup fee or monthly
subscription is configured. A baseline or watch report is a completed research
report, even when it does not recommend further investigation. A source failure
with no report produces no report charge. Failed or blocked upstream collection
can still incur the upstream provider's separately capped charges.

Clockworks collection is billed separately to the invoking account, within
`upstream_max_charge_usd`. At the checked source price of $0.003 per video, one
product report using ten videos is approximately $0.05 ($0.02 analysis + $0.03
collection). Fewer collected videos cost less. Check the current provider pricing
in Apify; the cap remains in force if its pricing changes. Existing export imports
do not start Clockworks. Platform account charges outside these events may apply
according to your Apify plan.

The parent run's spending limit is checked against the number of requested reports
before any upstream Actor starts. Every charged report is a delivered dataset item.
An unchanged reimport with the same state ID and acknowledged source timestamp is
not delivered again. `deliver_all_reports=true` is the default and includes new
baseline, watch and candidate observations; alert identity and deduplication remain
separate. Set `deliver_all_reports=false` to select the legacy alert/baseline feed;
only records it actually delivers incur a report event. Unchanged alerts do not
become new alerts merely because another research report is delivered.

### Input and collection

`analysis_mode=product_videos` is the default. Supply explicit product keywords.
Public `video_urls` can seed collection where pages expose their data. Without
URLs the public challenge/video paths are attempted; empty or blocked sources
fail explicitly. Direct-public collection is blocked by the cloud setup guard before collection.
This mode does not query Shop, ads, global sounds or unrelated Creative Center
discovery. `analysis_mode=multi_signal` retains the old engine in a separate state
namespace; `Run-PublicRadar.ps1` explicitly selects the legacy hashtag path.

An existing export uses this envelope:

```json
{
  "observed_at": "2026-10-23T12:00:00Z",
  "source": "source name and collection run",
  "records": []
}
```

Records need exact view counters, matching product descriptions and canonical
public TikTok video URLs. The parser accepts native `view_count`, `description`,
`creator`, `url` or export fields `playCount`, `text`, `authorMeta.name` /
`authorMeta: {name: ...}`, `webVideoUrl`. Likes, comments, shares, publication time
and sponsorship flags are optional. These names match the
[documented Clockworks output](https://apify.com/clockworks/tiktok-scraper), but
the same formats are used by the optional collection integration below.

### Optional capped cloud collection

```json
{
  "analysis_mode": "product_videos",
  "keywords": ["mini projector"],
  "state_id": "my-product-research",
  "upstream_source": "clockworks",
  "allow_paid_upstream": true,
  "upstream_max_charge_usd": 0.04,
  "max_items": 10,
  "max_requests": 60,
  "max_runtime_seconds": 120
}
```

The first collection uses `clockworks/tiktok-hashtag-scraper` (build 0.0.474).
With the same watchlist ID, later runs use `clockworks/tiktok-video-scraper`
(build 0.0.477) to refresh the already observed video URLs. `video_urls` can also
seed an explicit cohort. Existing video cohorts are followed rather than silently
replaced with unrelated search results. This is cohort research, not full-market
product discovery. Start another watchlist to research a new sample.

The upstream PPE spending cap is split across all keywords, with at most one
upstream run per keyword. Each must have at least a $0.009 allocation; reduce
the keyword count when the budget is too small. The cap defaults to $0.04 total
and requires a separate `allow_paid_upstream=true` opt-in; it is not a monthly
subscription. Parent Actor platform usage is separate. Current pricing and
limited permissions are checked before a child run is started. Video downloads,
covers, slideshows, related-video expansion and subtitles/transcripts are disabled.
Child runs have a charge cap and timeout; interrupted collection requests abort.

The collection time is the end of the source run's recorded collection window,
not the later analysis time. The complete window and upstream run IDs are exposed
in OUTPUT. Fine timing within that window is unknown. All measured growth is
still subject to the six-hour minimum comparison window. A successful first run
establishes a baseline and cannot create a growth alert yet.

`observed_at` means when counters were collected, not when a file was downloaded
or imported. Future, mixed-time, conflicting reimports and out-of-order observations
are rejected. A repeated export cannot become a new measurement. Imports older
than 24 hours can establish history but cannot issue an alert now.

```powershell
./Run-VideoRadar.ps1 -SnapshotFile C:/data/observation.json -Keywords 'mini projector' -StateId projector-research
```

Reuse the state ID and keywords with later exports. At most 100 records can be
imported per run; `max_items` caps each product sample. The import path makes no
outbound source requests. All observations, including non-alerts, are saved to
`product-video-data/history/`. `OUTPUT.json` contains every ranked observation;
the dataset contains delivered research reports by default. `REPORT.html`
provides a Hebrew decision report with the same measurements and blockers. Cloud
runs expose the HTML report in their output as well as the machine-readable JSON.

### Scoring policy

Comparisons require 6-72 hours between measurements of the same video and creator.
Counter decreases are excluded as corrections. New IDs establish baselines only.
The daily rate is normalized from the actual window, not a forecast.

| Component | Points | Calculation |
| --- | ---: | --- |
| Momentum | 40 | Daily percentage lift saturates at 50%; daily added views saturate at 10,000 |
| Creator breadth | 25 | Accounts adding at least 1,000 views/day and 20%/day, saturating at five |
| Engagement | 15 | Mean interaction-delta/view-delta ratio on videos adding at least 1,000 views, saturating at 5%; missing counters stay unknown |
| Persistence | 20 | At least 500 added views and 20%/day in each of two non-overlapping intervals, saturating at three accounts |

Alerts also need three repeatedly observed videos, growth from three distinct
accounts, 3,000 actual added views, at least 20% normalized daily growth, no creator
responsible for more than 70% of measured daily growth, current source data and
the configured `min_score` (default 30). These are explicit heuristics awaiting
validation, not calibrated probabilities. First observations are baselines.
Missing comparisons or concentrated growth cause abstention. Delivery must
succeed before a fingerprint is acknowledged.

### Measure whether ranking helps

```powershell
python -m src.evaluation --history product-video-data/history --output product-video-data/evaluation.json --top-k 3
```

Rankings are frozen at decision time, then labeled from the same videos around
24 hours later (18-30 hours). Labels require at least 80% coverage and three
matched video IDs. The target is continued attention: 3,000 added views, 20%
daily growth and three growing accounts. Missing future data stays pending.
Replays are deduplicated; an unobserved choice is not replaced with a lower-ranked
measured one. Radar top-k considers actual research alerts; no-alert batches abstain.
The popularity comparator selects the same number of products by lifetime views.
The precision difference uses fully observed paired batches only and reports the
number of pending comparison batches. Alert precision is also measured separately.
This does not test profit, causality or market-wide
discovery. Overlapping windows are not independent trials. No live forward
performance claim is made.

`python scripts/video_demo.py` produces a clearly labeled synthetic example of an
emerging product, an established high-view product and a single-creator spike.
Its outputs illustrate and test decisions; they are not market evidence.

# Actor input Schema

## `keywords` (type: `array`):

Start with one specific product. Each delivered product report costs $0.02; the collection cap is shared across all keywords.

## `upstream_source` (type: `string`):

Cloud quick start: select Clockworks and check the collection consent box. Select public only to import an existing video_snapshot; direct anonymous cloud collection is not supported.

## `allow_paid_upstream` (type: `boolean`):

Required for live collection, off by default. At most upstream_max_charge_usd for all products together, plus $0.02 per delivered report. Import an existing export to avoid collection fees.

## `upstream_max_charge_usd` (type: `number`):

Hard total PPE limit split across all product keywords. First observations use a hashtag source; subsequent runs refresh the same tracked video URLs. Parent Actor platform usage is separate.

## `max_items` (type: `integer`):

Maximum parsed records per source and keyword; rounded sales counters remain display-only.

## `state_id` (type: `string`):

Use the same ID for repeat observations of one watchlist. Different IDs isolate histories.

## `video_snapshot` (type: `object`):

Optional existing public-data export. Supply its actual collection time, source label and up to 100 records. No upstream service is started. Native and Clockworks JSON field names are supported.

## `video_urls` (type: `array`):

Optional public TikTok video URLs. Only videos matching a keyword contribute linked sound evidence.

## `analysis_mode` (type: `string`):

Product-video mode scores repeated video measurements and creator breadth. Legacy mode retains the previous four-channel engine.

## `discovery_terms` (type: `array`):

Commerce topic terms used to filter discovered hashtags; unrelated entertainment trends are excluded.

## `country` (type: `string`):

ISO two-letter region for Creative Center and public Shop pages.

## `period_days` (type: `integer`):

Creative Center trend window, in days. This is separate from observed run-to-run growth.

## `proxyConfiguration` (type: `object`):

Optional customer proxy configuration. No proxy is used by default.

## `allow_paid_proxy` (type: `boolean`):

Explicitly permit billable Apify proxy traffic. Disabled by default.

## `product_urls` (type: `array`):

Optional public TikTok Shop product URLs. Only relevant listings are used for sales-counter comparisons.

## `min_score` (type: `integer`):

Minimum temporal score for a growth alert. Baselines are separately labeled and are not alerts.

## `discovery_limit` (type: `integer`):

Maximum additional matching hashtags to include from public trend discovery.

## `max_requests` (type: `integer`):

Hard cap on outbound public requests for this run, including fallbacks.

## `max_runtime_seconds` (type: `integer`):

Network time budget in seconds. The Actor can finish earlier when the request cap is reached.

## `discover` (type: `boolean`):

Inspect public Creative Center trend records and add matching commerce topics.

## `include_baseline` (type: `boolean`):

Deliver explicit first-observation baseline rows, with is_alert=false.

## `only_changes` (type: `boolean`):

Suppress repeated delivery of the same signal fingerprint.

## `deliver_all_reports` (type: `boolean`):

Deliver baselines, watch reports and candidates for every new observation. Each delivered report is billable. Reimporting the same acknowledged observation is suppressed. False selects the legacy alert/baseline feed.

## `collect_ads` (type: `boolean`):

Attempt public Creative Center ad collection. Permission failures are reported, not bypassed.

## `collect_shop` (type: `boolean`):

Attempt public product listing collection. Missing sales counters do not mean zero sales.

## `collect_videos` (type: `boolean`):

Attempt public matching-video collection and linked sound statistics.

## Actor input object example

```json
{
  "keywords": [
    "mini projector"
  ],
  "upstream_source": "clockworks",
  "allow_paid_upstream": false,
  "upstream_max_charge_usd": 0.04,
  "max_items": 10,
  "state_id": "default",
  "analysis_mode": "product_videos",
  "discovery_terms": [
    "product",
    "finds",
    "shop",
    "skin",
    "clean",
    "kitchen",
    "makeup",
    "beauty",
    "gadget",
    "home",
    "fashion",
    "fitness"
  ],
  "country": "US",
  "period_days": 7,
  "proxyConfiguration": {
    "useApifyProxy": false
  },
  "allow_paid_proxy": false,
  "min_score": 30,
  "discovery_limit": 10,
  "max_requests": 60,
  "max_runtime_seconds": 120,
  "discover": true,
  "include_baseline": true,
  "only_changes": true,
  "deliver_all_reports": true,
  "collect_ads": true,
  "collect_shop": true,
  "collect_videos": true
}
```

# Actor output Schema

## `humanReport` (type: `string`):

No description

## `results` (type: `string`):

No description

## `runSummary` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("hirepulse/viral-product-radar").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "proxyConfiguration": { "useApifyProxy": False } }

# Run the Actor and wait for it to finish
run = client.actor("hirepulse/viral-product-radar").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call hirepulse/viral-product-radar --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,hirepulse/viral-product-radar"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/4qV3txMmMKxtxbQG1/builds/AgbPiLAfgjMxBfDAe/openapi.json
