Skip to main content

python-appie

python-appie is an unofficial async Python client for the Albert Heijn API.

Full documentation is available in English and Dutch. The source for the docs lives in docs/ and is built from mkdocs.yml. Changelog tracks release history.

Releases are intended to publish to PyPI as python-appie from version tags via GitHub Actions.

Install

uv add python-appie

Or:

pip install python-appie

For local development in this repository:

uv sync --extra dev
pre-commit install

Quick start

Authenticate once:

uv run appie-login

This opens Chrome for an interactive AH login and captures the OAuth redirect code automatically. If automatic capture cannot start, the CLI falls back to asking for the redirect URL or raw code manually.

Then use the client:

import asyncio

from appie import AHClient


async def main() -> None:
    async with AHClient() as client:
        products = await client.products.search("melk", limit=3)
        for product in products:
            print(product)


asyncio.run(main())

Tokens are stored in ~/.config/appie/tokens.json and refreshed automatically when they are close to expiring.

Features

Authentication

  • appie-login CLI for browser-based login
  • automatic code capture from the AH redirect flow
  • token persistence in ~/.config/appie/tokens.json
  • automatic token refresh using the stored refresh token

Products

  • search products via client.products.search(query, limit=10)
  • fetch a single product via client.products.get(product_id)
  • product results include current price, original price, bonus metadata, and biologisch-related markers when available

Example:

import asyncio

from appie import AHClient


async def main() -> None:
    async with AHClient() as client:
        product = await client.products.get(1525)
        print(
            product.title,
            product.price,
            product.original_price,
            product.is_bonus,
            product.bonus_label,
            product.is_organic,
        )


asyncio.run(main())

Expected outcome:

AH Halfvolle melk 1.29 1.49 True 2e halve prijs True

Receipts

  • list in-store POS receipt summaries via client.receipts.list_all(limit=50)
  • fetch a receipt with line items via client.receipts.get_pos_receipt(receipt_id)

Important: list_all() and list_pos_receipts() return receipt summaries. In those results, products is intentionally empty. To retrieve line items, call get_pos_receipt() with a receipt ID from the summary list.

Example:

import asyncio

from appie import AHClient


async def main() -> None:
    async with AHClient() as client:
        receipts = await client.receipts.list_all(limit=5)
        detailed = await client.receipts.get_pos_receipt(receipts[0].id)
        print(detailed)


asyncio.run(main())

Shopping lists

  • add an item via client.lists.add_item(description, quantity=1, product_id=None)
  • read the current shopping list via client.lists.get_list()
  • remove one item via client.lists.remove_item(item_id)
  • clear the entire list via client.lists.clear()
  • use MockAHClient for local development and tests without touching AH

Example:

import asyncio

from appie import AHClient


async def main() -> None:
    async with AHClient() as client:
        await client.lists.add_item("Halfvolle melk", quantity=2)
        items = await client.lists.get_list()
        print(items)


asyncio.run(main())

Note: the ShoppingListItem.id returned by get_list() is an opaque removal key designed for remove_item(item_id). It should be treated as an implementation detail rather than a stable AH server identifier.

Mocking and downstream tests

  • MockAHClient() provides an in-memory drop-in client for local development
  • client.mock.calls and client.mock.last_call capture what your code did
  • client.mock.next_response(operation, value) seeds a one-shot result
  • client.mock.next_error(operation, exc) seeds a one-shot failure
  • client.mock.set_scenario(operation, delay_ms=..., error=...) applies persistent delay/error behavior
  • appie.pytest_plugin provides pytest fixtures for downstream packages

Example:

import asyncio

from appie import MockAHClient


async def main() -> None:
    async with MockAHClient() as client:
        client.mock.next_response("products.search", [])
        products = await client.products.search("melk")
        print(products)
        print(client.mock.last_call)


asyncio.run(main())

Expected outcome:

[]
AppieMockCall(operation='products.search', params={'query': 'melk', 'limit': 10}, result=[], error=None)

Pytest plugin example:

# tests/conftest.py
pytest_plugins = ["appie.pytest_plugin"]
import pytest


@pytest.mark.asyncio
async def test_checkout_uses_expected_query(appie_mock):
    await appie_mock.products.search("melk", limit=3)

    assert appie_mock.mock.last_call is not None
    assert appie_mock.mock.last_call.params == {"query": "melk", "limit": 3}

Expected outcome:

  • the test runs without touching AH
  • the recorded call proves what your code sent into python-appie

API overview

Main client

  • AHClient()
  • MockAHClient()
  • MockAHClient().mock
  • await client.login()
  • await client.graphql(query, variables=None)

Auth client

  • AHAuthClient.get_anonymous_token()
  • AHAuthClient.login_with_code(code)
  • AHAuthClient.refresh_token(refresh_token)

Sub-APIs

  • client.products.search(query, limit=10)
  • client.products.get(product_id)
  • client.receipts.list_pos_receipts(limit=50)
  • client.receipts.list_all(limit=50)
  • client.receipts.get_pos_receipt(receipt_id)
  • client.lists.add_item(description, quantity=1, product_id=None)
  • client.lists.get_list()
  • client.lists.remove_item(item_id)
  • client.lists.clear()

Mock helpers

  • client.mock.calls
  • client.mock.last_call
  • client.mock.clear_calls()
  • client.mock.next_response(operation, value)
  • client.mock.next_error(operation, exc)
  • client.mock.clear_seeded_responses()
  • client.mock.set_scenario(operation, delay_ms=0, error=None)
  • client.mock.clear_scenarios()

Development

Run checks locally:

uv run ruff format .
uv run --extra dev ruff check .
uv run --extra dev pyright
uv run --extra dev pytest
uv run --extra dev mkdocs build --strict

Notes

  • This client is unofficial and may break when Albert Heijn changes its backend.
  • Receipt support currently covers in-store POS receipts.
  • Shopping-list read, add, remove, and clear are implemented against the live main-list endpoint.
  • Receipt summaries do not include line items; call get_pos_receipt() for a detailed receipt.
  • Endpoint discovery for this package is inspired by gwillem/appie-go.

Metadata

Release files for python-appie 0.2.2

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

Source distribution (sdist)

Source distribution for python-appie 0.2.2
File Size Uploaded Provenance Actions
python_appie-0.2.2.tar.gz 99.3 kB With provenance Details for python_appie-0.2.2.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for python-appie 0.2.2
File Interpreter ABI Platform Actions
python_appie-0.2.2-py3-none-any.whl Python 3 none any Details for python_appie-0.2.2-py3-none-any.whl

Total release size: 118.1 kB

Release files / python_appie-0.2.2.tar.gz

Download URL python_appie-0.2.2.tar.gz
Size 99.3 kB
Tags Source
SHA-256 checksum
How to use checksums
2a8065784fa5e20cc493ea61c34edb265eacf7942e4c3d341fabcacfa2555917
BLAKE2b-256 checksum
How to use checksums
fd1394cc6e95d10cc2d4a35e951f7b859665f6a7903b82274ed470f24a1ade23
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

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

Transparency log

Release files / python_appie-0.2.2-py3-none-any.whl

Download URL python_appie-0.2.2-py3-none-any.whl
Size 18.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
4179b601c22167e03996d501b0e1ac30cb7ea4bc0eab78b417689f39d274f662
BLAKE2b-256 checksum
How to use checksums
e8080634bbfb8c13e896b589708e0b387be14a54dba9fac31668e1660dc0df75
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

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

Transparency log

Security for python-appie 0.2.2

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

Both files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for python_appie-0.2.2.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 Mar 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.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

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