v0.3.0
The version that made the tool honest: no cookies, no user agent rotation, robots.txt enforced at request time, and every record says where it came from and what it could not find.
v0.2.1 sent rotating browser user agents, loaded cookies from a file, ignored
robots.txt, and pointed four commands at pages that redirect to a login. It got
blocked, and it deserved to.
This release deletes all of that, points every command at a surface that answers a logged out reader, and makes every record say where it came from and what it could not find.
Breaking changes
No command is removed. Three change what they read, one loses a flag, and the record shape changes.
| v0.2.1 | v0.3.0 |
|---|---|
amz reviews reads /product-reviews/, which redirects to a login |
reads the reviews and the histogram embedded in the detail page, and says in missed that the corpus needs a login |
amz qa reads /ask/questions/, which redirects to a login |
reads the answered question count and any inline pairs, and says the rest needs a login |
amz offers reads /gp/offer-listing/, which is disallowed and redirects |
reads the buy box from the detail page and states the count behind it. Needs no flag |
amz product returns 38 fields |
returns everything the 288 feature regions carry |
--cookies <file> |
removed. The code that sent a Cookie header is deleted |
--workers <n> |
removed. One connection, paced |
| rotating browser user agents | one honest user agent naming the tool and its repo |
| flat records | records carry an envelope, so every JSON key moves down one level unless --flat |
amz db query needs a duckdb binary |
pure Go SQLite, nothing to install |
| exit 5 for everything unusual | 5 CAPTCHA, 6 challenge, 7 needs --no-robots, 8 robots unfetchable, 9 needs a login |
The change most people will notice is that amz reviews returns a handful of
reviews instead of failing, so the message has to be a good one when you wanted
all of them:
$ amz reviews B075F5X8BR
# 13 reviews on stdout, then on stderr:
amz: 13 of 21095. amazon requires a sign-in for the review corpus, and the detail page carries the histogram and the reviews medley only. the total is the ratings count, which is the largest number the page states
amz: /product-reviews/ is not readable without a session
amz: /portal/customer-reviews/ is not readable without a session
amz: run `amz why reviews` for the detail
Thirteen reviews and an honest sentence beats zero reviews and wrong advice.
Every record says where it came from
A record now carries an envelope beside its fields. It names the responses the
record was read from, the region each field came out of (via), which rung of
the extraction ladder answered (level), what was looked for and not found
(missed), and what is on the page that nothing reads yet (unread).
That last part is the design. If a field is missing and nothing in missed names
it, amz read the place that field lives and there was nothing there. Absence is
an answer rather than a gap.
robots.txt is asked, not assumed
The marketplace's live robots.txt is fetched, parsed and enforced before every
request, cached for 24 hours with the time it was fetched. Nothing about it is
compiled in, and amz robots check <url> prints the rule that decided any URL.
--no-robots is an explicit, loud, per-run override that prints every rule it
breaks and raises the pace floor to 5s. There is no no_robots config key and no
AMZ_NO_ROBOTS environment variable, and there are tests asserting neither ever
appears. A stop signal you can turn off in a file you forgot about is not a stop
signal.
Getting past 306 results
Amazon serves at most 306 results over 20 pages for any query, whatever total it
advertises. amz search --all partitions the query on a refinement group, runs
one search per value, and unions the cells on ASIN.
Measured on 2026-08-18, amz search "usb-c hub" --all partitioned on Brands into
68 cells and returned 1,508 unique results. It reports the cells that still hit
the ceiling and the ones Amazon served unfiltered, so the holes in the union are
named rather than papered over.
The refinement vocabulary is read, not compiled in
Only six refinement codes mean the same thing on every search. Everything else is
per query, so the sidebar is the source: amz refine <query> prints every group
a query offers with its code, label, scope and values. A refinement Amazon takes
and then does not apply is an error rather than an unfiltered result set wearing
a filter's label.
A local store with nothing to install
SQLite is compiled into the binary as pure Go. crawl, db, query, find,
lookup, graph, series and export all work on a machine with nothing on it
but this binary.
New commands
| Command | What it does |
|---|---|
why [topic] |
why something returns less than you expected, with the measurement and the date |
surfaces |
every Amazon surface amz knows, what robots says about it, when it was measured |
extraction |
the four rung ladder, and what is on a page that nothing reads yet |
verify |
today's read against the golden captures, so drift is caught |
refine |
the refinement vocabulary a query offers |
variants |
the variation matrix, one row per sibling |
tree |
walk the browse node graph outward |
find, lookup, graph, series, query |
read the local store with no network |
serve, mcp |
the read commands over HTTP and as Model Context Protocol |
doctor |
check the client is honest, the network works and the store is readable |
agent-map |
Amazon's own description of a page, verbatim |
Fixes
amz brand <name> works with a bare name. Amazon puts a storefront at
/stores/<name>/page/<uuid> and nothing derives that uuid from the name, so
/stores/anker is a 404 and always was. A bare name is now resolved through the
byline link on a product the brand sells.
The brand is also read off premiumBylineInfo, which is where Amazon puts it for
a premium brand while leaving bylineInfo on the page and empty. Until now every
premium brand, which is most of the ones anybody searches for by name, came back
with a null brand and a null brand URL.
One cell of a partitioned search that Amazon serves unfiltered no longer discards the whole union. It is recorded, named in the summary, and the other cells are still read.
Migrating
--flat emits the v0.2.1 product record for one more version and prints a
deprecation note. It goes away in v0.4.0. The envelope travels with it, because
provenance is not a projection.
--cookies and --workers are gone with no replacement. If you were using
cookies to reach the review corpus or the all-offers panel, read amz why reviews and amz why offers: both now return what the public page carries and
say what is behind the login and why. If you were using --workers to go faster,
that is the reason the tool got blocked. --rate only goes slower.
The full list of renamed fields is in the changelog.