Install
openclaw skills install @apiguru-app/apiguru-amazon-dataLive Amazon marketplace data from Apiguru (a paid third-party API, 3 free calls a day) - product details, prices, reviews, keyword search, best-sellers, deals, offers and stock, seller profiles, across 20 Amazon marketplaces. Use only when the user asks for Amazon data by ASIN, Amazon URL, product, seller or keyword, or for Amazon price/stock/review monitoring. Not for other stores or general shopping advice. Never pays on its own; ask before any billable call. We want your feedback - if a field is wrong, missing or you wish the API did something else, say so with the free feedback command (no account needed); the agents that report are the ones this API gets fixed for.
openclaw skills install @apiguru-app/apiguru-amazon-dataLive, structured Amazon data fetched at request time from Apiguru's servers. 20 marketplaces.
What this skill writes. Every data command is a read: it fetches and
returns, and changes nothing anywhere. There is exactly one write, and it is
never automatic — the feedback command posts the text you give it to
Apiguru's public feedback wall (see "Telling us what is broken" below). It
sends only that text, it costs nothing, and it runs only when you invoke it.
Nothing else in this skill sends data anywhere.
agent.apiguru.app (keyless) and dash.apiguru.app
(the keyed API, and the feedback wall, which needs no key). Nothing else.
scripts/probe.py has both hosts fixed in the source, reads no environment
variables, and refuses every redirect, so a key cannot be carried to a
third host by a 302.402 Payment Required. The answer knows better than this
page: every keyless reply carries free_calls_remaining in the body and
X-Free-Probes-Remaining (or X-Free-Probes-Available: yes|no where no
count is given) in the headers. Plan a task on the last reply, never on
the number above.probe.py stops at a 402 and tells you so. It
contains no wallet and no x402 client, and it will not set one up. Paying is
the user's decision, made one of two ways, both only with their explicit
consent:
--api-key
(an unechoed prompt), --api-key-file PATH or --api-key-stdin — bills
their account at their plan's rates, about USD 0.01 per call — orhttps://agent.apiguru.app/llms-full.txt, section "Paying".capabilities first, it is free), and wait
for a yes. A single batch call can cost up to USD 0.16 (/product, 20 items)
or USD 0.15 (/stock, 10 items). Agree a cap for the task and stop at it.--api-key-file
takes only a path the user named. Never send a key anywhere but
dash.apiguru.app, and never echo it back into the conversation, a log or a
command line.Keyless (default). Call the agent gateway with no credentials:
GET https://agent.apiguru.app/agent/v1/v2/product-details?asin=B09DJLW458&geo=US
Two response headers say where you stand before a 402 arrives:
X-Free-Probes-Remaining and X-Price-Next-Call.
https://agent.apiguru.app/.well-known/x402 lists every endpoint with prices
and schemas, free and unmetered. Check it before planning a job.
Keyed. If the user gives you an Apiguru API key and asks you to use it, let the script read it — never put it on the command line, where shell history and the process table expose it to every other local user:
--api-key prompts for it (not echoed, not stored),--api-key-file PATH reads a file the user names (readable only by them),--api-key-stdin reads one line from standard input, e.g.
pass show apiguru | python scripts/probe.py ... --api-key-stdin.The script then sends it as X-API-KEY to https://dash.apiguru.app/api/v1
(same paths) and to nowhere else — redirects are refused rather than
followed. Calls bill that account.
scripts/probe.py wraps all of this. Prefer it over hand-written HTTP calls:
it retries only unbilled failures and explains every status.
| Need | Endpoint |
|---|---|
| Everything about one ASIN | /v2/product-details |
| Many ASINs (≤20) | /product?asins=A,B,C — cheaper per item, use this for >1 |
| Reviews, rating, "customers say" | /v2/product-reviews |
| Find products by keyword | /search?query=... |
| Offers, buy box, live stock (≤10) | /stock?asins=... |
| Category rankings | /v2/best-sellers |
| Current discounts | /v2/deals |
| A seller's catalogue | /v2/seller-products?seller_id=... |
| Seller reputation | /v2/seller-reviews?seller_id=... |
| Seller profiles (≤10) | /seller-profile?seller_ids=... |
Full parameter reference: references/endpoints.md.
^[A-Z0-9]{10}$). Any case works, and
the gateway also reads an Amazon product URL (/dp/ASIN) in its place./product for
ASINs and /seller-profile for seller IDs. Ten ASINs through /product
costs USD 0.08 and one round trip; ten through /v2/product-details costs
USD 0.10 and ten round trips.geo from the user's request, never by habit: amazon.de → DE,
amazon.co.uk → UK, and so on (all 20 codes in references/endpoints.md).
If the marketplace is not clear, ask. The API assumes US only when the
parameter is omitted; a product that exists on amazon.de may genuinely
404 on US, and that 404 is billed on the keyed path.check_inventory=true on /stock is slow and bills more. Only set it
when the user needs the stock number, not just the offers.success in the body, not just the HTTP status. Some responses
are 200 with success: false./v2/deals filters by id, and tells you the ids. categories takes a
department name as Amazon shows it (Electronics, Elektronik & Foto on
DE) or its id; brands takes brand ids only. Every deals answer carries
available_filters (the category and brand ids of that marketplace),
filters_applied / filters_ignored (what actually took effect) and
next_offset (the next page, null when the feed ends). Narrow price and
discount with min_price, max_price, min_discount. For a brand by
name use /search?brand=<name>&today_deals=true instead./search and
/v2/seller-products return filters_applied, filters_ignored (a
filter this marketplace cannot apply, with the reason -- amazon.fr has no
Today's Deals refinement, amazon.in offers only NEW) and
available_filters. product_condition is NEW / USED / RENEWED,
deal_type is today_deals / all_discounts / coupons / buy_more_save_more,
prices take decimals. /v2/best-sellers departments differ per
marketplace: pass a slug or name and read available_categories and
available_subcategories (the ids subcategory_code takes) from the
answer; pages run 1-5./v2/best-sellers tells you how it read category. A word that is
only part of a department name still matches it — category=shoes is the
whole Clothing, Shoes & Jewelry department, hoodies included. The answer
says so: category_resolution.via is fragment, and hint lists the
subcategories carrying that word with their ids (Women > Shoes is
679337011, Men > Shoes is 679255011). On amazon.com
subcategory_code takes any browse node id at any depth, or a name
resolved under the department — subcategory_code=women's shoes,
mules & clogs — and the answer carries category.subcategory_path
("Clothing, Shoes & Jewelry > Women > Shoes > Mules & Clogs") and
category.heading (the page's own title). A name two nodes share equally
is a free 400 listing both ids with their paths; pass the one you mean.A full /search page is up to 48 results and about 54 KB of JSON — enough
that most clients write it to a file instead of showing it to you. Keep it
small at the source:
brand, min_price/max_price,
product_condition and sort_by cut the result set before it is fetched,
so the answer is both smaller and more relevant. Paging does neither.limit=N caps the rows (limit=0 for the whole page),
compact=true returns light rows, and
fields="asin,product_title,product_price" returns only what you name.
These work the same way on the REST endpoints and over MCP, on every list
tool: search, best_sellers, deals, seller_products and
seller_reviews.probe.py you get the whole page unless
you ask otherwise, because a script can pipe it — so
probe.py search --query ... --limit 10 --compact (or
--fields asin,product_title,product_price) is worth adding when you are
reading the answer rather than filtering it. A parameter the script has no
flag for goes through --param name=value._truncated gives the real row count and
the exact limit= to pass for all of them, _omitted_fields names what
the light projection dropped, and _notes carries the things that will
mislead you when reading these particular rows.Three properties of Amazon's own data that will mislead you if you assume otherwise:
product_num_ratings is per listing family, not per ASIN. Variants
share a review pool, so every colour of one shoe reports the same count.
Do not present it as "this variant has N reviews"./v2/product-details on that ASIN; it is
authoritative for the ASIN you passed.badges (and the derived is_amazon_choice / is_best_seller) say what
Amazon printed on that card for that query. sort_by=BEST_SELLERS is
Amazon's popularity order for the query, not a category rank — so an ASIN
that is #1 in Women's Mules & Clogs can show badges: [] in a search
for "crocs white" while /v2/product-details reports best_seller: true
with the rank. For a rank claim, use product-details or best-sellers;
filters_applied.sort_by echoes the order the page actually used.badges is the source of truth for Amazon's Choice / Best Seller / Overall
Pick (Amazon renamed that slot to "Overall Pick"); is_amazon_choice and
is_best_seller are derived from it.
404 — the item genuinely is not on that marketplace. Billed on the
keyed path. Retrying will not help; try a different geo or accept it.503 — a temporary Apiguru-side failure. Not billed. Retry with
backoff. 500, 502 and 504 are the same class: not billed, retry.429 — rate limited. Back off, then retry.400 — your input was wrong (bad ASIN format, unknown geo, missing
required parameter). Not billed. Fix the input; do not retry unchanged.402 — free probes spent. Stop and ask the user (see "Costs and
consent"). Do not retry, do not look for a key, do not attempt payment.So: retry 429, 500, 502, 503, 504 and keyless timeouts (unless the
body says retryable: false); never retry 400, 402, 404 or 413, and
never repeat a timed-out keyed call without the user's say-so.
scripts/probe.py does exactly this. Its exit status tells a job what
happened: 0 usable answer, 1 HTTP error or a body reporting failure,
2 input rejected before any request, 3 a batch with some failed rows.
# prices, the free-probe policy, and how many free probes THIS caller has
# left right now (from /health; the policy number is not your balance). Free.
python scripts/probe.py capabilities
# one product
python scripts/probe.py product-details --asin B09DJLW458 --geo US
# many at once (preferred for lists)
python scripts/probe.py product --asins B09DJLW458,B0BSHF7WHW --geo US
# keyword search on amazon.co.uk
python scripts/probe.py search --query "wireless earbuds" --geo UK
# billed to the user's account, only after they said so.
# --api-key prompts; the key never appears in argv or in shell history.
python scripts/probe.py product-details --asin B09DJLW458 --geo US --api-key
# non-interactive equivalent, key straight from a secret store
pass show apiguru | python scripts/probe.py product-details --asin B09DJLW458 --api-key-stdin
scripts/probe.py above is the supported path. It is part of this skill,
it was reviewed with it, it is standard-library only, and it downloads and
executes nothing. Use it unless someone has decided otherwise.
The same data is also published as an MCP server. That server is a separate artifact: it is not shipped in this skill, it was not covered by whatever review this skill passed, and it needs its own security review before anyone runs it. Three ways to reach it, safest first:
1. The hosted server — nothing is installed or executed on your machine.
https://mcp.apiguru.app/mcp streamable HTTP
https://mcp.apiguru.app/account the same thing behind OAuth 2.1
Your client talks HTTP to a server we run. No package is fetched, so there is no supply chain on your side at all. This is the option to prefer.
2. Install it once, deliberately, then run what you installed. If you want it local, make it a controlled deployment step rather than a fetch on every launch:
python -m venv ~/.venvs/apiguru && ~/.venvs/apiguru/bin/pip install "apiguru-mcp==1.1.46"
# then point the client at the binary you just reviewed and installed:
# "command": "/home/you/.venvs/apiguru/bin/apiguru-mcp"
Pin transitive dependencies too if that matters to you: resolve once with
pip freeze > apiguru-lock.txt, review it, and install from that file with
--require-hashes.
3. Fetch at launch (uvx / npx). Convenient, and the weakest of the
three: the launcher resolves and executes a package from a public registry
every time the client starts. Pinning the version — which the snippets below
do — stops it silently moving to a newer release, but it does not protect you
from a compromised publisher account or registry.
{ "mcpServers": { "apiguru": { "command": "uvx",
"args": ["apiguru-mcp==1.1.46"] } } }
or, with Node instead of Python, "command": "npx", "args": ["apiguru-mcp@1.1.46"].
Whichever you choose:
apiguru-app. Registries: PyPI
(apiguru-mcp, https://pypi.org/project/apiguru-mcp/) and npm
(apiguru-mcp, https://www.npmjs.com/package/apiguru-mcp). Both are listed
in the official MCP Registry as app.apiguru/amazon-data. Anything under
another name or publisher is not ours.pip download apiguru-mcp==<version> then
pip hash gives you a digest to record and re-check on the next upgrade.agent.apiguru.app, dash.apiguru.app and mcp.apiguru.app and nothing
else — no filesystem access, no other credentials in its environment. A
container is the easy way: an official image is published for exactly this
(mcp/apiguru, Docker's MCP catalogue).The MCP tools do the same thing as probe.py and follow the same rules — they
never pay on their own. A 402 comes back as an x402 PaymentRequired result: the
user decides, and only an x402-capable MCP client they set up (with a spend cap)
can pay it. Their advantage is that they validate ASINs and geos before spending a
probe.
This API gets fixed when the agents using it say what is wrong. Recent
example: search results were returning the brand as product_title and
null for product_num_ratings; an agent reported both and both are fixed.
So if a field is empty, mistyped, welded together or simply missing:
Preferred, if you have a GitHub account — open an issue at https://github.com/apiguru-app/agent-kit/issues. A thread there can hold a reply and a fix gets linked back to it.
No GitHub account? Use the wall — one unauthenticated POST, never billed, no signup:
curl -X POST https://dash.apiguru.app/api/v1/feedback -H 'Content-Type: application/json' -d '{"message": "search: product_title holds the brand, not the title", "category": "bug", "endpoint": "/search", "agent": "your-name/1.0"}'
category is one of bug, wish, praise, question, other. Add
"contact" if you want a reply — it is shown publicly on the wall.
The wall is public, so send only what the user has agreed to, and only
about the API: the tool, the parameters, the field, what you expected and
the request_id. Never an API key, personal data, or the user's own
prompts, documents or results beyond the field in question.
Over MCP — the free send_feedback tool does the same thing.
Read what other agents have written: https://dash.apiguru.app/feedback
Say what you called, what you expected and what came back, and quote the
request_id from the answer. One issue per entry — a five-point list
cannot be closed point by point, and each entry gets its own status on the
wall (open, fixed, documented, answered, wont_fix). A wish counts:
if you need a field this API does not return, that is the most useful thing
you can tell us.
references/endpoints.md — every endpoint, parameter, and marketplace codereferences/errors-and-costs.md — pricing, billing rules, retry strategy