Shopify-ATC
A command-line tool that turns any Shopify storefront into a list of direct add-to-cart permalinks.
Point it at a store and it reads the shop's public catalog, then prints a ready-to-click …/cart/add?id=… link for every product variant — so you can drop an item straight into your cart instead of clicking through the product page. Output comes in human-readable text, JSON, or CSV.
How it works
Shopify exposes two public, documented features this tool builds on:
/products.json— every Shopify store publishes its catalog (products, variants, prices, stock) as JSON. No API key required./cart/add?id=<variant_id>permalinks — Shopify's built-in cart permalink format adds a specific variant to the cart in one request.
Shopify-ATC fetches the first, then generates the second for each variant.
Features
- Add-to-cart links for every variant — resolves each product's variants and builds a
/cart/addpermalink with a configurable quantity. - In-stock filtering —
--in-stock-onlydrops sold-out variants so you only see what you can actually buy. - Three output formats —
textfor reading,jsonfor piping into other tools,csvfor spreadsheets. - Clear failure modes — a non-Shopify URL, an unreachable host, and an HTTP error each produce a distinct message and a distinct exit code, rather than one catch-all "something went wrong".
Tech Stack
- Language: Python 3.9+
- HTTP:
requests - CLI:
argparse(standard library) - Tests:
pytest(HTTP mocked — the suite runs fully offline) - CI: GitHub Actions, matrix across Python 3.9–3.13
Getting Started
Prerequisites
- Python 3.9 or newer
Installation
# From PyPI (recommended)
pip install shopify-atc
# …or isolated with pipx
pipx install shopify-atc
This installs a shopify-atc command. The package is published on PyPI at
pypi.org/project/shopify-atc.
Runnable examples (CLI and library) live in examples/.
For local development from a clone:
# Editable install with test deps
pip install -e ".[dev]"
Usage
shopify-atc <store-url> [--limit N] [--in-stock-only] [--quantity N] [--format text|json|csv]
| Flag | Default | Description |
|---|---|---|
store_url |
— | Store URL, e.g. https://www.allbirds.com (scheme optional) |
--limit |
250 |
Max products to fetch (Shopify's page maximum) |
--in-stock-only |
off | Only include available variants |
--quantity |
1 |
Quantity placed in each cart link |
--format |
text |
text, json, or csv |
Examples
# Human-readable, in-stock only
shopify-atc https://www.allbirds.com --in-stock-only
# JSON for scripting
shopify-atc https://www.allbirds.com --limit 50 --format json > catalog.json
# CSV for a spreadsheet
shopify-atc https://www.allbirds.com --format csv > catalog.csv
Text output looks like:
Trino® Cozy Crew - Heathered Onyx
https://www.allbirds.com/products/trino-cozy-crew-heathered-onyx
S (W5-7) — $24.00
https://www.allbirds.com/cart/add?id=39574630924368&quantity=1
Exit codes: 0 success · 2 bad arguments or unreachable host · 3 HTTP error from the store · 4 not a Shopify storefront.
Use as a Python library
Everything the CLI does is available programmatically. The key names are re-exported from the top-level package:
import shopify_atc
base = shopify_atc.normalize_url("www.allbirds.com") # -> https://www.allbirds.com
try:
products = shopify_atc.fetch_products(base, limit=5)
except shopify_atc.ShopifyError as exc:
raise SystemExit(str(exc))
products = shopify_atc.filter_in_stock(products) # optional: drop sold-out variants
# Render to any format the CLI supports:
print(shopify_atc.render(products, "json", base, quantity=1))
# …or work with the dataclasses directly:
for product in products:
for variant in product.variants:
print(variant.title, variant.price, f"{base}/cart/add?id={variant.id}&quantity=1")
API at a glance
| Name | Description |
|---|---|
fetch_products(url, limit=250, *, timeout=15) |
Fetch + parse a store's products.json into Product objects. Raises a ShopifyError subclass on failure. |
filter_in_stock(products) |
Return products keeping only available variants; drops products left empty. |
normalize_url(url) |
Trim whitespace/trailing slash and default to https:// if no scheme. |
render(products, fmt, base_url, quantity) |
Render products as "text", "json", or "csv". |
Product, Variant |
Dataclasses describing a product and its variants. |
ShopifyError |
Base exception; subclasses NetworkError (exit 2), HTTPError (exit 3), NotShopifyError (exit 4). |
Development
# Install with dev dependencies
pip install -e ".[dev]"
# Run the test suite
pytest
Project Structure
Shopify-ATC/
├── shopify_atc/
│ ├── client.py # fetch + parse products.json into typed dataclasses; typed errors
│ ├── formatters.py # pure text / json / csv renderers
│ └── cli.py # argparse + error→exit-code wiring
├── tests/ # offline tests (HTTP mocked)
├── pyproject.toml # packaging + `shopify-atc` console entry point
└── .github/workflows/ # CI
Responsible use
Shopify-ATC reads only public endpoints and generates links — it does not log in, store payment details, or complete checkouts. It is not affiliated with Shopify. Respect each store's Terms of Service and avoid hammering a storefront with rapid repeated requests.
License
MIT
Author
Jacob Kanfer — GitHub
Release files for shopify-atc 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 | |
|---|---|---|---|
| shopify_atc-1.0.1.tar.gz | 13.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| shopify_atc-1.0.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 22.4 kB
Release files / shopify_atc-1.0.1.tar.gz
| Download URL | shopify_atc-1.0.1.tar.gz |
|---|---|
| Size | 13.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
7f135f56d426d83257295a15532d0ed6384899dca18afb52cc2abc3bab049333
|
|
BLAKE2b-256 checksum How to use checksums |
f2560df473fc203293884c3f97275bb80135509576d4e57859f431d3a4697253
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
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 Jun 1, 2026.
Transparency logRelease files / shopify_atc-1.0.1-py3-none-any.whl
| Download URL | shopify_atc-1.0.1-py3-none-any.whl |
|---|---|
| Size | 9.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
847771e15d8b39bb5638e5e30c56e7b46cc3fe06c555a1617bbe942aa16be4b9
|
|
BLAKE2b-256 checksum How to use checksums |
322e61e1225af25c4f14ce3b13a24976d86a1602d5fd1af8bddb1287dea57b5e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
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 Jun 1, 2026.
Transparency log