Skip to main content

Pyroski

Asynchronous Python client for Eroski Digital Services via the Tempos21 Mobile Gateway (xpas1.tempos21.com).

It provides clean, scoped access to digital purchase receipts, itemized line items, PDF receipt downloads, digital saving coupons (vales de ahorro), customer loyalty cards, store occupancy (aforos), and fuel prices.


Features

  • Long-Lived Bearer Token: Logs in once using the mobile login flow and persists an OAuth2 Bearer token across runs (~68 years validity).
  • Auto-Healing Token Refresh: Automatically detects 401 Unauthorized responses and re-authenticates transparently.
  • Modern Scoped Interface: Logical resource scoping via api.tickets, api.coupons, api.customer, and api.stores.
  • Digital Receipt Export: Fetch itemized receipt details, VAT breakdowns, or download original receipts in PDF format.
  • Batch Coupon Activation: Activate all eligible savings coupons concurrently with throttled semaphores.
  • Headless 2FA Support: Custom SMS handlers for headless or automated environments (e.g. bots, webhooks, Home Assistant).

Installation

Install using uv (recommended) or pip:

# Using uv
uv add pyroski

# Or using pip
pip install pyroski

Configuration & Initialization

Credentials can be passed explicitly in code or loaded automatically from environment variables:

export EROSKI_EMAIL="your_email@example.com"
export EROSKI_PASSWORD="your_password"
export EROSKI_TOKEN_PATH=".eroski_token.json"  # optional, default: .eroski_token.json

Initialization Options

The Api constructor supports several configuration options:

Api(
    email: str | None = None,
    password: str | None = None,
    token_path: str | Path | None = None,
    sms_handler: Callable[[], Awaitable[str] | str] | None = None,
    timeout: float = 30.0,
    api_version: str = "v20",
)
Parameter Type Default Description
email str | None os.getenv("EROSKI_EMAIL") Account email address. Required if env var is unset.
password str | None os.getenv("EROSKI_PASSWORD") Account password. Required if env var is unset.
token_path str | Path | None ".eroski_token.json" File path where the OAuth2 Bearer token is stored and reused. Set via arg or EROSKI_TOKEN_PATH.
sms_handler Callable | None None (CLI input()) Custom sync or async callback returning the 5-digit SMS 2FA code. Ideal for headless servers, Telegram bots, or webhooks.
timeout float 30.0 HTTP request timeout in seconds for all gateway calls.
api_version str "v20" Tempos21 gateway API version prefix (e.g. "v20").

Examples

1. Standard Initialization (Environment Variables):

from pyroski import Api

# Reads EROSKI_EMAIL and EROSKI_PASSWORD from environment
api = Api()

2. Custom Token Location & Timeout:

api = Api(
    email="user@example.com",
    password="secretpassword",
    token_path="/var/data/eroski_token.json",
    timeout=45.0,
)

3. Headless / Automated SMS 2FA Handler:

async def fetch_sms_from_telegram_or_db() -> str:
    # Await code from external notification, webhook, or queue
    return await external_2fa_service.get_latest_code()


api = Api(
    email="user@example.com",
    password="secretpassword",
    sms_handler=fetch_sms_from_telegram_or_db,
)

Quickstart

import asyncio
from pyroski import Api


async def main():
    async with Api() as api:
        # 1. Customer profile
        customer = await api.customer.get()
        print(f"Customer: {customer.name} {customer.surname} | Card: {customer.club_card}")

        # 2. Latest receipts
        recent_tickets = await api.tickets.latest()
        for t in recent_tickets:
            print(f"Ticket #{t.ticket_number}: {t.total_amount}€ at {t.center_name}")

        # 3. Activate all coupons
        activated = await api.coupons.activate_all()
        print(f"Activated {activated} coupon(s)")


if __name__ == "__main__":
    asyncio.run(main())

API Endpoints

The API is organized into resource-scoped services:

1. Tickets (api.tickets)

Manage and retrieve digital purchase receipts:

