Skip to main content

Async Python client for the Rohlik.cz API

Project description

🛒 Rohlik API Python Client

An async, fully typed Python client for the Rohlik.cz online grocery service — search products, manage your cart, browse recipes (Rohlík Chef), and read your orders and deliveries, all from Python.

⚠️ Unofficial — personal use only

This is an unofficial client for Rohlik.cz's non-public API. It is not affiliated with, authorized by, or endorsed by Rohlik.cz / Rohlik Group.

  • Intended for personal, non-commercial use with your own account only.
  • The private API can change or break at any time, without notice.
  • Your use may be subject to Rohlik.cz's Terms of Service — review them and behave responsibly (don't hammer the API or use it commercially).
  • Provided "as is", with no warranty. Use at your own risk.

Table of contents

Features

  • 🚀 Built on aiohttp; bring your own session (e.g. Home Assistant's shared session)
  • 🔐 Automatic login/logout, plus transparent re-authentication when a session expires (HTTP 401)
  • 🎯 Clean, service-based API (client.cart, client.products, …)
  • 🧩 Fully typed dataclass models for parsed responses (py.typed)
  • 🔄 Works as an async context manager
  • 🍳 Recipe search and ingredient shopping (Rohlík Chef)
  • 📦 Product details, composition/nutrition, prices, and AI summaries

Requirements

  • Python 3.13+
  • aiohttp (installed automatically)

Installation

pip install rohlik-api

Quick start

import asyncio
from rohlik_api import RohlikAPI

async def main():
    async with RohlikAPI(username="your_email@example.com", password="your_password") as client:
        # Search for products (returns a SearchResults model)
        results = await client.products.search("mleko", limit=5)
        for product in results.results:
            print(f"{product.name} - {product.price}")

        # Get cart contents (returns a Cart model)
        cart = await client.cart.get_content()
        print(f"Cart total: {cart.total_price} ({cart.total_items} items)")

        # Search recipes (returns a RecipeSearchResults model)
        recipes = await client.recipes.search("rajská", limit=5)
        print(f"Found {recipes.total_hits} recipes")

asyncio.run(main())

The async context manager logs you in on entry and logs out + closes the connection on exit.

Credentials & security

The client authenticates with your normal Rohlik.cz email and password.

  • Never hard-code credentials in source you commit. Prefer environment variables or a secrets manager:

    import os
    from rohlik_api import RohlikAPI
    
    client = RohlikAPI(
        username=os.environ["ROHLIK_USERNAME"],
        password=os.environ["ROHLIK_PASSWORD"],
    )
    
  • Credentials are only ever sent to Rohlik.cz over HTTPS. This library does not store or transmit them anywhere else.

  • Use a dedicated account if you're uncomfortable automating your primary one.

Typed models

Service methods that parse responses return typed dataclasses (importable from rohlik_api) rather than raw dictionaries, so your editor and type checker know the shape of the data:

from dataclasses import asdict
from rohlik_api import Cart, SearchResults

cart = await client.cart.get_content()   # -> Cart
cart.total_price                         # float
cart.products[0].name                    # str

# Convert any model to a plain dict (e.g. for JSON / Home Assistant / MCP):
asdict(cart)

Raw passthrough endpoints (orders.*, delivery.*, account.get_premium_profile, account.get_bags_info, account.get_announcements, and get_data) return the decoded JSON as dict / list, since they are not reshaped by the client.

Services

Functionality is grouped into services, accessed as properties on the client:

Service Property Description
Cart client.cart Shopping cart operations
Products client.products Product search and details
Orders client.orders Order history
Delivery client.delivery Delivery info and timeslots
Account client.account Account data and shopping lists
Recipes client.recipes Recipe search and ingredients (Rohlík Chef)

API reference

Cart service (client.cart)

# Get cart contents
cart = await client.cart.get_content()
# -> Cart(total_price=199.90, total_items=3, can_make_order=True, products=[CartItem, ...])

# Add items to cart
added = await client.cart.add_items([
    {"product_id": 123456, "quantity": 2},
    {"product_id": 789012, "quantity": 1},
])
# -> [123456, 789012]   (list of product IDs successfully added)

# Delete item from cart (raises APIRequestFailedError on failure)
await client.cart.delete_item(order_field_id="abc123")

Products service (client.products)

# Search for products -> SearchResults | None (None only on request failure)
results = await client.products.search("mléko", limit=10, favourite=False)
for product in results.results:  # ProductSearchResult: id, name, price, brand, amount
    print(product.name, product.price)

# AI-generated product summary -> AISummary | None
summary = await client.products.get_ai_summary(product_id=1384964)

# Composition / nutrition / allergens -> ProductComposition | None
composition = await client.products.get_composition(product_id=1425155)

# Current price -> ProductPrice | None
price = await client.products.get_price(product_id=1425155)

# Raw product detail (brand, attributes, …) -> dict | None (None on 404)
detail = await client.products.get_detail(product_id=1425155)

# Category hierarchy -> list[dict] | None (None if discontinued / 404)
categories = await client.products.get_categories(product_id=1425155)

Orders service (client.orders)

next_order = await client.orders.get_next()                       # upcoming order
last_order = await client.orders.get_last()                       # last delivered order
orders = await client.orders.get_delivered(limit=50, offset=0)    # one history page
all_orders = await client.orders.get_all_delivered()              # every page, paginated
detail = await client.orders.get_detail(order_id=12345678)        # full order incl. items

Delivery service (client.delivery)

delivery = await client.delivery.get_info()
timeslot = await client.delivery.get_timeslot_reservation()
slots = await client.delivery.get_next_slots()
announcements = await client.delivery.get_announcements()

Account service (client.account)

premium = await client.account.get_premium_profile()
bags = await client.account.get_bags_info()
announcements = await client.account.get_announcements()

# Shopping list by ID -> ShoppingList
shopping_list = await client.account.get_shopping_list("list_id_here")
# ShoppingList(name="My List", products_in_list=[...])

Recipes service (client.recipes) — Rohlík Chef

# Search recipes -> RecipeSearchResults(recipes=[RecipeSummary, ...], total_hits=4)
recipes = await client.recipes.search("rajská", limit=10, offset=0)

# Recipe details -> RecipeDetail | None
recipe = await client.recipes.get_detail(recipe_id=59)

# Products for ingredients -> IngredientProducts | None
products = await client.recipes.get_ingredient_products(ingredient_ids=[102, 56], limit=5)

Aggregated data

# Fetch delivery, orders, cart, premium profile, announcements, etc. in one call
all_data = await client.get_data()

Error handling

All errors derive from RohlikAPIError:

from rohlik_api import RohlikAPI, InvalidCredentialsError, APIRequestFailedError

try:
    async with RohlikAPI(username="email@example.com", password="password") as client:
        cart = await client.cart.get_content()
except InvalidCredentialsError:
    print("Wrong username or password")
except APIRequestFailedError as err:
    print(f"Request failed: {err}")

Error contract:

  • Critical / mutating operations (login, cart.get_content, cart.delete_item, account.get_shopping_list) raise APIRequestFailedError on failure.
  • Read / optional fetches (most orders, delivery, account, products, and recipes getters) return None on failure, so an aggregate fetch can continue gracefully.

Advanced usage

Configuration

client = RohlikAPI(
    username="your_email@example.com",
    password="your_password",
    base_url="https://www.rohlik.cz",  # optional
    timeout=30.0,                       # optional
    headers={"Custom-Header": "Value"}, # optional
    auto_login=True,                    # optional, default True
)

Manual session management

from rohlik_api import RohlikAPI

async def main():
    client = RohlikAPI(
        username="email@example.com",
        password="password",
        auto_login=False,  # disable auto-login
    )
    try:
        await client.login()
        cart = await client.cart.get_content()
        await client.logout()
    finally:
        await client.close()

Reusing an existing aiohttp session

The client is built on aiohttp. By default it creates and owns its own ClientSession, but you can inject an externally managed session instead — useful inside a Home Assistant integration, where the recommended pattern is to share a single session per instance. An injected session is never closed by the client; its lifecycle stays with the owner.

import aiohttp
from rohlik_api import RohlikAPI

async def main(session: aiohttp.ClientSession):
    client = RohlikAPI(
        username="email@example.com",
        password="password",
        session=session,  # reuse the caller's session
    )
    async with client:
        cart = await client.cart.get_content()
    # `session` is left open for the caller to close.

Inside a Home Assistant integration you would pass the shared session, for example RohlikAPI(..., session=async_get_clientsession(hass)).

Development

# Install with development dependencies
pip install -e ".[dev]"

# Run the test suite
pytest

# Lint, format check and type check
ruff check .
black --check .
mypy rohlik_api

Please make sure pytest, ruff, black and mypy all pass before opening a pull request.

Disclaimer

This project is an independent, unofficial client. It is not affiliated with, authorized by, or endorsed by Rohlik.cz, Rohlik Group, or any of its subsidiaries. "Rohlik", "Rohlik.cz" and "Rohlík Chef" are trademarks of their respective owners.

It talks to a private, undocumented API that may change or stop working at any time. It is provided for personal, non-commercial use only, and comes with no warranty of any kind. You are responsible for complying with Rohlik.cz's Terms of Service and applicable law. Use at your own risk.

License

MIT © Daniel Vejsada

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

rohlik_api-0.1.0.tar.gz (40.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

rohlik_api-0.1.0-py3-none-any.whl (31.9 kB view details)

Uploaded Python 3

File details

Details for the file rohlik_api-0.1.0.tar.gz.

File metadata

  • Download URL: rohlik_api-0.1.0.tar.gz
  • Upload date:
  • Size: 40.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for rohlik_api-0.1.0.tar.gz
Algorithm Hash digest
SHA256 b405d0e411ce0da3b4214db62627b59c14bc142b0d8cd1572d6ff8e198e7645f
MD5 a67b66ed1924eb8f1dd5307a19681323
BLAKE2b-256 6fb623bf7f35b8dc5685d3e201571e6b08f46a2e27bef76b25f194454fc8b976

See more details on using hashes here.

Provenance

The following attestation bundles were made for rohlik_api-0.1.0.tar.gz:

Publisher: publish.yml on dvejsada/rohlik_api_python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file rohlik_api-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: rohlik_api-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 31.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for rohlik_api-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 4a6b05aca6d0357f5781e506949021cce7a5cd910c628d1ee6535a1991359a38
MD5 715f020811ee68a76c2a3fd5da49b876
BLAKE2b-256 c59b7f012e44605329745dd2b9e0d7cd91d9ea9f214a9483c46a943cd8c048b7

See more details on using hashes here.

Provenance

The following attestation bundles were made for rohlik_api-0.1.0-py3-none-any.whl:

Publisher: publish.yml on dvejsada/rohlik_api_python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page