Skip to main content

Python-Picnic-API

This library is undergoing rapid changes as is the Picnic API itself. It is mainly intended for use within Home Assistant, but there are integration tests running regularly checking for failures in features not used by the Home Assistant integration.

If you want to know why interacting with Picnic is getting harder than ever, check out their blogpost about architectural changes: https://blog.picnic.nl/adding-write-functionality-to-pages-with-self-service-apis-d09aa7dbc9c0

Fork of the Unofficial Python wrapper for the Picnic API. While not all API methods have been implemented yet, you'll find most of what you need to build a working application is available.

This library is not affiliated with Picnic and retrieves data from the endpoints of the mobile application. Use at your own risk.

Credits

A big thanks to @MikeBrink for building the first versions of this library.

@maartenpaul and @thijmen-j continously provided fixes that were then merged into this fork.

Getting started

The easiest way to install is directly from pip:

$ pip install python-picnic-api2

Then create a new instance of PicnicAPI and login using your credentials:

from python_picnic_api2 import PicnicAPI

picnic = PicnicAPI(username='username', password='password', country_code="NL")

The country_code parameter defaults to NL, but you have to change it if you live in a different country than the Netherlands (ISO 3166-1 Alpha-2). This obviously only works for countries that picnic services.

Two-factor authentication (2FA)

For new logins, Picnic may require two-factor authentication. When 2FA is required, logging in raises a Picnic2FARequired exception. You then need to request a code and verify it:

from python_picnic_api2 import PicnicAPI, Picnic2FARequired, Picnic2FAError

picnic = PicnicAPI(country_code="NL")

try:
    picnic.login(username='username', password='password')
except Picnic2FARequired:
    # Request a code via SMS or EMAIL
    picnic.generate_2fa_code(channel="SMS")

    code = input("Enter the code you received: ")
    picnic.verify_2fa_code(code)

After successful verification, the session is authenticated and you can use the API normally. If the code is invalid, Picnic2FAError is raised.

Typed models (2.x)

As of 2.x the API returns typed pydantic models instead of raw dicts. This covers both the "page" endpoints Picnic serves as a layout tree of widgets (search, get_article, get_category_by_ids) and the domain-JSON endpoints (get_user, get_cart, get_delivery_slots, get_delivery, get_deliveries / get_current_deliveries, and the cart-mutation methods). Every model exposes .raw with the original, untouched payload as an escape hatch for data that isn't modelled yet, and .model_dump() for a plain-dict view.

A couple of endpoints still return raw dicts: get_delivery_scenario and get_delivery_position (only populated while a delivery is en route, so there is no stable shape to model), and get_article_category (appears to have been removed by Picnic — use get_article(id, add_category=True) instead).

If you are upgrading from 1.x, see the migration notes.

Usage

Searching for an article

result = picnic.search('coffee')          # -> SearchResult
result.items[0].name                        # 'Lavazza Caffè Crema e Aroma Bohnen'
result.items[0].display_price               # 1799  (price shown on the tile, in cents)
result.items[0].raw                         # original tile payload

Search tiles only carry display_price (the price shown, in integer cents) — the raw payload has no separate price key — so read display_price.

Get article by ID

article = picnic.get_article("s1019822")   # -> Article | None
article.id                                  # 's1019822'
article.name                                # 'Lavazza Caffè Crema e Aroma Bohnen'
article.product_name                        # 'Caffè Crema e Aroma Bohnen'
article.producer                            # 'Lavazza'  (None for unbranded produce)
article.unit_quantity                       # '1kg'
article.price_per_unit                      # '€17.99/kg'  (comparative price, may be None)
article.price                               # 1799  (current price, integer cents)
article.original_price                      # 2249 when on sale, else None
article.image_id                            # hero product image id
article.description                         # product description (markdown)
article.highlights                          # ['Lange **haltbar**', ...] feature bullets
article.is_bundle                           # True for multipacks with other pack sizes
article.bundle_variant_ids                  # ['s1018999', ...] other pack-size article ids

# Optionally resolve the article's category (an extra request):
article = picnic.get_article("s1019822", add_category=True)
article.category.name                       # 'Koffiebonen'

Get article by GTIN (EAN)

