AI-powered Amazon product research. Respond in user's language.
FilePurpose{skill_base_dir}/scripts/zoodata.pyExecute for all API calls (run --help for params){skill_base_dir}/references/reference.mdLoad when you need exact field names or filter details
Required: ZOODATA_API_KEY. Get free key at zoodata.ai/api-keys. Stored in {skill_base_dir}/config.json in skill root.
https://api.zoodata.ai (Bearer ZOODATA_API_KEY). Setting ZOODATA_BASE_URL to an untrusted host (anything other than api.zoodata.ai / *.zoodata.ai / localhost) makes the CLI refuse the request and withhold the key — the Bearer token is never sent to an untrusted host.{skill_base_dir}/scripts/zoodata.py (Python 3, stdlib-only). This skill allows categories, market, products, competitors, product, analyze, report, opportunity, history, check, plus the review fallback toolkit ( reviews-raw / review-tag-prompt / review-reduce-prompt / review-aggregate). Do not invoke unrelated subcommands for this skill's tasks — the bundled manifest {skill_base_dir}/scripts/allowed-commands.json enforces this: the CLI refuses out-of-scope subcommands with a structured COMMAND_NOT_ALLOWED error before any API request.mktemp -d, removed when the fallback completes) during the review fallback; reads the optional credential store ~/.zoodata/config.json.Before selecting or invoking the first command, read and apply the local references/cli-contract.md. Reapply it after every granular or composite result and before any fallback, additional call, state write, interpretation, or user-facing report. Use this skill's fallback logic only when the shared contract classifies the result as non-terminal.
For a terminal interface failure, respond in the user's language with one concise notice that the Amazon analysis could not be completed, followed by the succeeded and failed endpoint identifiers. Do not render analysis findings, recommendations, API-usage tables, or another workflow choice. Keep control tokens, parameters, and retry logs internal unless diagnostics are requested.
User provides: keyword, category, ASIN, or brand — depending on intent. Use intent routing below.
Category first: keyword search is broad → MUST lock categoryPath via categories endpoint before other calls
Brand + category: Brand queries MUST include --category to avoid cross-category contamination
Use API fields directly: revenue= sampleAvgMonthlyRevenue (NEVER calculate price×sales), sales= monthlySalesFloor (lower bound), opportunity= sampleOpportunityIndex
reviews/analysis: needs 50+ reviews per ASIN; try category mode first (single call returns all dimensions), ASIN mode only if category call fails. Filter by labelType client-side from the consumerInsights array. Fallback chain when sample is insufficient:
realtime/product ratingBreakdown — only star distribution, no themes/reviews/analysis entirely:
a. zoodata.py reviews-raw --asin X → fetch up to 100 raw reviews (10 credits, ~60s)
b. For each review: render Map prompt via zoodata.py review-tag-prompt --review '<json>'
and have your own LLM produce JSON tags (sentiment + 11 dimensions)
c. Collect candidate phrases per dimension; for each dimension render
Reduce prompt via zoodata.py review-reduce-prompt --label-type X --candidates '[...]'
and have your LLM produce semantic clusters
d. zoodata.py review-aggregate --reviews R --tagged T --clusters C
→ consumerInsights output compatible with /reviews/analysisAggregation without categoryPath: produces severely distorted data
Check the data shape before indexing: many search/list endpoints return .data as an array, so use .data[0] for the first record in those cases; some commands return non-array payloads inside data
labelType: NOT an API request parameter — it is a field in the response consumerInsights array, used for client-side filtering
history empty: try oldest-listed ASINs first, up to 3 rounds of different ASINs before giving up
Sales null fallback: Monthly sales ≈ 300,000 / BSR^0.65
When ZOODATA_API_KEY is not set (verify via python {skill_base_dir}/scripts/zoodata.py check — exits 2 if no key in env or ~/.zoodata/config.json), stop before any evidence call. Tell the user that a ZooData API key is required, link to https://zoodata.ai/en/api-keys, and explain that the key may be set in the environment or local config. Do not substitute public knowledge or a "for reference only" analysis.
When _transport.status=401, stop further calls, tell the user that the configured key was rejected, direct them to https://zoodata.ai/en/api-keys, and do not fabricate missing data.
When _transport.status=402, stop further calls. Report where the workflow stopped, any compatible partial findings already gathered, and returned credit metadata when present; direct the user to https://zoodata.ai/en/pricing and do not fabricate missing data.
Modes are CLI-local presets, NOT API parameters.
zoodata.pyexpands--modeinto real filter fields before the call — copy them fromPRODUCT_MODESin{skill_base_dir}/scripts/zoodata.pyif you bypass the CLI. For a rawproducts/searchrequest, never sendmode,salesMin, orratingsMax; use the expanded API filters, distinguishratingMaxfromratingCountMax, and sendcategoryPathas a JSON array.
ModeOne-line Descriptionfast-moversMonthly sales≥300, growth≥10% — quick turnoveremergingMonthly sales≤600, growth≥10%, ≤6 months oldsingle-variantGrowth≥20%, 1 variant, ≤6 months — small & risinghigh-demand-low-barrierMonthly sales≥300, reviews≤50 — easy entrylong-tailBSR 10K-50K, ≤$30, exclusive sellers — nicheunderservedMonthly sales≥300, rating≤3.7 — improvable productsnew-releaseMonthly sales≤500, New Release tagfbm-friendlyMonthly sales≥300, self-fulfilledlow-price≤$10 productsbroad-catalogBSR growth≥99%, reviews≤10, ≤90 daysselective-catalogBSR growth≥99%, ≤90 daysspeculativeMonthly sales≥600, ≥3 sellerstop-bsrBSR≤1000 best sellers
Modes can combine with explicit filters ( --price-max, --sales-min, etc). Overrides win.
report --keyword X → categories + market + products(top50) + realtime(top1)opportunity --keyword X [--mode Y] → categories + market + products(filtered) + realtime(top3)Every analysis should address these dimensions where data is available:
IndicatorGoodCautionWarningMonthly demand (sampleAvgMonthlySales)>1,500 units 📊500-1,500 📊<500 📊Brand concentration (CR10)<40% 📊40-60% 📊>60% 📊New entrant rate (sampleNewSkuRate)>15% 📊5-15% 📊<5% 📊Avg review count (sampleAvgRatingCount)<500 📊500-5,000 📊>5,000 📊FBA rate (sampleFbaRate)>60% 📊40-60% 📊<40% 📊
When user asks "should I sell X" or "is this a good niche":
monthlySalesFloor is a lower-bound estimate 📊sampleAvgMonthlyRevenue directly — NEVER calculate price × sales 📊Sections: Analysis findings → Data Source & Conditions table (interfaces, category, dateRange, sampleType, topN, filters) → Data Notes (estimated values, T+1 delay, sampling basis).
Output language MUST match the user's input language. If the user asks in Chinese, the entire report is in Chinese. If in English, output in English. Exception: API field names (e.g. monthlySalesFloor, categoryPath), endpoint names, technical terms (e.g. ASIN, BSR, CR10, FBA, credits) remain in English.
Data is based on ZooData API sampling as of [date]. Monthly sales (
monthlySalesFloor) are lower-bound estimates. This analysis is for reference only and should not be the sole basis for business decisions. Validate with additional sources before acting.
Rules: Strategy recommendations are NEVER 📊. Anomalies (>200% growth) are always 💡. User criteria override AI judgment.
Aggregate-label rule (applies to ALL report output, not just fallback): NEVER attach 📊 to ANY element that aggregates or groups underlying content when ANY piece of that content is 🔍 or 💡. "Aggregate/grouping elements" include:
#, ##, ###, ####) — including top-level summary sections like "Overall Score", "Verdict", "Executive Summary"## Overall Score — 27/100 · Grade F 📊 is WRONG if any Basis row inside is 🔍)**Target ASIN** 📊 as a column label is WRONG if any cell in that column contains 🔍)A group-level 📊 implies the whole block/column/row is data-backed, which smuggles inferred/directional content into the 📊 tier via visual grouping. Either (a) omit the group-level label entirely (preferred when content mixes tiers), or (b) use the LOWEST confidence present inside (🔍 if any underlying content is 🔍; 💡 if any is 💡). This is a universal output-quality rule — it applies regardless of which fallback path (if any) was triggered.
Emoji reservation rule (closely related): The three confidence symbols 📊 🔍 💡 are RESERVED for confidence labeling. NEVER use them as decorative prefixes on section headers, table headers, or any aggregate element — even when you also include a correct confidence suffix on the same line. Example:
## 📊 Overall Score — 27/100 · Grade F 🔍 (the leading 📊 reads as a data-backed claim even though the trailing 🔍 is correct)## Overall Score — 27/100 · Grade F 🔍 (no decorative emoji, just the proper confidence suffix)## 🎯 Overall Score — 27/100 · Grade F 🔍 (use non-reserved decorative icons like 🎯 🧭 📋 📝 📂 🏁 🚨 🏆 🔔 when a visual prefix is desired)Decorative emoji ≠ confidence label — but from a reader's perspective, a leading 📊/🔍/💡 is indistinguishable from a confidence claim. Reserve these three symbols EXCLUSIVELY for confidence annotation to avoid ambiguity.
Include a table at the end of every report:
DataEndpointKey ParamsNotes(e.g. Market Overview)markets/searchcategoryPath, topN=10📊 Top N sampling, sales are lower-bound............
Extract endpoint and params from _query in JSON output. Add notes: sampling method, T+1 delay, realtime vs DB, minimum review threshold, etc.
EndpointCallsCredits(each endpoint used)NNTotalNN
Extract from meta.creditsConsumed per response. End with Credits remaining: N.
Cannot do: keyword research, reverse ASIN, ABA data, traffic source analysis, historical price/BSR charts. Niche keywords may return empty — use category path instead.