44
55** 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 ] ( https://jobs.picnic.app/en/blogs/adding-write-functionality-to-pages-with-self-service-apis ) **
66
7- Fork of the Unofficial Python wrapper for the [ Picnic] ( https://picnic.app ) API. While not all API methods have been implemented yet, you'll find most of what you need to build a working application are available.
7+ Fork of the Unofficial Python wrapper for the [ Picnic] ( https://picnic.app ) 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.
88
99This library is not affiliated with Picnic and retrieves data from the endpoints of the mobile application. ** Use at your own risk.**
1010
@@ -62,14 +62,18 @@ domain-JSON endpoints (`get_user`, `get_cart`, `get_delivery_slots`,
6262` get_delivery ` , ` get_deliveries ` / ` get_current_deliveries ` , and the cart-mutation
6363methods). Every model exposes ` .raw ` with the original, untouched payload as an
6464escape hatch for data that isn't modelled yet, and ` .model_dump() ` for a
65- plain-dict view. See the [ migration notes ] ( #migrating-from-1x-to-20 ) .
65+ plain-dict view.
6666
6767A couple of endpoints still return raw dicts: ` get_delivery_scenario ` and
6868` get_delivery_position ` (only populated while a delivery is en route, so there is
6969no stable shape to model), and ` get_article_category ` (appears to have been
7070removed by Picnic — use ` get_article(id, add_category=True) ` instead).
7171
72- ## Searching for an article
72+ If you are upgrading from 1.x, see the [ migration notes] ( #migrating-from-1x-to-20 ) .
73+
74+ ## Usage
75+
76+ ### Searching for an article
7377
7478``` python
7579result = picnic.search(' coffee' ) # -> SearchResult
@@ -81,7 +85,7 @@ result.items[0].raw # original tile payload
8185Search tiles only carry ` display_price ` (the price shown, in integer cents) — the
8286raw payload has no separate ` price ` key — so read ` display_price ` .
8387
84- ## Get article by ID
88+ ### Get article by ID
8589
8690``` python
8791article = picnic.get_article(" s1019822" ) # -> Article | None
@@ -104,32 +108,14 @@ article = picnic.get_article("s1019822", add_category=True)
104108article.category.name # 'Koffiebonen'
105109```
106110
107- ## Get article by GTIN (EAN)
111+ ### Get article by GTIN (EAN)
112+
108113``` python
109114article = picnic.get_article_by_gtin(" 8000070025400" ) # -> Article | None
110115article.name # 'Lavazza Caffè Crema e Aroma Bohnen'
111116```
112117
113- ## Migrating from 1.x to 2.0
114-
115- - ` search() ` now returns a ` SearchResult ` (` .items ` is a list of ` SearchResultItem ` )
116- instead of ` [{"items": [...]}] ` .
117- - ` get_article() ` / ` get_article_by_gtin() ` now return an ` Article ` (or ` None ` )
118- instead of a ` dict ` ; use ` .id ` / ` .name ` / ` .category ` instead of key access.
119- - ` get_category_by_ids() ` now returns a ` Category ` instead of a ` dict ` .
120- - Missing/unexpected PML nodes now raise ` PicnicParseError ` (from
121- ` python_picnic_api2 ` ) instead of a bare ` KeyError ` .
122- - The domain-JSON methods now return typed models instead of raw dicts:
123- ` get_user() ` → ` User ` , ` get_cart() ` / ` add_product() ` / ` remove_product() ` /
124- ` clear_cart() ` → ` Cart ` , ` get_delivery_slots() ` → ` DeliverySlots ` ,
125- ` get_delivery() ` → ` Delivery ` , and ` get_deliveries() ` /
126- ` get_current_deliveries() ` → ` list[DeliverySummary] ` . Use attribute access
127- (` cart.items ` , ` user.contact_email ` ) instead of ` ["items"] ` / ` ["contact_email"] ` .
128- - ` get_delivery_scenario() ` , ` get_delivery_position() ` and ` get_article_category() `
129- still return raw dicts (see [ Typed models] ( #typed-models-2x ) ).
130- - Any field you need that isn't modelled yet is available on ` model.raw ` .
131-
132- ## Get the user
118+ ### Get the user
133119
134120``` python
135121user = picnic.get_user() # -> User
@@ -138,7 +124,7 @@ user.address.city # 'Amsterdam'
138124user.total_deliveries # 25
139125```
140126
141- ## Check cart
127+ ### Check cart
142128
143129``` python
144130cart = picnic.get_cart() # -> Cart
@@ -148,7 +134,8 @@ cart.items[0].items[0].name # 'Lavazza Caffè Crema e Aroma Bohnen'
148134cart.raw # original cart payload
149135```
150136
151- ## Manipulating your cart
137+ ### Manipulating your cart
138+
152139All of these methods return the updated ` Cart ` .
153140
154141``` python
@@ -162,7 +149,7 @@ picnic.remove_product("s1019822")
162149picnic.clear_cart()
163150```
164151
165- ## See upcoming deliveries
152+ ### See upcoming deliveries
166153
167154``` python
168155deliveries = picnic.get_current_deliveries() # -> list[DeliverySummary]
@@ -175,10 +162,29 @@ delivery = picnic.get_delivery(deliveries[0].delivery_id) # -> Delivery
175162delivery.orders[0 ].items[0 ].items[0 ].name
176163```
177164
178- ## See available delivery slots
165+ ### See available delivery slots
179166
180167``` python
181168slots = picnic.get_delivery_slots() # -> DeliverySlots
182169slots.delivery_slots[0 ].window_start # '2025-04-29T17:15:00.000+02:00'
183170slots.selected_slot.slot_id
184171```
172+
173+ ## Migrating from 1.x to 2.0
174+
175+ - ` search() ` now returns a ` SearchResult ` (` .items ` is a list of ` SearchResultItem ` )
176+ instead of ` [{"items": [...]}] ` .
177+ - ` get_article() ` / ` get_article_by_gtin() ` now return an ` Article ` (or ` None ` )
178+ instead of a ` dict ` ; use ` .id ` / ` .name ` / ` .category ` instead of key access.
179+ - ` get_category_by_ids() ` now returns a ` Category ` instead of a ` dict ` .
180+ - Missing/unexpected PML nodes now raise ` PicnicParseError ` (from
181+ ` python_picnic_api2 ` ) instead of a bare ` KeyError ` .
182+ - The domain-JSON methods now return typed models instead of raw dicts:
183+ ` get_user() ` → ` User ` , ` get_cart() ` / ` add_product() ` / ` remove_product() ` /
184+ ` clear_cart() ` → ` Cart ` , ` get_delivery_slots() ` → ` DeliverySlots ` ,
185+ ` get_delivery() ` → ` Delivery ` , and ` get_deliveries() ` /
186+ ` get_current_deliveries() ` → ` list[DeliverySummary] ` . Use attribute access
187+ (` cart.items ` , ` user.contact_email ` ) instead of ` ["items"] ` / ` ["contact_email"] ` .
188+ - ` get_delivery_scenario() ` , ` get_delivery_position() ` and ` get_article_category() `
189+ still return raw dicts (see [ Typed models] ( #typed-models-2x ) ).
190+ - Any field you need that isn't modelled yet is available on ` model.raw ` .
0 commit comments