Apiguru Amazon Data
Live, structured Amazon data fetched at request time from Apiguru's servers. 23 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.
Costs and consent (read this first)
- Hosts contacted:
agent.apiguru.app(keyless) anddash.apiguru.app(the keyed API, and the feedback wall, which needs no key). Nothing else.scripts/probe.pyhas 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 a302. - Free quota: 3 calls per machine per 24 hours. After that the gateway
answers
402 Payment Required. The answer knows better than this page: every keyless reply carriesfree_calls_remainingin the body andX-Free-Probes-Remaining(orX-Free-Probes-Available: yes|nowhere no count is given) in the headers. Plan a task on the last reply, never on the number above. - This skill never pays.
probe.pystops 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:- an Apiguru API key, handed to the script by the user through
--api-key(an unechoed prompt),--api-key-file PATHor--api-key-stdin— bills their account at their plan's rates, about USD 0.01 per call — or - their own x402-capable HTTP client with a funded wallet and a spend cap
(USDC on Base, Polygon, Arbitrum or Avalanche). How that works is documented for the user at
https://agent.apiguru.app/llms-full.txt, section "Paying".
- an Apiguru API key, handed to the script by the user through
- Ask before you spend. Before the first billable call in a task, and
before any batch or broad search, tell the user what you will call, how many
items, and what it costs (run
capabilitiesfirst, 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. - Do not go looking for an API key: not in the environment, not in config or
dotfiles, not anywhere the user did not hand you deliberately.
--api-key-filetakes only a path the user named. Never send a key anywhere butdash.apiguru.app, and never echo it back into the conversation, a log or a command line.
Getting access
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-keyprompts for it (not echoed, not stored),--api-key-file PATHreads a file the user names (readable only by them),--api-key-stdinreads 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.
Choosing an endpoint
| Need | Endpoint |
|---|---|
| Everything about one ASIN | /v2/product-details |
| Many ASINs (≤20) | /product?asins=A,B,C — one call for many; no ranks, category path or merchant id |
| 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.
Rules that prevent wasted calls and wasted money
- An ASIN is 10 letters and digits (
^[A-Z0-9]{10}$). Any case works, and the gateway also reads an Amazon product URL (/dp/ASIN) in its place. - Never loop a single-item endpoint over a list. Use
/productfor ASINs and/seller-profilefor seller IDs. Ten ASINs through/productcosts USD 0.08 and one round trip; ten through/v2/product-detailscosts USD 0.10 and ten round trips. - Choose
geofrom the user's request, never by habit: amazon.de →DE, amazon.co.uk →UK, and so on (all 23 codes inreferences/endpoints.md). If the marketplace is not clear, ask. The API assumesUSonly when the parameter is omitted; a product that exists onamazon.demay genuinely404onUS, and that 404 is billed on the keyed path. check_inventory=trueon/stockis slow and bills more. Only set it when the user needs the stock number, not just the offers.- Read
successin the body, not just the HTTP status. Some responses are200withsuccess: false. /v2/dealsfilters by id, and tells you the ids.categoriestakes a department name as Amazon shows it (Electronics,Elektronik & Fotoon DE) or its id;brandstakes brand ids only. Every deals answer carriesavailable_filters(the category and brand ids of that marketplace),filters_applied/filters_ignored(what actually took effect) andnext_offset(the next page,nullwhen the feed ends). Narrow price and discount withmin_price,max_price,min_discount. For a brand by name use/search?brand=<name>&today_deals=trueinstead.- Every list answer reports its filters.
/searchand/v2/seller-productsreturnfilters_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) andavailable_filters.product_conditionis NEW / USED / RENEWED,deal_typeis today_deals / all_discounts / coupons / buy_more_save_more, prices take decimals./v2/best-sellersdepartments differ per marketplace: pass a slug or name and readavailable_categoriesandavailable_subcategories(the idssubcategory_codetakes) from the answer; pages run 1-5. /v2/best-sellerstells you how it readcategory. A word that is only part of a department name still matches it —category=shoesis the whole Clothing, Shoes & Jewelry department, hoodies included. The answer says so:category_resolution.viaisfragment, andhintlists the subcategories carrying that word with their ids (Women > Shoesis679337011,Men > Shoesis679255011). On amazon.comsubcategory_codetakes any browse node id at any depth, or a name resolved under the department —subcategory_code=women's shoes,mules & clogs— and the answer carriescategory.subcategory_path("Clothing, Shoes & Jewelry > Women > Shoes > Mules & Clogs") andcategory.heading(the page's own title). A name two nodes share equally is a free400listing both ids with their paths; pass the one you mean.
Big responses, and the two fields that mislead
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:
- Filter before you page.
brand,min_price/max_price,product_conditionandsort_bycut the result set before it is fetched, so the answer is both smaller and more relevant. Paging does neither. - Ask for less.
limit=Ncaps the rows (limit=0for the whole page),compact=truereturns light rows, andfields="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_productsandseller_reviews. - The defaults differ, deliberately. Over MCP a list tool returns the
first 10 light rows (about 7 KB) because a tool result goes straight
into a context window. Over
probe.pyyou get the whole page unless you ask otherwise, because a script can pipe it — soprobe.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. - The answer says what it did.
_truncatedgives the real row count and the exactlimit=to pass for all of them,_omitted_fieldsnames what the light projection dropped, and_notescarries 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_ratingsis 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".- A variant's title can be the parent's. In search results the ASIN and
the title can disagree — the URL slug usually shows the real variant. When
the exact variant matters, call
/v2/product-detailson that ASIN; it is authoritative for the ASIN you passed. - A search row's badges are the result card's, not the listing's.
badges(and the derivedis_amazon_choice/is_best_seller) say what Amazon printed on that card for that query.sort_by=BEST_SELLERSis Amazon's popularity order for the query, not a category rank — so an ASIN that is #1 in Women's Mules & Clogs can showbadges: []in a search for "crocs white" while/v2/product-detailsreportsbest_seller: truewith the rank. For a rank claim, use product-details or best-sellers;filters_applied.sort_byechoes 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.
Error handling — which failures cost money
404— the item genuinely is not on that marketplace. Billed on the keyed path. Retrying will not help; try a differentgeoor accept it.503— a temporary Apiguru-side failure. Not billed. Retry with backoff.500,502and504are the same class: not billed, retry.429— rate limited. Back off, then retry.- no answer (your client timed out) — keyless, nothing was billed; retry. With an API key the call may still have finished and been billed, so it is not repeated automatically: tell the user and retry only if they agree. Some marketplaces answer more slowly; allow 60s before giving up.
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.
Quick start
# 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
MCP alternative (optional, and outside this skill's audit)
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.54"
# 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.54"] } } }
or, with Node instead of Python, "command": "npx", "args": ["apiguru-mcp@1.1.54"].
Whichever you choose:
- Expected publisher:
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 asapp.apiguru/amazon-data. Anything under another name or publisher is not ours. - Verify the artifact, not just the name and version. PyPI and npm publish
a SHA-256 for every file;
pip download apiguru-mcp==<version>thenpip hashgives you a digest to record and re-check on the next upgrade. - Before raising the pin, read the changelog at https://github.com/apiguru-app/agent-kit/releases and diff the dependency list. Do it deliberately; do not let a launcher do it for you.
- Run it with least privilege: it needs outbound HTTPS to
agent.apiguru.app,dash.apiguru.appandmcp.apiguru.appand 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.
Telling us what is broken
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"}'categoryis one ofbug,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_feedbacktool 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.
Reference files
references/endpoints.md— every endpoint, parameter, and marketplace codereferences/errors-and-costs.md— pricing, billing rules, retry strategy
