- name
- clipcat
- description
- All-in-one TikTok Shop selling-video skill for any AI agent (Claude Code, Codex, WorkBuddy, OpenClaw). Find viral TikTok videos, research TikTok Shop products, shops, creators and live rooms, break down why a video sells (script, scenes, hooks, music), search the largest library of real high-GMV AI selling videos and their reverse-engineered prompts and adapt the closest match to your own product, replicate a winning video, turn product photos — or your own raw prompt and images — into AI selling / UGC / talking-head / product-demo videos, generate e-commerce images from a text prompt, upscale to 1080p or 2K, and download TikTok or Douyin videos. Keywords — AI selling video, TikTok viral replication, TikTok Shop product research, competitor shop analysis, creator and influencer ranking, AI selling video prompt library, product-to-video, UGC video generator, AI product image, TikTok video downloader. Use whenever the user needs TikTok e-commerce data, viral video research, or AI video/image generation.
- user-invocable
- true
- metadata
- {"openclaw":{"requires":{"env":"[Truncated]"},"primaryEnv":"CLIPCAT_API_KEY"},"homepage":"https://clipcat.ai"}
# Clipcat CLI
This skill is intentionally short. Detailed flags and supported values belong to the CLI itself — always treat `clipcat -h` and `clipcat <subcommand> -h` as the primary reference. The one thing `-h` cannot be current about is the model catalog: models come and go between releases, so `clipcat models` is the authority on which models, resolutions and durations exist right now.
## Installation
Run `clipcat --version` first — if it prints a version, clipcat is installed; skip to API key. If the command is missing, install for the platform:
macOS / Linux / Git Bash:
```bash
curl -fsSL https://clipcat.ai/cli | bash
```
Windows (PowerShell, no bash):
```powershell
irm https://clipcat.ai/cli.ps1 | iex
```
Then set the API key (see below). Update later with `clipcat update` (re-runs the installer; your saved config is preserved).
### Windows sandbox note (Codex etc.)
If the Windows install fails with `SEC_E_NO_CREDENTIALS`, `AcquireCredentialsHandle`, `0x8009030E`, "The underlying connection was closed", or 「基础连接已经关闭」, you are in a restricted sandbox (e.g. the Codex Windows sandbox) where the Windows TLS stack (Schannel) can't open credentials — `Invoke-WebRequest` and system `curl.exe` both fail there. The installer automatically retries the download through Node (its OpenSSL bypasses Schannel), so installing Node in the sandbox usually fixes it. If it still fails, **show the install command to the user and ask them to run it in a normal PowerShell outside the sandbox, or to approve running it outside the sandbox — do not keep retrying with different commands.**
## API key
Configure the key in the local config file — the only reliable method:
```bash
clipcat config --api-key <your-key> --base-url https://clipcat.ai
```
Get the key at https://clipcat.ai/workspace?modal=settings&tab=apikeys. Prefer the config file over the `CLIPCAT_API_KEY` environment variable: sandboxed agents (e.g. Codex) filter out env vars whose names contain KEY/SECRET/TOKEN, so it is usually invisible there. (OpenClaw injects `CLIPCAT_API_KEY` automatically; when set, it overrides the config file.)
## What this CLI is for
`clipcat` is the local entrypoint for all Clipcat AI video generation workflows:
- Query TikTok e-commerce data: creators, products, shops, videos, lives, search
- Generate a ready-to-shoot selling-video prompt from the viral prompt library
- Replicate viral videos with your product
- Generate product videos from images
- Generate a video straight from your own prompt and assets, sent to the model verbatim
- Generate AI images from text prompts using GPT Image 2 / GPT Image 2.5 (Flare / Sunburst), with optional reference images
- Turn a script into an mp3 voice-over (text-to-speech, preset or cloned voice), reusable as reference audio
- Analyze videos (script, scenes, music)
- Download TikTok/Douyin videos
- Publish finished videos to your own TikTok accounts (direct post or drafts inbox)
- Schedule an Agent prompt to run by itself every day or on chosen weekdays
- Query async task status
## Default agent workflow
1. Start with `clipcat -h` to see all commands.
2. Before using any command, run `clipcat <subcommand> -h` to see flags.
3. Default to JSON output.
4. `replicate` / `product_video` / `generate` / `tiktok publish` submit in TWO calls:
the first charges nothing, creates nothing and returns a checklist + `confirmId`; you
show that checklist to the user, wait for an explicit yes, then run
`--confirm <confirmId>` (see "Two-step confirmation").
5. If any command prints an update notice on stderr (`⬆ clipcat X is
available … Run: clipcat update`), run `clipcat update` once, then continue.
It self-skips when already up to date, so it is safe to run.
## Choosing the right command
### TikTok e-commerce data — entity commands
These are noun-verb commands: `clipcat <entity> <verb>`. Run `clipcat <entity> -h`
to list verbs and `clipcat <entity> <verb> -h` for flags.
- `creator <list|rank|profile|enrich|trend|posts|sales-videos|lives|products|followers|following|region|milestones>` — TikTok creators/influencers
- `product <list|rank|detail|trend|reviews|live-comments|creators|videos|lives>` — TikTok Shop products
- `seller <list|rank|detail|trend|catalog|inventory|creators|videos|lives>` — TikTok Shop shops
- `video <list|rank|snapshot|sales|trend|comments|captions|products|hashtag>` — TikTok videos
- `live detail` — live-room detail (only while live)
- `find <creators|products|videos|lives|hashtags|music|photo|all>` — keyword/image search; `find all` is the broad fallback
**Two data sources, and the command name already picks one for you.** There is no
`--mode` flag to reason about — pick by what you need back:
| You need | Command | What you get | What you don't |
|---|---|---|---|
| A creator's recent posts | `creator posts` | any public creator, newest first | no per-video sales/GMV |
| A creator's shoppable videos | `creator sales-videos` | sales + GMV per video, sortable | only creators in the historical dataset |
| A creator's profile now | `creator profile` | any public creator | no cumulative commerce metrics |
| Commerce metrics for many creators | `creator enrich` | batch ≤10, cumulative metrics | only collected creators |
| One video's current state | `video snapshot` | any public video | no sales/GMV |
| Sales for videos you already have ids for | `video sales` | batch ≤10, sales + GMV | only collected videos |
| Reviews you can filter by rating | `product reviews` | rating filters, paging | slightly staler |
| The freshest comments | `product live-comments` | latest, needs `--region` | no rating filter |
| A shop's history incl. removed items | `seller catalog` | sales + GMV, sortable | not what's listed right now |
| What a shop lists right now | `seller inventory` | current, needs `--region` | no sales/GMV |
**The historical dataset does not cover everything** (collection is capped by cost),
so the `sales` / `catalog` / `enrich` side answers "not collected" fairly often —
roughly 4-6 times in 10 when the id came from a live search. Ids taken from
`… rank` / `… list` are in the dataset by construction and hit nearly every time.
An empty result there means *not collected*, not *does not exist* — check with the
live command instead of retrying. Never expose the words offline/realtime to end
users; say historical vs. latest data.
**Pagination**: each call returns one page and is billed once. Historical
list/rank commands take `--page` / `--page-size` (**`--page-size` maxes out at 10**;
larger values are clamped and the response says so in `pagination_corrected` — get
more rows with `--page 2`, `--page 3`, …); live lists take `--offset` /
`--cursor` / `--scroll-param` echoed back from a prior page. Fetch more by
repeating the command page by page (`--max-pages` is deprecated and ignored).
**The two data sources do not share a paging scheme.** Historical commands
(`creator sales-videos`, `product reviews`, `seller catalog`) page by number; their
live counterparts (`creator posts`, `product live-comments`, `seller inventory`)
page by cursor, and a page number cannot become a cursor. If you page a live list
with `--page`, the CLI rejects it outright; if an older client sends it anyway,
the response carries `pagination_ignored` — that means **this is the source's first
page**, not the page you asked for. Stop paging by your original number and continue
with the token in `next`; repeating the number returns the same rows and bills 6
credits again.
**Empty is an answer, not a failure.** The historical dataset does not cover
everything, so an empty result usually means "not in that dataset" rather than "no
such thing". The response then carries `try_instead` with a ready-to-run command for
the other source, plus what you gain (live: full coverage, no sales/GMV; historical:
sales/GMV and sorting, covered entities only) and any flags you still need to add.
Switching sources is a separate billed call — switch only if you need those fields.
Do not retry the same empty query.
**Errors tell you whether to retry.** Failures carry `error_kind` and `retryable`:
`transient` (rate limit or a brief wobble — retry the same command in a few
seconds), `invalid_params` (the message says exactly what is wrong — fix the flag,
never retry as-is), `temporarily_unavailable` (retrying will not help; change the
query or come back later).
**Insufficient credits**: read commands cost 6 credits each (`prompt search` is 3, charged only after the free allowance included with your plan is used up); below that balance they error out and return no data.
**Data-query playbook (dense):**
- **Chain ids, don't guess them.** Discover first (`<entity> list|rank`, `find …`),
take the id from the result, then call detail / trend / relationship verbs.
Batch verbs take **comma-separated ids** (`--user-ids`, `--product-ids`,
`--video-ids`, ≤10).
- **Where the id came from decides which command can answer.** Ids from `find …`
(live search) are any public entity, so follow them with the live commands —
`video snapshot`, `creator posts`, `creator profile`. Ids from `… rank` / `… list`
are in the historical dataset by construction, so those are the ones to follow with
`video sales`, `creator sales-videos`, `creator enrich`, `seller catalog`. Running a
live-search id straight into a sales command is the single most common way to burn
credits on empty results — a `find videos` id misses the sales dataset about 4 times
in 10. If you need sales figures for something you found live, say so plainly rather
than paging for data that was never collected.
- **Seed relationships from commerce-active entities.** Sub-resource verbs
(`creator products|lives`, `product creators|videos|lives`, `seller lives`,
`video products`) return `[]` for low-activity ids. Pull seeds from `… rank` or a
sorted `… list` (top sales/followers), not an arbitrary row, or expect empties.
- **`… rank` needs a *recent* `--date`.** Pass any day in the target period — the backend
auto-snaps it to the period anchor (week→that week's Monday, month→that month's 1st).
It never silently serves a *different* period: if the period hasn't ended, or its data
isn't generated yet (T+1, usually after midday), you get `data: []` plus `period`
(`requested` / `latest_available` / `previous`, each with `anchor`/`start`/`end`) and a
`hint` naming the exact `--date` to retry with — follow it instead of re-querying the same
period. The date must fall within the freshness window keyed to `--rank-type`:
**day ≤30d, week ≤6mo, month ≤12mo** back from *today*. A too-**old** date (e.g. last year)
is rejected upstream as `rant_type N only support …` — move it **forward toward today**;
don't switch rank-type.
- **Category filtering is numeric and split by level.** To scope `rank` / `list` to a
category, first run `category resolve --keyword <term>` (e.g. `lipstick` / `口红`; CJK
auto-uses the zh tree). It returns each match's level + ancestor ids `{l1_id, l2_id?,
l3_id?}` (ids work for any region). Pass the id for the level the target command takes:
**product/seller** rank/list use **L1→`--category-id`, L2→`--category-l2-id`,
L3→`--category-l3-id`** (`--category-id` is L1-only — don't put an L2/L3 id there).
The levels you pass must form **one parent-child chain**; a repeated or mismatched id is
rejected locally (costs nothing) with the offending fields in `issues` and the correct ids
in `suggested` — copy those and resend. Each entry in `issues` carries `field`, `reason`,
`value`, a localized `message`, and a structured `detail` (the machine-readable form of
the same thing — prefer `detail` when branching in code, `message` when showing a human). Then:
**creator** rank takes any level via `--product-category-id`; **video** rank only
accepts L1 (`l1_id`) — pass an L2/L3 id there and it is auto-lifted to its L1 ancestor,
which **widens** the filter (the response says so in `category_level_corrected`). Low-confidence `hint` → run `category tree` (L1+L2 overview), pick
the branch by meaning, then `category tree --parent <that L2 id>` to drill into its L3
leaves. For plain keyword *search* (no leaderboard), `find products --keyword` needs no id.
- **`find products` returns product_id only** (it's a search index). For title /
price / metrics, chain the ids into `product detail`.
- **Empty `[]` / `null` means "none", not an error.** A repeat of the same empty query may
come back with `cached: true` + `retry_after` (an ISO timestamp): the backend remembered
that this filter has no data and re-probes automatically after that time — don't poll it,
change the filter or move on. Known thin/quirky:
`creator region` (unreliable → read `region` from `creator profile` instead),
`video captions` (many videos have none), `live detail` (only while a room is
live), `seller inventory` (empty when a shop lists nothing right now — use
`seller catalog` for its history).
- Responses are **server-trimmed to signal** (ids, core metrics, names, key links;
images already converted to accessible URLs) — no raw-blob handling needed.
- **All monetary values are USD.** Every price / avg-price / GMV field (`min_price`,
`max_price`, `spu_avg_price`, `*_gmv_*_amt`, …) is a USD-converted number, regardless
of `--region`; the response carries `"currency": "USD"` to confirm it. Never label
them with a local symbol like `¥`/`円`. If a report needs the local currency (e.g.
JPY for a Japan market study), convert from USD using a current FX rate and mark the
result approximate.
### Viral selling-prompt generator — `clipcat prompt search`
Clipcat's own library of **structured prompts**, each reverse-engineered from a TikTok
video that actually drove sales — every TikTok market and category, ranked by real GMV.
This is not TikTok search: entries here are already broken down and rewritten into a
prompt you can hand to a video model as-is.
**When the user asks for a prompt, idea, script or angle for a selling video, start here
instead of writing one from scratch.** A prompt with a proven video behind it is the whole
point; an invented one is only a guess, and the user cannot tell the two apart.
#### Step 1 — find the closest proven videos
- `prompt search --query "<what you want>"` — semantic + keyword search over the library.
Describe a feel ("warm indoor light, handheld close-up, real person on camera") or
name something exact (a brand, `ASMR`, `OOTD`) — both work; the two are fused, so you
do not have to guess which style of query fits. Optional filters: `--region` (lowercase
market code), `--category` (TikTok Shop L1 code, e.g. `beauty-personal-care`),
`--video-type` (`real-review` | `ootd` | `asmr` | `unboxing-pov` | …), `--limit` (1-20).
Build the query from the user's own product and audience — what it is, who it is for,
the market, the vibe they asked for. A bare category name ("skincare") retrieves the
generic middle of the library.
Priced apart from the other read commands: each paid plan comes with an allowance of
free searches, and calls beyond it cost 3 credits each (other reads are a flat 6).
The response carries `quota.remaining` / `quota.free_quota` / `quota.cost_after_quota` —
tell the user what is left when it runs low instead of letting the next call surprise them.
- **Check `weak_match` and `degraded` before you trust the hits.** The library returns the
nearest entries it has, so a full result list does not by itself mean the results fit.
`weak_match: true` means nothing closely matches — say so and suggest rewording or
dropping a filter, rather than presenting the nearest entries as the answer.
`degraded: true` means semantic search was unavailable and only keyword matching ran:
results may be incomplete, and **that search is not charged** (quota is refunded).
Fewer hits than `--limit` is normal and healthy — only entries relevant enough are
returned, so a narrow `--region` + `--category` combination legitimately returns a few.
Each hit carries the full `prompt` text (`prompt_en` for the English version), the metrics
of the original video (GMV, sales, views), `matched_facet` (which part of the prompt your
query hit — style / camera / voiceover / …), `source_video_url` for the original TikTok
video, and `detail_url` for the public page.
#### Step 2 — rewrite the hit into the user's own prompt
Never hand back a library prompt unchanged: it sells someone else's product. Rewrite the
best hit (or 2-3 hits that agree on structure — averaging ones that disagree yields a
template) into a prompt for this user's product:
- **Keep what made it sell**: the opening hook and what happens in its first 1-2 seconds,
shot order and pacing, camera language, lighting, whether a presenter is on camera and
what kind, voiceover tone, promo mechanic, closing CTA.
- **Swap** the product and its selling points, on-screen text, voiceover lines, and
anything market-specific (language, currency, local wording).
- **Carry over no claim you cannot back.** Ratings, sales numbers, awards, before/after and
efficacy claims belong to the original product — drop them, or ask the user for their own.
- **Fit the target model**: keep the prompt inside the `--duration` you will submit and the
shot count it implies (a 5s clip holds 2 shots, not 6), and pick the voiceover language
with `--lang` (required — pick it deliberately, the CLI has no default).
- Show the user the finished prompt with the `detail_url` (and `source_video_url`) it was
built from **before** spending credits — citing the real video is what separates this
from a prompt you made up.
#### Step 3 — shoot it
- Product images only → `product_video`, passing the rewritten prompt via `--prompt-file -`.
- Want the original video's motion and cuts as the reference → `replicate
--url <source_video_url>` with the user's `--image`s (a TikTok link adds the 10-credit
download surcharge).
- Both are paid and two-step: submit → show the returned checklist to the user → wait for
an explicit yes → `--confirm <confirmId>` (see "Two-step confirmation").
```bash
clipcat prompt search --query "handheld close-up of a serum bottle, warm bathroom light, real user voiceover" \
--region us --category beauty-personal-care --limit 5
# pick a hit → rewrite its prompt for the user's product → step 1 (no charge):
Ver en GitHub