Install
openclaw skills install @apiclaw/zoodataAPI endpoint reference for the ZooData data platform: the 12 commerce endpoints plus 10 keyword-intelligence endpoints (categories, markets, products, competitors, realtime ASIN, AI review analysis, raw reviews, price band, brand, history, and the keyword detail/trend/extends/search/ market-profile/product-traffic/competitor-keywords/traffic-timeline family) — their inputs/outputs, parameter quirks, Quick Start (auth, base URL), how credits are tracked (meta.creditsConsumed), and the Local Review Toolkit (Map/Reduce for raw reviews). Use when the user asks about the API itself: which endpoints exist, how to call them (e.g. /products/search), field schemas returned by an endpoint, parameter quirks, how to authenticate, how credit consumption is reported, how to get started, or how the Local Review Toolkit works. Requires ZOODATA_API_KEY.
openclaw skills install @apiclaw/zoodata📋 Live API Reference: Field names and parameters may change. If you encounter field errors, check the latest OpenAPI spec at https://zoodata.ai/api/v1/openapi-spec for current field definitions. Keyword exception: the observation endpoints currently support
granularity=weekonly. Do not reintroduceday,month,lately_day, orlookbackDaysfrom a stale generated schema.
200M+ Amazon products. 22 endpoints. One API key.
export ZOODATA_API_KEY='hms_live_xxx'https://api.zoodata.ai/openapi/v2 — all POST with JSON bodyAuthorization: Bearer YOUR_API_KEYhttps://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 data-layer reference skill allows the complete literal subcommand surface exposed by the bundled client's current top-level help.~/.zoodata/config.json; the Local Review Toolkit uses a private temporary working dir (created with mktemp -d, removed when the fallback completes) during the review fallback.Before selecting or invoking a bundled CLI command, read and apply references/cli-contract.md; reapply it after every result. It is the local source of truth for invocation, command identity, execution-environment permission handling, composite reuse, exit-status handling, authoritative transport status, retries, terminal interface failures, and partial results.
For this API-reference skill, a terminal interface failure must produce one concise localized notice stating that the ZooData API lookup could not be completed, followed by the succeeded and failed endpoint identifiers. Do not continue into endpoint guidance, schema interpretation, or another API call. Do not expose control tokens or internal retry logs unless the user requests diagnostics.
categoryPath before interpreting category-sensitive product, market, competitor, brand, or price-band results. An explicitly labeled products/search category probe may run without a locked category only to resolve that category. Do not apply this rule to /openapi/v2/keywords/* Keyword Intelligence endpoints: their keyword / query inputs are Amazon search queries and do not require categoryPath.sampleAvgMonthlyRevenue directly. NEVER calculate avgPrice × totalSales (overestimates 30-70%)monthlySalesFloor (lower bound). Fallback: 300,000 / BSR^0.65, tag as 🔍sampleOpportunityIndex, sampleTop10BrandSalesRate — never reinventrealtime/product → ratingBreakdown (star distribution only, no themes)realtime/reviews (raw text, up to 100) + local Map/Reduce via the
Local Review Toolkit below — see "Local Review Toolkit" sectionkeyword (not categoryPath) — cross-validate returned productsmode is CLI-local, NOT an API parameter → zoodata.py expands --mode client-side into the filter sets in PRODUCT_MODES ({skill_base_dir}/scripts/zoodata.py, 13 presets) before the request; sending mode raw → 422--sales-min → monthlySalesMin; --ratings-max (review count) → ratingCountMax, not ratingMax (a different valid field — max star rating — that returns wrong results silently, no 422). Pass categoryPath as a JSON array (["Electronics"]), never a string. Unknown fields (salesMin, ratingsMax, …) → 422BEFORE calling any endpoint, verify credentials are configured. The reliable check is python {skill_base_dir}/scripts/zoodata.py check — credentials-only by default, no endpoint calls and no credit usage; exits non-zero if no key is found in env vars OR config files. A [ -z "$ZOODATA_API_KEY" ] test alone is NOT sufficient — a user may have only ~/.zoodata/config.json set.
When no key is found through any mechanism:
zoodata.py (you'll just get the same credential error and burn tokens).ZOODATA_API_KEY is not set — I need this to run the analysis."export ZOODATA_API_KEY='hms_live_xxx' (session only)mkdir -p ~/.zoodata && chmod 700 ~/.zoodata && (umask 077; echo '{"api_key":"hms_live_xxx"}' > ~/.zoodata/config.json) (persistent; keep the file private — 0600)When zoodata.py returns a structured error with _transport.status=401,
error.status=401, and error.message="API Key invalid or expired":
When zoodata.py returns a structured error with _transport.status=402,
error.status=402, and error.message="API quota exhausted or subscription expired":
For every parsed HTTP response from zoodata.py, treat _transport.status as the authoritative outer status; response-body and nested status-like fields do not override it. When the CLI returns HTTP 422 / VALIDATION_ERROR, read the preserved structured server error on stdout, including its message/details and _query.params. Do not retry the unchanged request. Correct the named fields first; the CLI exits non-zero while preserving the server error fields for the calling agent. Keyword endpoints that expose granularity currently accept week only; do not send day, month, lately_day, or lookbackDays.
| # | Endpoint | Purpose | Key Output |
|---|---|---|---|
| 1 | categories | Browse/search category tree | categoryPath, productCount |
| 2 | markets/search | Market-level metrics | sampleAvgMonthlySales, sampleAvgPrice, topSalesRate, sampleNewSkuRate |
| 3 | products/search | Product search (20+ filter fields) | asin, price, monthlySalesFloor, rating, ratingCount, fbaFee |
| 4 | products/competitors | Competitor discovery | same fields as products/search |
| 5 | realtime/product | Live ASIN detail | rating, features, bestsellersRank[], buyboxWinner.price, variants |
| 6 | reviews/analysis | AI review insights (11 dims) | sentimentDistribution, consumerInsights, topKeywords |
| 7 | realtime/reviews | Live raw review text (cursor paginated, max 100) | reviews[], nextCursor — feeds Local Review Toolkit |
| 8 | products/price-band-overview | Price band summary | hottestBand, bestOpportunityBand, sampleOpportunityIndex |
| 9 | products/price-band-detail | Full 5-band distribution | priceBands[] with sales, brands, ratings per band |
| 10 | products/brand-overview | Brand concentration | sampleTop10BrandSalesRate (CR10), sampleBrandCount |
| 11 | products/brand-detail | Per-brand breakdown | brands[] with sales, revenue, sampleProducts |
| 12 | products/history | Time series (single ASIN per call) | timestamps[], price[], bsr[], monthlySalesFloor[], rating[], ratingCount[], sellerCount[], title/imageUrl/bestSeller/newRelease/aPlus/inventoryStatus changelogs |
| 13 | /openapi/v2/keywords/detail | Keyword summary from the nearest available weekly snapshot | data.context + data.items[].snapshotData with estimateSearchCount, abaRank, market/SKU/ad fields |
| 14 | /openapi/v2/keywords/market-profile | Multidimensional weekly keyword profile | demand scale, Top3 concentration, ad activity, organic-entry difficulty, saturation, brand structure, organic benchmark, coverage |
| 15 | /openapi/v2/keywords/trend | Weekly keyword time series | data.context + data.items[].series[] with search count, ABA rank, Top3 shares, period bounds |
| 15b | /openapi/v2/keywords/trend-profile | Server-calculated trend profile over fixed weekly windows | trend shape, volatility, normalized slope, direction consistency, ABA-rank evidence |
| 16 | /openapi/v2/keywords/extends | Keyword expansion / long-tail discovery | data.context + data.rows[].{matchData,keywordSnapshot}; may return empty rows[] |
| 17 | /openapi/v2/keywords/search-results | Weekly keyword SERP snapshot | data.context + data.identity + data.rows[] with placement, product, and impression fields |
| 18 | /openapi/v2/keywords/competitor-product-keywords | Keyword set where an ASIN appears as a competitor | data.context + data.identity + data.rows[] with keyword, position, demand, and traffic share |
| 19 | /openapi/v2/keywords/product-traffic-terms | Traffic-driving keywords for an ASIN | same response shape as competitor-product-keywords |
| 20 | /openapi/v2/keywords/product-traffic-terms-overview | Weekly ASIN all-keyword traffic-change overview | current vs previous-period placement-level impression points, ORG first-3-page keyword entries/exits |
| 21 | /openapi/v2/keywords/product-traffic-terms-timeline | ASIN + keyword weekly timeline | data.context + data.items[].series[] with nested ASIN, traffic, placement, keyword, and ad groups |
topN, listingAge, newProductPeriod are strings ("10" not 10).data as an array — use .data[0] for the first record. But some commands may return non-array payloads inside data, so inspect the actual response shape before indexing.ratingCount not reviewCount everywherebsr (int) in products vs bestsellersRank (array) in realtimebuyboxWinner.price — NOT top-level price in realtimerealtime/product does NOT return: monthlySalesFloor, fbaFee, sellerCountrealtime/product cold-start: first call for an uncached ASIN may return success: true with an EMPTY data (asin: "") while the live fetch warms up — retry once after a few seconds before concluding "no data" (still billed 1 credit per call)reviewCountMin/Max filters currently broken (API-56)reviews/analysis may 500 for certain ASINs (API-58) — retry different ASINcategories uses categoryKeyword (not keyword) and parentCategoryPath (not parentCategoryName)reviews/analysis: mode required ("asin"/"category"), use asins (plural array) not asinrealtime/reviews: returns 10 reviews/page fixed (no pageSize param); 1 credit/page; cursor-paginated; hard cap = 100 reviews (10 pages); supports marketplace US/UK onlykeywords/detail accepts exactly one of keyword / keywords[] (max 20), resolves date to the nearest available weekly snapshot, and returns input-ordered data.items[]; an unmatched item has status=empty, not top-level data: nullkeywords/market-profile accepts one of keyword / keywords[] (max 20), requires date, supports weekly granularity only, and returns input-ordered data.items[] with status=ok|empty. emptyReason is descriptive no-result text, not an enum. A subject-specific calculation failure can return HTTP 500 for the whole batch.keywords/trend-profile accepts one of keyword / keywords[] (max 20), requires date and 1–4 unique windowPeriods selected from 4/8/12/26, and supports weekly granularity only.keywords/extends requires query (not keyword), uses the latest available weekly snapshot, supports queryType = phrase or fuzzy, and may legitimately return empty data.rows[]; legacy date is optional and ignoredgranularity currently support week only. day, month, lately_day, and lookbackDays are unsupported. Use returned period boundaries instead of inferring a rolling window.keyword or query, use the Amazon search query / keyword phrase being analyzeddate or dateTo, prefer T-1 or earlier and avoid the current date unless the user explicitly asks for today's lookupkeywords/search-results requires date + keyword; exploreTypes values are ORG, SP, SB, SBV, SPRkeywords/competitor-product-keywords and keywords/product-traffic-terms require date + asin; both currently return the same live item shape, including trafficSharekeywords/product-traffic-terms-overview requires date + asin; it returns the latest weekly overview of all keyword impression traffic changes under that ASIN at or before the date, compared with the previous periodkeywords/product-traffic-terms-timeline requires asin + exactly one of keyword / keywords[] + dateFrom + dateTo; the date range cannot exceed 61 days and the series request has no pagination or sort parameterskeywords/search-results is the default source for explaining what products currently appear on a keyword SERP because it already returns listing-level product fieldsproducts/search is a broader ZooData product-database query and must not be presented as Amazon live keyword SERP orderingThese ten endpoints fill the gap between raw catalog data and search-demand/search-visibility intelligence.
Keyword value boundary:
amazon-keyword-traffic-analysis skill. This API reference does not prescribe a blanket caveat or one seller view for every subject./openapi/v2/keywords/detailkeyword / keywords[] (1–20), required date, optional marketplace, granularity=week onlydate to the nearest available weekly snapshot at or before that datedate; avoid current-date lookup unless explicitly requesteddata.context + data.items[], preserving request orderidentity, status=ok|empty, snapshotData, emptyReason, nullable errorCode, nullable errorMessagesnapshotData fields include estimateSearchCount, abaRank, Top3 click/conversion shares,
marketCharacteristics, totalSkuCount, SKU/brand/title coverage, organic/ad counts, and Top48 benchmarksestimateSearchCountWeekly, totalSkuCnt, or top-level data:null/openapi/v2/keywords/market-profile (metric layer)keyword or keywords[] (1–20), required date, optional marketplace, granularity=week onlydata.context + data.items[], preserving request orderrequestedDate, resolvedDate, dataWindow.currentPeriod, scoringSpec, marketplace/site/granularityidentity, status=ok|empty, marketProfile, emptyReasonmarketProfile dimensions: marketCharacteristics, demandScale, top3Concentration, adActivity, top20OrganicEntryDifficulty, supplySaturation, brandStructure, organicProductBenchmarkcontext.scoringSpec (id, version, scoreType, scoreRange, referenceScope). Scored dimensions expose supported, calculationStatus, unsupportedReason, level, interpretation, and levelEvidence.score.{value,direction}. There is no aggregate coverage object.marketCharacteristics.volatility and marketCharacteristics.annualSeasonality are independent evidence objects. Do not collapse their classifications, let one override the other, or invent peak periods from an empty list.status=empty, marketProfile=null, and a descriptive emptyReason; resolved context and scoringSpec may be nullempty result; do not automatically fan out all subjects into single calls.keywords/detail for source snapshot fields, metric-layer keywords/market-profile for stable deterministic profile objects, and the Agent + skill layer for evidence composition, confidence, explanations, limitations, and actions/openapi/v2/keywords/trendkeyword / keywords[] (1–20), required dateFrom / dateTo, optional marketplace, granularity=week only; maximum 93-day rangedateTo; avoid current-date lookup unless explicitly requesteddata.context + data.items[].series[], preserving request orderidentity, status=ok|empty, series[], emptyReason, nullable errorCode, nullable errorMessageperiodStartDate, periodEndDate, estimateSearchCount, abaRank,
abaTop3ClickShareRate, abaTop3ConversionShareRate/openapi/v2/keywords/trend-profile (metric layer)keyword / keywords[] (1–20), required date, required unique windowPeriods[] selected from 4/8/12/26, optional marketplace, granularity=week onlydata.context + data.items[].rows[]; every requested window returns one row with rowContext, status=ok|empty, emptyReason, and trendProfilesearchDemand and abaRank dimensions with trend, trendPattern, and {value,direction} entries under trendEvidencekeywords/trend for trend-shape and volatility judgments. Descend only for required weekly points or fields omitted from the profile.status=ok window; use returned credit metadata./openapi/v2/keywords/extendsquery; optional marketplace, page, pageSize, queryType, sortBy, sortOrder; no date is requiredquery, not keyword; queryType supports phrase and fuzzydate may be sent but is ignoreddata.context + data.query + data.queryType + data.rows[]matchData.{query,keyword,site,relevanceScore} and keywordSnapshot, whose
dataWindow.currentPeriod and snapshot metrics use the same current field families as keywords/detailterm, seedKeyword, or estimateSearchCountWeekly; empty rows[] is normal/openapi/v2/keywords/search-resultskeyword / date, granularity=week only; optional marketplace, page, pageSize, exploreTypes, sortBy, sortOrderlookbackDays; day, month, and lately_day are unsupporteddate; avoid current-date lookup unless explicitly requesteddata.context + data.identity + data.rows[]latestObservedAt, exploreType, absolutePosition, pageIndex,
pagePosition, asin, title, brand, price, currency, link, imageLink, rating,
ratingCount, recentSales, hasVideo, estimateImpressionPoint,
keywordTotalEstimateImpressionPointproducts/search when the question is about observed keyword SERP composition or ordering/openapi/v2/keywords/competitor-product-keywordsasin / date, granularity=week only; optional marketplace, page, pageSize, exploreTypes,
keywordContains, sortBy, sortOrderlookbackDays; day, month, and lately_day are unsupported; use returned weekly period boundariesdate; avoid current-date lookup unless explicitly requesteddata.context + data.identity + data.rows[]latestObservedAt, exploreType, absolutePosition, pageIndex,
pagePosition, asin, keyword, estimateImpressionPoint, asinTotalEstimateImpressionPoint,
avgPosition, daysCoverageRate, observationCount, keywordEstimateSearchCount,
keywordEstimateSearchChangeCount, keywordEstimateSearchCountChangeRate, keywordAbaRank,
keywordAbaRankChangeCount, trafficShare/openapi/v2/keywords/product-traffic-termskeywords/competitor-product-keywordsdate + granularity=week; use returned period boundariesdate; avoid current-date lookup unless explicitly requesteddata.context + data.identity + data.rows[]latestObservedAt, exploreType, absolutePosition, pageIndex,
pagePosition, asin, keyword, estimateImpressionPoint, asinTotalEstimateImpressionPoint,
avgPosition, daysCoverageRate, observationCount, keywordEstimateSearchCount,
keywordEstimateSearchChangeCount, keywordEstimateSearchCountChangeRate, keywordAbaRank,
keywordAbaRankChangeCount, trafficSharekeywords/competitor-product-keywords
field-for-field; keep the semantic distinction in output wording rather than assuming a unique schema/openapi/v2/keywords/product-traffic-terms-overviewasin, date, optional marketplacedate; avoid current-date lookup unless explicitly requesteddata is an object or nullperiodStartDate, periodEndDate, asin, site,
organicImpressionPoint, sponsoredProductImpressionPoint, sponsoredBrandImpressionPoint,
sponsoredBrandVideoImpressionPoint, sponsoredRecommendImpressionPoint,
organicImpressionPointPrev, sponsoredProductImpressionPointPrev,
sponsoredBrandImpressionPointPrev, sponsoredBrandVideoImpressionPointPrev,
sponsoredRecommendImpressionPointPrev, first3PagesNewOrganicKeywords,
first3PagesLostOrganicKeywords*Prev fields are previous-period baselines for the matching current impression-point fieldsperiodStartDate / periodEndDate; it does not return separate previous-period boundaries. A *Prev field may be null or absent when no previous-period value is available.first3PagesNewOrganicKeywords and first3PagesLostOrganicKeywords are arrays of objects with
keyword, pageIndex, and pagePositionfirst3PagesNewOrganicKeywords lists keywords newly entering ORG first three pages; first3PagesLostOrganicKeywords
lists keywords that dropped out of ORG first three pagesopenapi_v2_product_traffic_terms_overview,
asin="B01CGLCGRA", date="2026-06-29", marketplace="US"/openapi/v2/keywords/product-traffic-terms-timelineasin, exactly one of keyword / keywords[] (1–20), dateFrom, dateTo, granularity=week only; optional marketplacelookbackDays, page, pageSize, sortBy, or sortOrder; day, month, and lately_day are unsupporteddateTo; avoid current-date lookup unless explicitly requesteddata.context + data.items[].series[], preserving keyword request orderidentity, status=ok|empty, series[], emptyReason, nullable errorCode, nullable errorMessageasinSnapshot, traffic, placement, keywordMetrics, and adActivity; keep their returned period boundaries separateasinSnapshot.latestPrice), BSR (asinSnapshot.latestBsr,
asinSnapshot.latestSubBsr), sales (asinSnapshot.latestMonthlySaleCount), rating
(asinSnapshot.latestRating, asinSnapshot.latestRatingCount), traffic estimate (traffic.*
plus placement averages), and listing events (asinSnapshot.latestTitle, asinSnapshot.latestMainImageLink)asinSnapshot; ORG/SP/SB/SBV/SPR impression points
in traffic; positions/pages/observation times in placement; weekly search/ABA fields and
metricWindow in keywordMetrics; observation/campaign/ad counts in adActivityWhen /reviews/analysis lacks aggregation (ASIN has <50 reviews or no daily snapshot),
fall back to live raw reviews + your own LLM. The toolkit does NOT call any external
LLM — you (the calling skill's LLM) perform the Map/Reduce steps.
Workflow:
# 1. Fetch raw reviews (up to 100, cursor-paginated, ~60s, 10 credits at full)
zoodata.py reviews-raw --asin B0XXXXXXXX [--marketplace US] [--max-pages 10]
# 2. For EACH review, render the per-review Map prompt
zoodata.py review-tag-prompt --review '<single review JSON>' \
[--product-title "..."] [--product-category "..."]
# → Your LLM produces a JSON object with sentiment + 11 dimension arrays
# (mentioned_scenarios, mentioned_issues, mentioned_positives, mentioned_improvements,
# mentioned_buying_factors, mentioned_pain_points, user_profiles, mentioned_usage_times,
# mentioned_usage_locations, mentioned_behaviors, keywords)
# Suggested map parallelism: ~20 concurrent if your LLM supports it
# 3. Collect candidate phrases per dimension. For EACH dimension render the Reduce prompt
zoodata.py review-reduce-prompt --label-type positives \
--candidates '["comfortable","comfy","very comfortable",...]'
# → Your LLM produces {clusters: [{canonical, members}, ...]}
# Suggested chunk size for `keywords` dim when >150 candidates: 150 per call
# 4. Aggregate into reviews/analysis-compatible consumerInsights
zoodata.py review-aggregate --reviews raw.json --tagged tags.json --clusters clusters.json
# → Output shape matches /reviews/analysis: reviewCount, avgRating,
# sentimentDistribution, consumerInsights[], topKeywords[]
When to use the toolkit instead of reviews/analysis:
reviews/analysis returns sparse consumerInsights (missing dimensions)| Data | markets | products/competitors | realtime/product | reviews/analysis | realtime/reviews | price-band | brand | history |
|---|---|---|---|---|---|---|---|---|
| Sales | sampleAvgMonthlySales | monthlySalesFloor | ❌ | ❌ | ❌ | sampleSalesRate | sampleGroupMonthlySales | monthlySalesFloor[] |
| Price | sampleAvgPrice | price | buyboxWinner.price | ❌ | ❌ | bandMin/MaxPrice | sampleAvgPrice | price[] |
| BSR | sampleAvgBsr | bsr (int) | bestsellersRank[] | ❌ | ❌ | ❌ | ❌ | bsr[] |
| Rating | sampleAvgRating | rating | rating | avgRating | rating (per review) | sampleAvgRating | sampleAvgRating | rating[] |
| Reviews | sampleAvgReviewCount | ratingCount | ratingCount | reviewCount | reviews[] (raw text, max 100) | ❌ | sampleAvgRatingCount | ratingCount[] |
| Insights | ❌ | ❌ | ❌ | ✅ consumerInsights | ❌ (raw only — feeds Local Review Toolkit) | ❌ | ❌ | ❌ |
| Concentration | topSalesRate | ❌ | ❌ | ❌ | ❌ | sampleTop3BrandSalesRate | CR10 | ❌ |
| Opportunity | ❌ | ❌ | ❌ | ❌ | ❌ | sampleOpportunityIndex | ❌ | ❌ |
Strategy recommendations and subjective conclusions are NEVER 📊. Extreme growth (>200%) = 💡 only.
monthlySalesFloor) = lower-bound estimatemeta.creditsConsumed