supermercapy
typed clients for the public storefront apis of fourteen spanish supermarkets, for sync code and, through AsyncClient, for asyncio. a shared base client defines the standard operations, each store implements them against its own backend, and every client declares which optional operations and data shapes its storefront supports.
this is an unofficial project. it is not affiliated with, endorsed by, or maintained by any of the retailers it reads. the upstream apis are undocumented and may change without notice.
installation
supermercapy supports cpython 3.11 through 3.14. the distribution and the import name are both supermercapy.
python -m pip install supermercapy
quick start
bind a client to a place, search, then ask for the complete product:
from supermercapy import Mercadona
with Mercadona.from_postal_code("28001") as mercadona:
page = mercadona.search_products("leche sin lactosa", page_size=10)
for summary in page.products:
print(summary.name, summary.price.amount)
if page.products:
product = mercadona.get_product(page.products[0].id)
print(product.ean)
print(product.nutrition.ingredients)
every client is built the same way, so the same code reads another store:
from supermercapy import Capability, Lidl
with Lidl.from_postal_code("08013") as lidl:
page = lidl.search_products("chocolate", page_size=10)
if lidl.supports(Capability.OFFERS):
offers = lidl.get_offers()
construction performs no request. from_postal_code() costs one request at mercadona and more at lidl, which pages through its store list. the request counts give every store's figure. a client reuses its connections, so close it with a with block.
search_all() asks several stores at once, each on its own thread with its own client. it reports every store separately, so one store's failure never hides what the others found:
from supermercapy import search_all
result = search_all("leche", postal_code="08013", page_size=5)
for store, product in result.products:
print(store, product.name, product.price.amount)
for entry in result.failed:
print(entry.store, entry.error_type, entry.reason)
command line
installing the package also installs a supermercapy command, which prints aligned tables, or one json document with --json:
supermercapy stores
supermercapy search leche --postal-code 08013 --limit 3
supermercapy product consum 7080604
supermercapy ean 8431876011937 --json
the command line guide covers every command, its request count, and its exit codes.
stores
| store | class | binds to | page |
|---|---|---|---|
| mercadona | Mercadona |
a warehouse | docs |
| consum | Consum |
a zone, optionally | docs |
| plusfresc | Plusfresc |
a preparation center | docs |
| bonàrea | Bonarea |
nothing | docs |
| carrefour | Carrefour |
a sale point, optionally | docs |
| lidl | Lidl |
an offer region and a price zone | docs |
| bonpreu | Bonpreu |
nothing | docs |
| dia | Dia |
a delivery postcode, optionally | docs |
| eroski | Eroski |
nothing | docs |
| caprabo | Caprabo |
nothing | docs |
| aldi | Aldi |
a price region | docs |
| ahorramás | Ahorramas |
nothing | docs |
| alcampo | Alcampo |
a click-and-collect point, optionally | docs |
| condis | Condis |
a picking centre | docs |
supermercapy.ALL_CLIENTS holds all fourteen, so generic code never has to name one.
capabilities
search_products(), get_product(), get_categories() and get_category() work everywhere. everything else is optional and declared, because no two storefronts publish the same things:
| capability | stores |
|---|---|
POSTAL_CODE |
mercadona, consum, plusfresc, carrefour, lidl, dia, aldi, condis |
STORES |
mercadona, consum, plusfresc, carrefour, lidl, alcampo |
CATALOG |
mercadona, consum, plusfresc, bonàrea, lidl, dia, eroski, caprabo, aldi, ahorramás, condis |
EAN_LOOKUP |
consum, carrefour |
NEW_ARRIVALS |
mercadona, consum, plusfresc, bonpreu, dia, alcampo, condis |
OFFERS |
consum, plusfresc, lidl, bonpreu, dia, aldi, ahorramás, alcampo, condis |
HOME |
mercadona, carrefour |
EAN |
mercadona, consum, carrefour, lidl |
NUTRITION |
mercadona, plusfresc, bonàrea, carrefour, bonpreu, dia, eroski, caprabo, alcampo, condis |
PROMOTIONS |
consum, plusfresc, carrefour, lidl, bonpreu, dia, eroski, caprabo, aldi, ahorramás, alcampo, condis |
FUTURE_PRICES |
lidl, aldi |
an undeclared operation raises UnsupportedOperationError before making any request, and Client.supports(capability) answers before a client is even built. a test checks the full matrix against the code, so it cannot drift.
behavior
- client methods make the requests. reading a returned model never performs i/o.
- listing calls return
ProductSummary, andget_product()returns a completeProduct. each store narrows both to its own subclass. - models are frozen, slotted dataclasses. collections are tuples, ids are strings, and prices and quantities use
Decimal. - parsers ignore unknown response fields. missing optional values become
Noneor an empty tuple. - pagination is one opaque cursor, whatever the store does underneath.
- failures raise a
SupermercapyErrorsubclass. library code never prints. - connection failures, http
429and selected5xxresponses go through one bounded retry policy. stores that answer with challenges or expired tokens extend the classification, not the loop. - request pacing is per client, and a store that needs it ships a default.
documentation
- documentation index
- usage guide
- command line
- api guide
- generated api reference
- reliability and request behavior
- async
- caching
- matching products across stores
- examples: a shopping list priced at every store near a postcode, daily catalog snapshots and what changed between them, and the cheapest alternative to a branded product
- contribution guide
- release process
- changelog
supermercapy is distributed under the mit license.
Metadata
Release files for supermercapy 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| supermercapy-0.1.0.tar.gz | 725.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| supermercapy-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 981.7 kB
Release files / supermercapy-0.1.0.tar.gz
| Download URL | supermercapy-0.1.0.tar.gz |
|---|---|
| Size | 725.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
843239e428662b9b6bc77f9c25a663292b00ad325ac2f99ed6b362eb7fc74bf9
|
|
BLAKE2b-256 checksum How to use checksums |
1335851b686b381fa31a20ba49270d487ca9eade053a87f1d6f320370fda9fa2
|
| 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 6, 2026.
Transparency logRelease files / supermercapy-0.1.0-py3-none-any.whl
| Download URL | supermercapy-0.1.0-py3-none-any.whl |
|---|---|
| Size | 256.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
e0c7ad39459ef6b4a49e050b729fc9dfc77a43e7c7128cc60e8d5fbd6a85124c
|
|
BLAKE2b-256 checksum How to use checksums |
a7188390826f74b873eaebca94b6be37c7bb0629e2f74261b52a8e4f1e869b2c
|
| 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 6, 2026.
Transparency log