Method Description
await api.tickets.latest() Retrieves the last 5 purchase receipts.
await api.tickets.by_month(year: int, month: int) Retrieves all tickets for a specific month, automatically handling pagination.
await api.tickets.by_year(year: int) Retrieves all tickets for an entire calendar year (all 12 months).
await api.tickets.since(year: int, month: int) Retrieves all tickets starting from a specific year and month up to the current date.
await api.tickets.get(ticket_id: str) Fetches the full itemized receipt breakdown, including lines, prices, discounts, and VAT items.
await api.tickets.pdf(ticket_id: str) Downloads the receipt as a TicketPdf model containing raw PDF bytes and metadata.
await api.tickets.save_pdf(ticket_id: str, path: str | Path) Downloads and writes the ticket receipt PDF directly to a file on disk.
# Download latest receipt as PDF
tickets = await api.tickets.latest()
if tickets:
    pdf_path = await api.tickets.save_pdf(tickets[0].ticket_id, "./receipt.pdf")
    print(f"Saved PDF receipt to {pdf_path}")

2. Coupons (api.coupons)

Retrieve and activate digital saving coupons (vales):

Method Description
await api.coupons.all(limit: int = 100, offset: int = 0) Retrieves available and active saving coupons for the customer.
await api.coupons.activate(num_offer: str | int, num_sequence: str | int = "") Activates an individual coupon by offer number and optional sequence.
await api.coupons.activate_all(max_concurrent: int = 5) Concurrently activates all eligible unactivated coupons.
# List and activate all coupons
coupons = await api.coupons.all()
print(f"Found {len(coupons)} coupon(s)")

count = await api.coupons.activate_all(max_concurrent=3)
print(f"Activated {count} coupon(s)")

3. Customer Profile (api.customer)

Access customer loyalty account and membership data:

Method Description
await api.customer.get() Returns profile information including loyalty card number, customer barcode, tier, and favorite store ID.
customer = await api.customer.get()
print(f"Club Card: {customer.club_card} | Favorite Store CECO: {customer.favourite_shop}")

4. Stores (api.stores)

Access live store metadata, affluence, and fuel pricing:

Method Description
await api.stores.influx(shop_id: str) Retrieves real-time occupancy and estimated hourly affluence for a store.
await api.stores.fuel_prices(shop_id: str) Retrieves real-time fuel prices for a specific Eroski gas station.
# Check store affluence
occupancy = await api.stores.influx("159")
print(f"Current affluence: {occupancy}")

Error Handling

Pyroski defines specific exception types for authentication and API errors:

  • PyroskiError: Base exception for all library errors.
  • AuthenticationError: Raised on invalid credentials or failed OAuth2 token exchange.
  • TwoFactorAuthError: Raised when SMS 2FA is challenged or verification fails.

Metadata

Release files for pyroski 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pyroski 0.1.1
File Size Uploaded Provenance
pyroski-0.1.1.tar.gz 13.2 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for pyroski 0.1.1
File Interpreter ABI Platform
pyroski-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 28.1 kB

Release files / pyroski-0.1.1.tar.gz

Download URL pyroski-0.1.1.tar.gz
Size 13.2 kB
Tags Source
SHA-256 checksum
How to use checksums
48f73670078cbef7abbc1938432c7334b28a2bcf61fe0d543da11127aa3f910e
BLAKE2b-256 checksum
How to use checksums
7e1b95f41d16880a9bcc2dd7df016821980b04bc4ef6e59725eea9934ebe20c5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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 22, 2026.

Transparency log

Release files / pyroski-0.1.1-py3-none-any.whl

Download URL pyroski-0.1.1-py3-none-any.whl
Size 14.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
86994e58d17823d80907225d879e1475266e8bed5fa4b348425477008c9d729d
BLAKE2b-256 checksum
How to use checksums
b0e734dc0c7276fefa7e8c68f4314d1ddc8620a5d80123e6f9c29312fa37b230
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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 22, 2026.

Transparency log

Security for pyroski 0.1.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.

Select from 2 files

Attestation for pyroski-0.1.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 22, 2026.

Transparency log

A 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.

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page