Skip to content

Basic Usage

Client lifecycle

Create a :class:~pysainsburys.Sainsburys client with a :class:~pysainsburys.GOLAuth session and close it when finished. The async context manager is the simplest pattern:

from pysainsburys import GOLAuth, Sainsburys

auth = await GOLAuth.from_session_file("~/.config/pysainsburys/session.json")

async with Sainsburys(auth) as client:
    customer = await client.get_customer()
    basket = await customer.basket.fetch()

For public endpoints (search, product detail, store lookup), an empty GOLAuth() is sufficient — no saved session is required.

Authentication

Method When to use
await GOLAuth.from_session_file(path) Reuse a session saved by the CLI or a previous login
auth.login(email, password) Credential login (MFA supported)
auth.send_login_request() + auth.finish_login(redirect) Browser-based OAuth with PKCE

After login, persist the session for reuse:

await auth.save_session_file("~/.config/pysainsburys/session.json")

Products

# Search (public)
results = await client.search_products("milk", page_number=1, page_size=24)

# Detail with nutrition (public)
product = await client.get_product("3236048")
print(product.nutrition.summary if product.nutrition else "no nutrition data")

Basket

Authenticated basket changes go through customer.basket:

customer = await client.get_customer()

basket = await customer.basket.fetch()
await customer.basket.add("3236048", 2)
await customer.basket.set_quantity("3236048", 3, item_uid="line-uid")
await customer.basket.remove("3236048")
await customer.basket.clear()

You can also mutate a bound :class:~pysainsburys.models.product.product.Product:

product = await client.get_product("3236048")
await product.add_to_basket(2)
await product.set_basket_quantity(0)  # removes the line

CLI examples::

pysainsburys basket show
pysainsburys basket add 3236048 --quantity 2
pysainsburys basket set 3236048 3 --item-uid LINE_UID
pysainsburys basket remove 3236048
pysainsburys basket clear

Use basket show to read each line's line id before set or remove.

Slots

List delivery or click-and-collect availability through customer.slots:

customer = await client.get_customer()

week = await customer.slots.list_delivery()
for slot in week.available_slots:
    print(slot.start_time, slot.price)

collection = await customer.slots.list_collection(store_identifier="0474")
reservation = await customer.slots.fetch_reservation()

# Reserve a listed slot, or use the same method to change the current slot.
reservation = await customer.slots.reserve(week.available_slots[0])
reservation = await customer.slots.validate()

CLI examples::

pysainsburys slots list --type delivery
pysainsburys slots list --type collection --store 0474
pysainsburys slots reservation
pysainsburys slots reserve SLOT_UID --type delivery
pysainsburys slots validate
pysainsburys slots context

The list call uses POST /slot/v2/slots with X-Http-Method-Override: GET. Location context is fetched automatically unless --no-context is passed on the CLI or use_location_context=False in Python.

Slot reservation writes use the statically inferred POST /slot/v1/slot/reservation payload. They have not yet been verified against a live commerce session and should be treated as experimental.

Customer resources

Authenticated helpers hang off :class:~pysainsburys.models.customer.Customer:

customer = await client.get_customer()

favourites = await customer.favourites.fetch()
await customer.favourites.add("3236048")

orders = await customer.orders.fetch()
status = await customer.orders.latest.status()

Stores

# Nearby stores (public)
stores = await client.find_stores(lat=51.5, lon=-0.12)

# Click and collect by postcode (public)
stores = await client.find_stores_by_postcode("SW1A 1AA")

# In-store aisle search (store must be bound to client)
store = await client.get_store("1234")
products = await store.search_products("milk")

Refreshing cached state

Call :meth:~pysainsburys.Sainsburys.update to reload customer, basket, favourites, orders, and the latest order status in one pass:

await client.update()

Register callbacks to run after each update cycle:

client.register_callback(lambda: print("data refreshed"))

Models

Domain objects live under :mod:pysainsburys.models. See the models guide for the package layout and import recommendations.

CLI

Command groups mirror the library modules:

Group Commands
auth url, finish, login, mfa, resend-mfa, refresh, logout
customer show
basket show, add, set, remove, clear
favourites list, add, remove
orders list, show, status
slots list, reservation, reserve, validate, context
product show, search
store near, postcode, show, search
python -m pysainsburys --help
python -m pysainsburys product search bread --page 2
python -m pysainsburys product show 3236048 --json
python -m pysainsburys product search bread --raw
python -m pysainsburys basket add 3236048 --quantity 2
python -m pysainsburys favourites add 3236048
python -m pysainsburys slots list --type delivery
python -m pysainsburys store search 2665 milk
python -m pysainsburys auth refresh

--json prints the command result as one JSON object, including list metadata such as pagination. --raw prints the records as a JSON array. Each element includes every public attribute of that object, including nested models. The two flags cannot be combined.

Session files default to ~/.config/pysainsburys/session.json. Override with --session /path/to/session.json.