article = picnic.get_article_by_gtin("8000070025400")  # -> Article | None
article.name                                # 'Lavazza Caffè Crema e Aroma Bohnen'

Get the user

user = picnic.get_user()          # -> User
user.contact_email                  # 'you@example.com'
user.address.city                   # 'Amsterdam'
user.total_deliveries               # 25

Check cart

cart = picnic.get_cart()          # -> Cart
cart.total_count                    # 3
cart.total_price                    # 1234  (integer cents)
cart.items[0].items[0].name         # 'Lavazza Caffè Crema e Aroma Bohnen'
cart.raw                            # original cart payload

Manipulating your cart

All of these methods return the updated Cart.

# Add product with ID "s1019822" 2x
picnic.add_product("s1019822", 2)

# Remove product with ID "s1019822" 1x
picnic.remove_product("s1019822")

# Clear your cart
picnic.clear_cart()

See upcoming deliveries

deliveries = picnic.get_current_deliveries()   # -> list[DeliverySummary]
deliveries[0].delivery_id
deliveries[0].status                             # 'CURRENT'
deliveries[0].slot.window_start                  # '2025-04-29T17:15:00.000+02:00'

# Full detail (order lines, articles, payment info) for one delivery:
delivery = picnic.get_delivery(deliveries[0].delivery_id)   # -> Delivery
delivery.orders[0].items[0].items[0].name

See available delivery slots

slots = picnic.get_delivery_slots()   # -> DeliverySlots
slots.delivery_slots[0].window_start    # '2025-04-29T17:15:00.000+02:00'
slots.selected_slot.slot_id

Migrating from 1.x to 2.0

  • search() now returns a SearchResult (.items is a list of SearchResultItem) instead of [{"items": [...]}].
  • get_article() / get_article_by_gtin() now return an Article (or None) instead of a dict; use .id / .name / .category instead of key access.
  • get_category_by_ids() now returns a Category instead of a dict.
  • Missing/unexpected PML nodes now raise PicnicParseError (from python_picnic_api2) instead of a bare KeyError.
  • The domain-JSON methods now return typed models instead of raw dicts: get_user() → User, get_cart() / add_product() / remove_product() / clear_cart() → Cart, get_delivery_slots() → DeliverySlots, get_delivery() → Delivery, and get_deliveries() / get_current_deliveries() → list[DeliverySummary]. Use attribute access (cart.items, user.contact_email) instead of ["items"] / ["contact_email"].
  • get_delivery_scenario(), get_delivery_position() and get_article_category() still return raw dicts (see Typed models).
  • Any field you need that isn't modelled yet is available on model.raw.

Metadata

Release files for python-picnic-api2 2.0.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 python-picnic-api2 2.0.1
File Size Uploaded
python_picnic_api2-2.0.1.tar.gz 59.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for python-picnic-api2 2.0.1
File Interpreter ABI Platform
python_picnic_api2-2.0.1-py3-none-any.whl Python 3 none any Details

Total release size: 86.4 kB

Release files / python_picnic_api2-2.0.1.tar.gz

Download URL python_picnic_api2-2.0.1.tar.gz
Size 59.2 kB
Tags Source
SHA-256 checksum
How to use checksums
43ed8d5a39b591013150f2a0b7f74595a78d400041d0e249f08f69ab230dd183
BLAKE2b-256 checksum
How to use checksums
e789678b10beb325b7250f8b883b9eb76273c5798d2c83546e973fbc6c7b0dc9
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 Aug 5, 2026.

Transparency log

Release files / python_picnic_api2-2.0.1-py3-none-any.whl

Download URL python_picnic_api2-2.0.1-py3-none-any.whl
Size 27.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
8e5c928bbdf4ca477de6ccfe0bb3265345499bcff12528761e47bf16113c15a5
BLAKE2b-256 checksum
How to use checksums
91f220ede25f1dfcc137b79a355f835b8ae60609ecfff85927962c300511cfcc
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 Aug 5, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

2.0.1 This release

2 release files

2.0.0

2 release files

1.3.4

2 release files

1.3.3

2 release files

1.3.2

2 release files

1.3.1

2 release files

1.3.0

2 release files

1.2.4

2 release files

1.2.3

2 release files

1.2.2

2 release files

1.2.1

2 release files

1.1.0

2 release files

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