amazon-subscriptions
Read Amazon Subscribe & Save subscriptions and upcoming deliveries as JSON. Strictly read-only.
Built on amazon-orders, which handles login and the session, and requests every page in English, whatever language the account is set to. Every Amazon site that offers its pages in English is supported; so far it is tested with amazon.de. See Other sites.
Installation
Requires Python 3.10 or later. Install the CLI from PyPI with uv:
uv tool install amazon-subscriptions
Or with pipx:
pipx install amazon-subscriptions
To use it as a library, add it to your project:
uv add amazon-subscriptions # or: pip install amazon-subscriptions
Or from a checkout, prefixing the commands below with uv run:
uv sync
The installation includes amazon-orders; its CLI is not needed.
Usage
amazon-subscriptions login # asks for username, password and one-time password
amazon-subscriptions list # all subscriptions
amazon-subscriptions upcoming # upcoming deliveries with their items
amazon-subscriptions list --json
amazon-subscriptions list --with-prices # also regular, list, unit and alternative prices from the product pages
amazon-subscriptions upcoming --json
amazon-subscriptions check-session
amazon-subscriptions logout
login, logout and check-session are the commands of amazon-orders. Each Amazon site has its own session, stored
in ~/.config/amazonorders/cookies.<site>.json (e.g. cookies.amazon.de.json, for amazon.com cookies.json) and
shared with amazon-orders. A session that older versions of amazon-orders stored in cookies.json for another site is
still used. Credentials are only asked for by login and never stored; the other commands never log in by themselves.
login signs out of the site's stored session first, so it always starts a new one. login --browser signs in
through a browser window instead, which needs the browser extra of amazon-orders. check-session --verify asks
Amazon whether it still accepts the session.
A language_package configured for amazon-orders is ignored, as the pages are only read in English. The configuration
file is not changed.
Both list and upcoming load the same pages, about 3 requests plus one per upcoming delivery: list needs the
delivery pages for the prices of the next delivery. list --with-prices loads the product page of every subscription
on top, one request each.
Options (before the command):
| Option | Description |
|---|---|
--domain amazon.de |
The Amazon domain. Defaults to domain in ~/.config/amazonorders/config.yml. |
--debug |
Log the requests to stderr and write every loaded page to the output directory. |
--output-dir DIR |
The directory for the pages written with --debug, defaults to ./output. |
Errors
| Exit status | Meaning |
|---|---|
| 0 | Complete result. |
| 1 | No result, only a message. |
| 2 | Partial result: some delivery or product pages could not be read. The output is complete JSON (or text), the affected entries have parse_errors (text output: [incomplete]), and each error is printed to stderr. |
The command exits with status 1 if
- there is no session or it has expired. Run
amazon-subscriptions logoutandamazon-subscriptions login(with the same--domain, if any), - the overview (
/auto-deliveriesand its further pages) cannot be loaded, e.g. because Amazon answers with a captcha (open Amazon in a browser, solve it there and try again later) or has changed the page. The message names the page; run again with--debugto keep it. An empty result is only returned if Amazon says so.
A delivery or product page that cannot be loaded or read is loaded once more after 2 seconds, as Amazon sometimes serves another variant of a page. If it fails again, the result is partial (status 2):
- A delivery without date is dated by the
deliveryDateof its URL, and keeps its items. - A delivery page without items gives the delivery as its card on the overview shows it: date, change deadline and
number of items, but
itemsis empty andtotalisnull. The subscriptions of that delivery get its error too, as theirdiscount_percentandsubscription_pricemay be missing. - A product page leaves the prices of its subscription
null. - A captcha is not retried. It stops loading the remaining delivery and product pages, which then fail too.
Every received page that could not be read is saved to ~/.cache/amazon-subscriptions/failed/ (or
$XDG_CACHE_HOME/amazon-subscriptions/failed/), also without --debug, as <timestamp>_<page>_<attempt>.html. The
path is printed to stderr and is in html_path of the error:
Error: No delivery date found, it was taken from the URL (page: https://www.amazon.de/auto-deliveries/?…), page saved to ~/.cache/amazon-subscriptions/failed/20261021-220012_delivery-2026-11-01_2.html
The pages written with --debug and to the failed pages directory contain your subscriptions, addresses and more.
Keep them private.
JSON
- Dates are ISO 8601 (
2026-11-01). - Amounts are decimal strings (
"12.34"), in the currency of the ISO 4217 code incurrency. - Values Amazon does not show are
null. - Every subscription and delivery has
parse_errors, the pages of it that could not be read (see Errors). It is empty ([]) if the entry is complete:
{
"url": "https://www.amazon.de/auto-deliveries/?…&deliveryDate=1793487600000&…",
"message": "No delivery date found, it was taken from the URL",
"selector": "[data-testid='ddp-atd-delivery-date']",
"html_path": "/Users/…/.cache/amazon-subscriptions/failed/20261021-220012_delivery-2026-11-01_2.html"
}
| Field | Description |
|---|---|
url |
The page that could not be read. |
message |
What went wrong, e.g. a missing element, an HTTP status or a captcha. |
selector |
CSS selector of the missing element, null if the page was not read at all. |
html_path |
Where the page was saved, null if no page was received (e.g. an HTTP error). |
list --json
A list of subscriptions:
[
{
"subscription_id": "…",
"asin": "B0TEST0001",
"title": "Product 01",
"product_url": "https://www.amazon.de/dp/B0TEST0001",
"image_url": "https://m.media-amazon.com/images/I/….jpg",
"quantity": 1,
"interval": {"every": 2, "unit": "month"},
"next_delivery_date": "2026-11-01",
"status": "active",
"price": "8.69",
"list_price": "11.99",
"list_price_type": "rrp",
"list_price_source": "buybox",
"discount_percent": 15,
"subscription_price": "7.39",
"unit_price": "57.93",
"unit_price_unit": "l",
"alternative_offer": null,
"currency": "EUR",
"raw_status_text": null,
"parse_errors": []
}
]
| Field | Description |
|---|---|
interval.unit |
day, week or month. |
status |
active, paused, unavailable (a backup product is sent instead), skipped or unknown. |
discount_percent, subscription_price |
Discount and price of the whole quantity in the next delivery. Only set if that delivery shows prices, usually only the next one. |
price, list_price |
Regular (one-time purchase) price of one unit in the buybox of the product page, and the strike-through price shown for it. Only read with --with-prices, null if the product is not available or the page shows no list price. Prices of other variants, sponsored products and recommendations on the page are never used. |
list_price_type |
What list_price is, by its label: rrp (recommended retail price, "RRP"), lowest_30d (lowest price in the last 30 days, see Limitations) or was (previous price, "Was"). null without list_price. |
list_price_source |
Where list_price was read: buybox (price block of the buybox, any type), widget (the entry of this product, "This item:", in a comparison widget, only rrp) or alternative_offer (price block of an alternative offer of the same product, only rrp). Tried in this order. |
unit_price, unit_price_unit |
Price per unit of measure of price and its unit as Amazon shows it, e.g. "57.93" and "l", "kg" or "count". Only read with --with-prices. |
alternative_offer |
The cheapest other offer of the same product next to the buybox offer ("Another way to buy"), e.g. of another seller, or null: {"price": "5.45", "unit_price": "1.36", "unit_price_unit": "count", "seller": "…", "seller_id": "A1B2C3D4E5F6G7", "subscribable": false}. seller is the seller name as the offer shows it, Amazon if Amazon sells it, or null if the page does not show a name. seller_id is the merchant ID from the link to the seller profile, null if Amazon sells the offer or the page has no such link. These offers cannot be subscribed to, so subscribable is always false. Only read with --with-prices. |
raw_status_text |
The status or alert text shown by Amazon, e.g. for a backup product. |
parse_errors |
The errors of the product page (--with-prices) and of the page of the next delivery, if they could not be read. |
upcoming --json
A list of deliveries:
[
{
"date": "2026-11-01",
"change_deadline": "2026-10-27",
"items": [
{
"title": "Product 01",
"quantity": 1,
"interval": {"every": 2, "unit": "month"},
"price": "7.39",
"discount_percent": 15,
"alert_text": null,
"substitute": false,
"subscription_id": "…",
"asin": "B0TEST0001",
"substitute_asin": null
}
],
"discount_tier": {"item_count": 11, "max_discount_unlocked": true, "savings": "10.58"},
"total": "86.20",
"currency": "EUR",
"delivery_bundle_id": "…",
"url": "https://www.amazon.de/auto-deliveries/?…",
"parse_errors": []
}
]
| Field | Description |
|---|---|
change_deadline |
The last day to change the delivery. |
items[].price |
Price of the whole quantity with the discount applied. Amazon shows prices only for the next delivery. |
items[].substitute |
true if a backup product is sent instead of the subscribed one; alert_text has Amazon's note. |
items[].asin, items[].substitute_asin |
asin is always the subscribed product. If substitute is true, title, price and discount_percent are those of the backup product, and substitute_asin is its ASIN. It is read from the subscription's detail sheet and is null if that sheet names another backup product. |
items[].subscription_id |
The subscription of the item. Delivery pages do not link items to subscriptions, so they are matched by title, quantity and interval; null if there is no unique match. |
discount_tier |
item_count is the number of items the discount is based on, max_discount_unlocked whether the delivery has the highest discount ("Max savings unlocked"), savings the amount saved as Amazon states it ("Saving €10.58"), usually only for the next delivery, otherwise null. |
total |
Sum of the item prices, null if an item has no price. |
parse_errors |
The errors of the delivery page, if it could not be read. items is then empty if the page had no items. |
Library
from amazonorders.conf import AmazonOrdersConfig
from amazonorders.session import AmazonSession
from amazon_subscriptions.client import SubscriptionsClient
from amazon_subscriptions.serialize import to_json
session = AmazonSession(config=AmazonOrdersConfig()) # logged in with `amazon-subscriptions login`
client = SubscriptionsClient(session) # with_prices=True also loads the product pages
print(to_json(client.get_subscriptions()))
print(to_json(client.get_upcoming_deliveries()))
if client.errors: # the result is partial, see parse_errors
print(to_json(client.errors))
SubscriptionsClient also takes retry_delay (seconds before loading a failed page again, default 2) and
failed_dir (where failed pages are saved, default ~/.cache/amazon-subscriptions/failed).
The parsers in amazon_subscriptions.parse are pure functions of the page HTML and send no requests.
Read-only
The client only requests the Subscribe & Save overview, the further pages of its lists (loaded like the browser does
when scrolling), the page of each upcoming delivery and, if a backup product is sent instead of a subscribed one, the
detail sheet of that subscription. With --with-prices (with_prices=True) it also requests the product page
(/dp/<ASIN>) of each subscription. Every URL is checked against an allow-list before it is requested, so links that
change subscriptions (skip, pause, cancel, change quantity or interval, deliver now) are never requested.
Limitations
-
Prices are only known for deliveries that show them, which is usually only the next one.
subscription_priceanddiscount_percentof a subscription are therefore only set if its next delivery is that one. -
Regular, list and unit prices are not shown on the Subscribe & Save pages. They are only read with
--with-prices, from the product pages of the subscribed products, so a backup product sent instead has none. Loading 30 or more product pages at once can make Amazon answer with a captcha; the remaining prices then staynullwith a warning. -
Some English texts are not known yet, as no page with them has been captured. Until they are added to
amazon_subscriptions/locales/en.py:- paused, skipped and unavailable subscriptions on the overview give
unknownand a warning; list_price_typeis neverlowest_30d, as that label is not known. amazon.de no longer showed the lowest price of the last 30 days on any product page checked, in German either.
Please contribute such pages, see CONTRIBUTING.md.
- paused, skipped and unavailable subscriptions on the overview give
-
Only the deliveries shown on the overview and its further pages are read, which Amazon limits to a few months.
Other sites
All Amazon sites that offer their pages in English use the same English locale: dates are read as 1 Oct and as
Oct 1, amounts as 12.34 and 1,234.56, and the currency is the ISO 4217 code of the site (the pages only show its
symbol, and $ alone does not tell US, Canadian and Australian dollars apart). Sites without English pages, such as
amazon.com.br, amazon.com.mx, amazon.com.tr and amazon.pl, are refused.
Only amazon.de has been tested so far. On other sites, the texts, the markup of the pages or the URLs may differ, so
some values may be missing (with a warning or an [incomplete] result) or a page may not be read at all. Login,
session and cookies are handled by amazon-orders for all sites.
If you use another site, please contribute anonymized pages of it, so it can be tested. CONTRIBUTING.md explains step by step how to capture and anonymize them.
Development
With mise, which installs Python and uv:
mise run install # uv sync
mise run lint # ruff, ruff format and mypy
mise run lint-fix
mise run test # pytest
Pull request titles and commits follow Conventional Commits; release-please creates the releases and the changelog from them.
The test fixtures in tests/fixtures/<tld>/ are anonymized English pages, see CONTRIBUTING.md for
how to capture and anonymize them. The product page fixtures (product*.html) are reduced to their title and price
blocks with scripts/extract_product_fixture.py. It also keeps the variants, the comparison widget and a sponsored
product, as prices the parser must not read.
Metadata
Release files for amazon-subscriptions 1.0.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| amazon_subscriptions-1.0.1.tar.gz | 222.1 kB | With provenance | Details for amazon_subscriptions-1.0.1.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| amazon_subscriptions-1.0.1-py3-none-any.whl | Python 3 | none | any | Details for amazon_subscriptions-1.0.1-py3-none-any.whl |
Total release size: 264.8 kB
Release files / amazon_subscriptions-1.0.1.tar.gz
| Download URL | amazon_subscriptions-1.0.1.tar.gz |
|---|---|
| Size | 222.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
802fe345242bef30ab2bb285a61e6237455db5a1d3cb9dc572a0fee13dabbc93
|
|
BLAKE2b-256 checksum How to use checksums |
97e83ef1ffe55442f7219bbfd0019a569c00c54b7968105797bf9d27af59ecab
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 28, 2026.
Transparency logRelease files / amazon_subscriptions-1.0.1-py3-none-any.whl
| Download URL | amazon_subscriptions-1.0.1-py3-none-any.whl |
|---|---|
| Size | 42.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
c693d197fa0bb19b32e331a6aa743992bbe9a16f940d7f2187260c3206c62ff7
|
|
BLAKE2b-256 checksum How to use checksums |
22e8c6d458b17f89d6f1901e9f56ef4547122e5c47fb20f36533c232ef0d63d8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 28, 2026.
Transparency logSecurity for amazon-subscriptions 1.0.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for amazon_subscriptions-1.0.1.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 28, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.