grocy-py Interactive Example¶
This notebook demonstrates all available API calls.
Prerequisites: Start the local Grocy instance with docker compose up -d
In [74]:
Copied!
from grocy import Grocy
from grocy.data_models.generic import EntityType
grocy = Grocy(base_url="http://localhost", api_key="test_local_devenv", port=9192)
from grocy import Grocy
from grocy.data_models.generic import EntityType
grocy = Grocy(base_url="http://localhost", api_key="test_local_devenv", port=9192)
Stock Management¶
In [75]:
Copied!
# Get all products currently in stock
for product in grocy.stock.current():
print(f"{product.id}: {product.name} - {product.available_amount} in stock")
# Get all products currently in stock
for product in grocy.stock.current():
print(f"{product.id}: {product.name} - {product.available_amount} in stock")
1: Cookies - 10.0 in stock 2: Chocolate - 13.0 in stock 3: Gummy bears - 5.0 in stock 4: Crisps - 5.0 in stock 5: Eggs - 6.0 in stock 6: Noodles - 5.0 in stock 7: Pickles - 5.0 in stock 8: Gulash soup - 5.0 in stock 9: Yogurt - 5.0 in stock 10: Cheese - 5.0 in stock 11: Cold cuts - 4.0 in stock 12: Paprika - 5.0 in stock 13: Cucumber - 5.0 in stock 14: Radish - 5.0 in stock 15: Tomato - 5.0 in stock 20: Minced meat - 1.0 in stock 21: Flour - 2000.0 in stock 22: Sugar - 2.0 in stock 23: Milk - 3.0 in stock 24: Milk Chocolate - 2.0 in stock 25: Dark Chocolate - 2.0 in stock 27: Ice Cream - 3.0 in stock 28: Soda - 12.0 in stock 29: Beer - 12.0 in stock
In [76]:
Copied!
# Get all products defined in the system (regardless of stock)
for product in grocy.stock.all_products():
print(f"{product.id}: {product.name}")
# Get all products defined in the system (regardless of stock)
for product in grocy.stock.all_products():
print(f"{product.id}: {product.name}")
1: Cookies 2: Chocolate 3: Gummy bears 4: Crisps 5: Eggs 6: Noodles 7: Pickles 8: Gulash soup 9: Yogurt 10: Cheese 11: Cold cuts 12: Paprika 13: Cucumber 14: Radish 15: Tomato 16: Pizza dough 17: Sieved tomatoes 18: Salami 19: Toast 20: Minced meat 21: Flour 22: Sugar 23: Milk 24: Milk Chocolate 25: Dark Chocolate 26: Waffle rolls 27: Ice Cream 28: Soda 29: Beer
In [77]:
Copied!
# Get a specific product by ID
product = grocy.stock.product(1)
if product:
print(f"{product.name}: {product.available_amount} (barcodes: {product.barcodes})")
# Get a specific product by ID
product = grocy.stock.product(1)
if product:
print(f"{product.name}: {product.available_amount} (barcodes: {product.barcodes})")
Cookies: 10.0 (barcodes: [])
In [78]:
Copied!
# Get due/overdue/expired/missing products
print("Due:")
for p in grocy.stock.due_products(get_details=True):
print(f" {p.name} - best before: {p.best_before_date}")
print("\nOverdue:")
for p in grocy.stock.overdue_products(get_details=True):
print(f" {p.name}")
print("\nExpired:")
for p in grocy.stock.expired_products(get_details=True):
print(f" {p.name}")
print("\nMissing:")
for p in grocy.stock.missing_products(get_details=True):
print(f" {p.name} - missing: {p.amount_missing}")
# Get due/overdue/expired/missing products
print("Due:")
for p in grocy.stock.due_products(get_details=True):
print(f" {p.name} - best before: {p.best_before_date}")
print("\nOverdue:")
for p in grocy.stock.overdue_products(get_details=True):
print(f" {p.name}")
print("\nExpired:")
for p in grocy.stock.expired_products(get_details=True):
print(f" {p.name}")
print("\nMissing:")
for p in grocy.stock.missing_products(get_details=True):
print(f" {p.name} - missing: {p.amount_missing}")
Due: Eggs - best before: None Paprika - best before: None Radish - best before: None Milk - best before: None Overdue: Cucumber Tomato Minced meat Expired: Minced meat Missing: Gummy bears - missing: 4.0 Crisps - missing: 5.0
In [79]:
Copied!
# Add product to stock
grocy.stock.add(product_id=1, amount=5, price=2.99)
# Add product to stock
grocy.stock.add(product_id=1, amount=5, price=2.99)
Out[79]:
[{'id': 95,
'product_id': 1,
'amount': 5,
'best_before_date': '2026-02-09',
'purchased_date': '2026-02-09',
'used_date': None,
'spoiled': 0,
'stock_id': '6989bf4e745e6',
'transaction_type': 'purchase',
'price': 2.99,
'undone': 0,
'undone_timestamp': None,
'opened_date': None,
'location_id': 4,
'recipe_id': None,
'correlation_id': None,
'transaction_id': '6989bf4e745e4',
'stock_row_id': None,
'shopping_location_id': None,
'user_id': 1,
'row_created_timestamp': '2026-02-09 11:04:46',
'note': None}]
In [80]:
Copied!
# Consume product from stock
grocy.stock.consume(product_id=1, amount=1)
# Consume product from stock
grocy.stock.consume(product_id=1, amount=1)
In [81]:
Copied!
# Open a product (mark as opened)
grocy.stock.open(product_id=1, amount=1)
# Open a product (mark as opened)
grocy.stock.open(product_id=1, amount=1)
In [82]:
Copied!
# Transfer product between locations
# grocy.stock.transfer(product_id=1, amount=1, location_from=1, location_to=2)
# Transfer product between locations
# grocy.stock.transfer(product_id=1, amount=1, location_from=1, location_to=2)
In [83]:
Copied!
# Set exact inventory amount for a product
result = grocy.stock.inventory(product_id=1, new_amount=10)
if result:
print(f"Inventory corrected: {result.name} now has {result.available_amount}")
# Set exact inventory amount for a product
result = grocy.stock.inventory(product_id=1, new_amount=10)
if result:
print(f"Inventory corrected: {result.name} now has {result.available_amount}")
Inventory corrected: Cookies now has 10.0
In [84]:
Copied!
# Get stock entries for a product
entries = grocy.stock.product_entries(product_id=1)
for entry in entries:
print(
f" Entry {entry.id}: amount={entry.amount}, best_before={entry.best_before_date}"
)
# Get stock entries for a product
entries = grocy.stock.product_entries(product_id=1)
for entry in entries:
print(
f" Entry {entry.id}: amount={entry.amount}, best_before={entry.best_before_date}"
)
Entry 83: amount=10.0, best_before=2026-08-07 00:00:00
In [85]:
Copied!
# Get stock locations for a product
locations = grocy.stock.product_locations(product_id=1)
for loc in locations:
print(f" Location {loc.location_id}: {loc.amount}")
# Get stock locations for a product
locations = grocy.stock.product_locations(product_id=1)
for loc in locations:
print(f" Location {loc.location_id}: {loc.amount}")
Location 4: 10.0
In [86]:
Copied!
# Get price history for a product
prices = grocy.stock.product_price_history(product_id=1)
for p in prices:
print(f" {p.date}: {p.price}")
# Get price history for a product
prices = grocy.stock.product_price_history(product_id=1)
for p in prices:
print(f" {p.date}: {p.price}")
2026-02-09 00:00:00: 2.99 2026-02-09 00:00:00: 2.99 2026-02-07 00:00:00: 0.7225
In [87]:
Copied!
# Get stock entries by location
locations = grocy.generic.list(EntityType.LOCATIONS)
if locations:
loc_id = locations[0]["id"]
entries = grocy.stock.entries_by_location(location_id=loc_id)
for entry in entries:
print(f" Product {entry.product_id}: {entry.amount}")
# Get stock entries by location
locations = grocy.generic.list(EntityType.LOCATIONS)
if locations:
loc_id = locations[0]["id"]
entries = grocy.stock.entries_by_location(location_id=loc_id)
for entry in entries:
print(f" Product {entry.product_id}: {entry.amount}")
Product 5: 1.0 Product 5: 1.0 Product 5: 1.0 Product 5: 1.0 Product 5: 1.0 Product 9: 1.0 Product 9: 1.0 Product 9: 1.0 Product 9: 1.0 Product 9: 1.0 Product 10: 1.0 Product 10: 1.0 Product 10: 1.0 Product 10: 1.0 Product 10: 1.0 Product 11: 1.0 Product 11: 1.0 Product 11: 1.0 Product 11: 1.0 Product 12: 1.0 Product 12: 1.0 Product 12: 1.0 Product 12: 1.0 Product 12: 1.0 Product 13: 1.0 Product 13: 1.0 Product 13: 1.0 Product 13: 1.0 Product 13: 1.0 Product 14: 1.0 Product 14: 1.0 Product 14: 1.0 Product 14: 1.0 Product 14: 1.0 Product 15: 1.0 Product 15: 1.0 Product 15: 1.0 Product 15: 1.0 Product 15: 1.0 Product 20: 1.0 Product 23: 1.0 Product 23: 1.0 Product 23: 1.0 Product 28: 12.0 Product 29: 12.0 Product 5: 1.0
In [88]:
Copied!
# Get product groups
groups = grocy.stock.product_groups()
for g in groups:
print(f"{g.id}: {g.name}")
# Get product groups
groups = grocy.stock.product_groups()
for g in groups:
print(f"{g.id}: {g.name}")
1: 01 Sweets 2: 02 Bakery products 3: 03 Tinned food 4: 04 Butchery products 5: 05 Vegetables/Fruits 6: 06 Refrigerated products 7: 07 Beverages
In [89]:
Copied!
# Barcode operations
# product = grocy.stock.product_by_barcode("1234567890")
# grocy.stock.add_by_barcode("1234567890", amount=1, price=1.0)
# grocy.stock.consume_by_barcode("1234567890", amount=1)
# grocy.stock.open_by_barcode("1234567890", amount=1)
# grocy.stock.transfer_by_barcode("1234567890", amount=1, location_from=1, location_to=2)
# grocy.stock.inventory_by_barcode("1234567890", new_amount=5)
# grocy.stock.barcode_lookup("1234567890")
# Barcode operations
# product = grocy.stock.product_by_barcode("1234567890")
# grocy.stock.add_by_barcode("1234567890", amount=1, price=1.0)
# grocy.stock.consume_by_barcode("1234567890", amount=1)
# grocy.stock.open_by_barcode("1234567890", amount=1)
# grocy.stock.transfer_by_barcode("1234567890", amount=1, location_from=1, location_to=2)
# grocy.stock.inventory_by_barcode("1234567890", new_amount=5)
# grocy.stock.barcode_lookup("1234567890")
In [90]:
Copied!
# Stock booking / transaction operations
# booking = grocy.stock.booking(booking_id=1)
# grocy.stock.undo_booking(booking_id=1)
# transactions = grocy.stock.transaction(transaction_id="abc")
# grocy.stock.undo_transaction(transaction_id="abc")
# Stock booking / transaction operations
# booking = grocy.stock.booking(booking_id=1)
# grocy.stock.undo_booking(booking_id=1)
# transactions = grocy.stock.transaction(transaction_id="abc")
# grocy.stock.undo_transaction(transaction_id="abc")
In [91]:
Copied!
# Merge two products (keeps first, removes second)
# grocy.stock.merge(product_id_keep=1, product_id_remove=2)
# Merge two products (keeps first, removes second)
# grocy.stock.merge(product_id_keep=1, product_id_remove=2)
Shopping Lists¶
In [92]:
Copied!
# Get shopping list items
items = grocy.shopping_list.items(get_details=True)
for item in items:
name = item.product.name if item.product else item.note
print(f"{name}: {item.amount} (done: {item.done})")
# Get shopping list items
items = grocy.shopping_list.items(get_details=True)
for item in items:
name = item.product.name if item.product else item.note
print(f"{name}: {item.amount} (done: {item.done})")
Some good snacks: 1.0 (done: False) Minced meat: 1.0 (done: False) Sieved tomatoes: 1.0 (done: False) Gummy bears: 4.0 (done: False) Crisps: 5.0 (done: False) Cookies: 2.0 (done: False)
In [93]:
Copied!
# Add a product to the shopping list
grocy.shopping_list.add_product(product_id=1, amount=3)
# Add a product to the shopping list
grocy.shopping_list.add_product(product_id=1, amount=3)
In [94]:
Copied!
# Remove a product from the shopping list
grocy.shopping_list.remove_product(product_id=1, amount=1)
# Remove a product from the shopping list
grocy.shopping_list.remove_product(product_id=1, amount=1)
In [95]:
Copied!
# Add missing / overdue / expired products to shopping list
grocy.shopping_list.add_missing_products()
# grocy.shopping_list.add_overdue_products()
# grocy.shopping_list.add_expired_products()
# Add missing / overdue / expired products to shopping list
grocy.shopping_list.add_missing_products()
# grocy.shopping_list.add_overdue_products()
# grocy.shopping_list.add_expired_products()
In [96]:
Copied!
# Clear the shopping list
# grocy.shopping_list.clear()
# Clear the shopping list
# grocy.shopping_list.clear()
Chores¶
In [97]:
Copied!
# List all chores
chores = grocy.chores.list(get_details=True)
for chore in chores:
print(f"{chore.id}: {chore.name} - next: {chore.next_estimated_execution_time}")
# List all chores
chores = grocy.chores.list(get_details=True)
for chore in chores:
print(f"{chore.id}: {chore.name} - next: {chore.next_estimated_execution_time}")
1: Change towels in the bathroom - next: 2026-02-12 23:59:59 2: Mop the kitchen floor - next: 2026-02-02 23:59:59 3: Take out the trash - next: 2026-02-08 23:59:59 4: Vacuum the living room floor - next: 2026-02-14 23:59:59 5: Clean the litter box - next: 2026-02-08 23:59:59 6: Change the bed sheets - next: 2026-02-18 23:59:59
In [98]:
Copied!
# Get specific chore details
chore = grocy.chores.get(chore_id=1)
print(f"{chore.name}: period={chore.period_type}, track_count={chore.track_count}")
# Get specific chore details
chore = grocy.chores.get(chore_id=1)
print(f"{chore.name}: period={chore.period_type}, track_count={chore.track_count}")
Change towels in the bathroom: period=PeriodType.HOURLY, track_count=0
In [99]:
Copied!
# Execute a chore
grocy.chores.execute(chore_id=1, done_by=1)
# Execute a chore
grocy.chores.execute(chore_id=1, done_by=1)
Out[99]:
{'id': 154,
'chore_id': 1,
'tracked_time': '2026-02-09 00:00:00',
'done_by_user_id': 1,
'row_created_timestamp': '2026-02-09 11:04:47',
'undone': 0,
'undone_timestamp': None,
'skipped': 0,
'scheduled_execution_time': '2026-02-12 23:59:59'}
In [100]:
Copied!
# Undo a chore execution
# grocy.chores.undo(execution_id=1)
# Undo a chore execution
# grocy.chores.undo(execution_id=1)
In [101]:
Copied!
# Calculate next chore assignments
grocy.chores.calculate_next_assignments()
# Calculate next chore assignments
grocy.chores.calculate_next_assignments()
In [102]:
Copied!
# Merge two chores (keeps first, removes second)
# grocy.chores.merge(chore_id_keep=1, chore_id_remove=2)
# Merge two chores (keeps first, removes second)
# grocy.chores.merge(chore_id_keep=1, chore_id_remove=2)
Tasks¶
In [103]:
Copied!
# List all tasks
tasks = grocy.tasks.list()
for task in tasks:
print(f"{task.id}: {task.name} - due: {task.due_date} (done: {task.done})")
# List all tasks
tasks = grocy.tasks.list()
for task in tasks:
print(f"{task.id}: {task.name} - due: {task.due_date} (done: {task.done})")
1: Task1 - due: None (done: 0) 2: Task2 - due: 2026-02-07 00:00:00 (done: 0) 3: Task3 - due: 2026-02-08 00:00:00 (done: 0) 4: Task4 - due: 2026-02-12 00:00:00 (done: 0) 5: Task5 - due: 2026-02-28 00:00:00 (done: 0)
In [104]:
Copied!
# Get a specific task
task = grocy.tasks.get(task_id=1)
print(f"{task.name}: {task.description}")
# Get a specific task
task = grocy.tasks.get(task_id=1)
print(f"{task.name}: {task.description}")
Task1: None
In [105]:
Copied!
# Complete a task
# grocy.tasks.complete(task_id=1)
# Complete a task
# grocy.tasks.complete(task_id=1)
In [106]:
Copied!
# Undo task completion
# grocy.tasks.undo(task_id=1)
# Undo task completion
# grocy.tasks.undo(task_id=1)
Batteries¶
In [107]:
Copied!
# List all batteries
batteries = grocy.batteries.list(get_details=True)
for battery in batteries:
print(f"{battery.id}: {battery.name} - last charged: {battery.last_charged}")
# List all batteries
batteries = grocy.batteries.list(get_details=True)
for battery in batteries:
print(f"{battery.id}: {battery.name} - last charged: {battery.last_charged}")
1: Battery1 - last charged: 2026-02-09 12:03:02 2: Battery2 - last charged: 2025-12-20 13:45:20 3: Battery3 - last charged: 2025-12-05 13:45:20 4: Battery4 - last charged: 2025-12-14 13:45:20
In [108]:
Copied!
# Get a specific battery
battery = grocy.batteries.get(battery_id=1)
if battery:
print(
f"{battery.name}: cycles={battery.charge_cycles_count}, interval={battery.charge_interval_days} days"
)
# Get a specific battery
battery = grocy.batteries.get(battery_id=1)
if battery:
print(
f"{battery.name}: cycles={battery.charge_cycles_count}, interval={battery.charge_interval_days} days"
)
Battery1: cycles=5, interval=180 days
In [109]:
Copied!
# Charge a battery
grocy.batteries.charge(battery_id=1)
# Charge a battery
grocy.batteries.charge(battery_id=1)
Out[109]:
{'id': 12,
'battery_id': 1,
'tracked_time': '2026-02-09 12:04:47',
'undone': 0,
'undone_timestamp': None,
'row_created_timestamp': '2026-02-09 11:04:47'}
In [110]:
Copied!
# Undo battery charge
# grocy.batteries.undo(charge_cycle_id=1)
# Undo battery charge
# grocy.batteries.undo(charge_cycle_id=1)
Equipment¶
In [111]:
Copied!
# List all equipment
equip = grocy.equipment.list(get_details=True)
for e in equip:
print(f"{e.id}: {e.name} - {e.description}")
# List all equipment
equip = grocy.equipment.list(get_details=True)
for e in equip:
print(f"{e.id}: {e.name} - {e.description}")
1: Coffee machine - <h1>Lorem ipsum</h1><p>Lorem ipsum <b>dolor sit</b> amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur <span style="background-color: rgb(255, 255, 0);">sadipscing elitr</span>, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.</p><ul><li>At vero eos et accusam et justo duo dolores et ea rebum.</li><li>Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.</li></ul><h1>Lorem ipsum</h1><p>Lorem ipsum <b>dolor sit</b> amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur <span style="background-color: rgb(255, 255, 0);">sadipscing elitr</span>, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.</p> 2: Dishwasher - <h1>Lorem ipsum</h1><p>Lorem ipsum <b>dolor sit</b> amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur <span style="background-color: rgb(255, 255, 0);">sadipscing elitr</span>, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.</p><ul><li>At vero eos et accusam et justo duo dolores et ea rebum.</li><li>Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.</li></ul><h1>Lorem ipsum</h1><p>Lorem ipsum <b>dolor sit</b> amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur <span style="background-color: rgb(255, 255, 0);">sadipscing elitr</span>, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.</p>
In [112]:
Copied!
# Get specific equipment by ID
e = grocy.equipment.get(equipment_id=1)
if e:
print(f"{e.name}: {e.description}")
# Get specific equipment by ID
e = grocy.equipment.get(equipment_id=1)
if e:
print(f"{e.name}: {e.description}")
Coffee machine: <h1>Lorem ipsum</h1><p>Lorem ipsum <b>dolor sit</b> amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur <span style="background-color: rgb(255, 255, 0);">sadipscing elitr</span>, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.</p><ul><li>At vero eos et accusam et justo duo dolores et ea rebum.</li><li>Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.</li></ul><h1>Lorem ipsum</h1><p>Lorem ipsum <b>dolor sit</b> amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur <span style="background-color: rgb(255, 255, 0);">sadipscing elitr</span>, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.</p>
In [113]:
Copied!
# Get equipment by name
# e = grocy.equipment.get_by_name("Coffee Machine")
# if e:
# print(f"Found: {e.name} (id={e.id})")
# Get equipment by name
# e = grocy.equipment.get_by_name("Coffee Machine")
# if e:
# print(f"Found: {e.name} (id={e.id})")
In [114]:
Copied!
# Get all equipment objects with full details
all_equip = grocy.equipment.get_all_objects()
for e in all_equip:
print(f"{e.id}: {e.name}")
# Get all equipment objects with full details
all_equip = grocy.equipment.get_all_objects()
for e in all_equip:
print(f"{e.id}: {e.name}")
1: Coffee machine 2: Dishwasher
Meal Plans¶
In [115]:
Copied!
# Get meal plan items
meals = grocy.meal_plan.items(get_details=True)
for meal in meals:
name = meal.recipe.name if meal.recipe else meal.note
print(f"{meal.day}: {name} (type: {meal.type})")
# Get meal plan items
meals = grocy.meal_plan.items(get_details=True)
for meal in meals:
name = meal.recipe.name if meal.recipe else meal.note
print(f"{meal.day}: {name} (type: {meal.type})")
2026-02-02: Pizza (type: MealPlanItemType.RECIPE) 2026-02-03: Spaghetti bolognese (type: MealPlanItemType.RECIPE) 2026-02-04: Sandwiches (type: MealPlanItemType.RECIPE) 2026-02-05: Pancakes (type: MealPlanItemType.RECIPE) 2026-02-06: Spaghetti bolognese (type: MealPlanItemType.RECIPE) 2026-02-07: Pizza (type: MealPlanItemType.RECIPE) 2026-02-08: Pancakes (type: MealPlanItemType.RECIPE) 2026-02-03: This is a note (type: MealPlanItemType.NOTE) 2026-02-01: None (type: MealPlanItemType.PRODUCT) 2026-02-02: None (type: MealPlanItemType.PRODUCT) 2026-02-04: None (type: MealPlanItemType.PRODUCT) 2026-02-07: Some good snacks (type: MealPlanItemType.NOTE)
In [116]:
Copied!
# Get meal plan sections
sections = grocy.meal_plan.sections()
for s in sections:
print(f"{s.id}: {s.name} (sort: {s.sort_number})")
# Get meal plan sections
sections = grocy.meal_plan.sections()
for s in sections:
print(f"{s.id}: {s.name} (sort: {s.sort_number})")
-1: None (sort: -1) 1: Breakfast (sort: 10) 2: Lunch (sort: 20) 3: Dinner (sort: 30)
In [117]:
Copied!
# Get a specific meal plan section
# section = grocy.meal_plan.section(section_id=1)
# if section:
# print(f"{section.name}")
# Get a specific meal plan section
# section = grocy.meal_plan.section(section_id=1)
# if section:
# print(f"{section.name}")
Recipes¶
In [118]:
Copied!
# Get a specific recipe
recipe = grocy.recipes.get(recipe_id=1)
if recipe:
print(f"{recipe.name}: {recipe.base_servings} servings")
if recipe.picture_file_name:
print(f" Picture URL: {recipe.get_picture_url_path()}")
# Get a specific recipe
recipe = grocy.recipes.get(recipe_id=1)
if recipe:
print(f"{recipe.name}: {recipe.base_servings} servings")
if recipe.picture_file_name:
print(f" Picture URL: {recipe.get_picture_url_path()}")
Pizza: 1 servings Picture URL: files/recipepictures/cGl6emEuanBn?force_serve_as=picture&best_fit_width=400
In [119]:
Copied!
# Get recipe fulfillment status
fulfillment = grocy.recipes.fulfillment(recipe_id=1)
if fulfillment:
print(
f"Recipe {fulfillment.recipe_id}: fulfilled={fulfillment.need_fulfilled}, missing={fulfillment.missing_products_count}"
)
# Get recipe fulfillment status
fulfillment = grocy.recipes.fulfillment(recipe_id=1)
if fulfillment:
print(
f"Recipe {fulfillment.recipe_id}: fulfilled={fulfillment.need_fulfilled}, missing={fulfillment.missing_products_count}"
)
Recipe 1: fulfilled=False, missing=3
In [120]:
Copied!
# Get all recipes' fulfillment status
all_fulfillment = grocy.recipes.all_fulfillment()
for f in all_fulfillment:
print(
f"Recipe {f.recipe_id}: fulfilled={f.need_fulfilled}, missing={f.missing_products_count}"
)
# Get all recipes' fulfillment status
all_fulfillment = grocy.recipes.all_fulfillment()
for f in all_fulfillment:
print(
f"Recipe {f.recipe_id}: fulfilled={f.need_fulfilled}, missing={f.missing_products_count}"
)
Recipe -96: fulfilled=False, missing=6 Recipe -95: fulfilled=False, missing=3 Recipe -87: fulfilled=True, missing=0 Recipe -81: fulfilled=False, missing=3 Recipe -76: fulfilled=True, missing=0 Recipe -75: fulfilled=True, missing=0 Recipe -69: fulfilled=False, missing=1 Recipe -62: fulfilled=True, missing=0 Recipe -60: fulfilled=True, missing=0 Recipe -53: fulfilled=False, missing=3 Recipe -44: fulfilled=False, missing=1 Recipe -42: fulfilled=False, missing=1 Recipe -35: fulfilled=True, missing=0 Recipe -33: fulfilled=True, missing=0 Recipe -26: fulfilled=True, missing=0 Recipe -17: fulfilled=False, missing=1 Recipe -8: fulfilled=False, missing=3 Recipe 1: fulfilled=False, missing=3 Recipe 2: fulfilled=False, missing=1 Recipe 3: fulfilled=True, missing=0 Recipe 4: fulfilled=True, missing=0 Recipe 5: fulfilled=True, missing=0 Recipe 6: fulfilled=True, missing=0
In [121]:
Copied!
# Add missing recipe ingredients to shopping list
# grocy.recipes.add_not_fulfilled_to_shopping_list(recipe_id=1)
# Add missing recipe ingredients to shopping list
# grocy.recipes.add_not_fulfilled_to_shopping_list(recipe_id=1)
In [122]:
Copied!
# Consume recipe ingredients from stock
# grocy.recipes.consume(recipe_id=1)
# Consume recipe ingredients from stock
# grocy.recipes.consume(recipe_id=1)
In [123]:
Copied!
# Copy a recipe
# grocy.recipes.copy(recipe_id=1)
# Copy a recipe
# grocy.recipes.copy(recipe_id=1)
Users¶
In [124]:
Copied!
# List all users
users = grocy.users.list()
for user in users:
print(f"{user.id}: {user.username} ({user.display_name})")
# List all users
users = grocy.users.list()
for user in users:
print(f"{user.id}: {user.username} ({user.display_name})")
1: Demo User (Demo User) 2: Demo User 2 (Demo User 2) 3: Demo User 3 (Demo User 3) 4: Demo User 4 (Demo User 4)
In [125]:
Copied!
# Get current logged-in user
me = grocy.users.current()
if me:
print(f"Logged in as: {me.username} ({me.display_name})")
# Get current logged-in user
me = grocy.users.current()
if me:
print(f"Logged in as: {me.username} ({me.display_name})")
Logged in as: Demo User (Demo User)
In [126]:
Copied!
# Get a specific user
user = grocy.users.get(user_id=1)
if user:
print(f"{user.username}: {user.first_name} {user.last_name}")
# Get a specific user
user = grocy.users.get(user_id=1)
if user:
print(f"{user.username}: {user.first_name} {user.last_name}")
Demo User: None None
In [127]:
Copied!
# User settings
settings = grocy.users.settings()
print(settings)
# User settings
settings = grocy.users.settings()
print(settings)
{'night_mode': 'follow-system', 'auto_night_mode_enabled': False, 'auto_night_mode_time_range_from': '20:00', 'auto_night_mode_time_range_to': '07:00', 'auto_night_mode_time_range_goes_over_midnight': True, 'night_mode_enabled_internal': '1', 'auto_reload_on_db_change': False, 'show_clock_in_header': False, 'keep_screen_on': False, 'keep_screen_on_when_fullscreen_card': False, 'product_presets_location_id': '3', 'product_presets_product_group_id': -1, 'product_presets_qu_id': '3', 'product_presets_default_due_days': 0, 'product_presets_treat_opened_as_out_of_stock': True, 'product_presets_default_stock_label_type': 0, 'stock_decimal_places_amounts': 4, 'stock_decimal_places_prices_input': 2, 'stock_decimal_places_prices_display': 2, 'stock_auto_decimal_separator_prices': False, 'stock_due_soon_days': 5, 'stock_default_purchase_amount': 0, 'stock_default_consume_amount': 1, 'stock_default_consume_amount_use_quick_consume_amount': False, 'scan_mode_consume_enabled': False, 'scan_mode_purchase_enabled': False, 'show_icon_on_stock_overview_page_when_product_is_on_shopping_list': True, 'stock_overview_show_all_out_of_stock_products': False, 'show_purchased_date_on_purchase': False, 'show_warning_on_purchase_when_due_date_is_earlier_than_next': True, 'shopping_list_to_stock_workflow_auto_submit_when_prefilled': False, 'shopping_list_show_calendar': False, 'shopping_list_round_up': False, 'shopping_list_auto_add_below_min_stock_amount': False, 'shopping_list_auto_add_below_min_stock_amount_list_id': 1, 'shopping_list_print_show_header': True, 'shopping_list_print_group_by_product_group': True, 'shopping_list_print_layout_type': 'table', 'recipe_ingredients_group_by_product_group': False, 'recipes_show_list_side_by_side': True, 'recipes_show_ingredient_checkbox': False, 'chores_due_soon_days': 5, 'chores_overview_swap_tracking_buttons': False, 'batteries_due_soon_days': 5, 'tasks_due_soon_days': 5, 'calendar_color_products': '#007bff', 'calendar_color_tasks': '#28a745', 'calendar_color_chores': '#ffc107', 'calendar_color_batteries': '#17a2b8', 'calendar_color_meal_plan': '#6c757d', 'datatables_state_apikeys-table': '{"time":1770584163946,"start":0,"length":10,"order":[[6,"desc"]],"search":{"search":"","smart":true,"regex":false,"caseInsensitive":true},"columns":[{"visible":true,"search":{"search":"","smart":true,"regex":false,"caseInsensitive":true}},{"visible":true,"search":{"search":"","smart":true,"regex":false,"caseInsensitive":true}},{"visible":true,"search":{"search":"","smart":true,"regex":false,"caseInsensitive":true}},{"visible":true,"search":{"search":"","smart":true,"regex":false,"caseInsensitive":true}},{"visible":true,"search":{"search":"","smart":true,"regex":false,"caseInsensitive":true}},{"visible":true,"search":{"search":"","smart":true,"regex":false,"caseInsensitive":true}},{"visible":true,"search":{"search":"","smart":true,"regex":false,"caseInsensitive":true}},{"visible":true,"search":{"search":"","smart":true,"regex":false,"caseInsensitive":true}}],"ColReorder":[0,1,2,3,4,5,6,7],"select":{"rows":[],"columns":[],"cells":[]}}', 'datatables_state_stock-overview-table': '{"time":1770584142748,"start":0,"length":10,"order":[[5,"asc"]],"search":{"search":"","smart":true,"regex":false,"caseInsensitive":true},"columns":[{"visible":true,"search":{"search":"","smart":true,"regex":false,"caseInsensitive":true}},{"visible":true,"search":{"search":"","smart":true,"regex":false,"caseInsensitive":true}},{"visible":false,"search":{"search":"","smart":true,"regex":false,"caseInsensitive":true}},{"visible":true,"search":{"search":"","smart":true,"regex":false,"caseInsensitive":true}},{"visible":false,"search":{"search":"","smart":true,"regex":false,"caseInsensitive":true}},{"visible":true,"search":{"search":"","smart":true,"regex":false,"caseInsensitive":true}},{"visible":false,"search":{"search":"","smart":true,"regex":false,"caseInsensitive":true}},{"visible":false,"search":{"search":"","smart":true,"regex":false,"caseInsensitive":true}},{"visible":false,"search":{"search":"","smart":true,"regex":false,"caseInsensitive":true}},{"visible":false,"search":{"search":"","smart":true,"regex":false,"caseInsensitive":true}},{"visible":false,"search":{"search":"","smart":true,"regex":false,"caseInsensitive":true}},{"visible":false,"search":{"search":"","smart":true,"regex":false,"caseInsensitive":true}},{"visible":false,"search":{"search":"","smart":true,"regex":false,"caseInsensitive":true}},{"visible":false,"search":{"search":"","smart":true,"regex":false,"caseInsensitive":true}},{"visible":false,"search":{"search":"","smart":true,"regex":false,"caseInsensitive":true}},{"visible":false,"search":{"search":"","smart":true,"regex":false,"caseInsensitive":true}},{"visible":false,"search":{"search":"","smart":true,"regex":false,"caseInsensitive":true}},{"visible":false,"search":{"search":"","smart":true,"regex":false,"caseInsensitive":true}},{"visible":false,"search":{"search":"","smart":true,"regex":false,"caseInsensitive":true}},{"visible":false,"search":{"search":"","smart":true,"regex":false,"caseInsensitive":true}}],"ColReorder":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19],"select":{"rows":[],"columns":[],"cells":[]}}'}
In [128]:
Copied!
# Get / set / delete a user setting
# grocy.users.get_setting("some_key")
# grocy.users.set_setting("some_key", "some_value")
# grocy.users.delete_setting("some_key")
# Get / set / delete a user setting
# grocy.users.get_setting("some_key")
# grocy.users.set_setting("some_key", "some_value")
# grocy.users.delete_setting("some_key")
In [129]:
Copied!
# Create / edit / delete user
# grocy.users.create({"username": "newuser", "password": "secret", "first_name": "New", "last_name": "User"})
# grocy.users.edit(user_id=2, data={"first_name": "Updated"})
# grocy.users.delete(user_id=2)
# Create / edit / delete user
# grocy.users.create({"username": "newuser", "password": "secret", "first_name": "New", "last_name": "User"})
# grocy.users.edit(user_id=2, data={"first_name": "Updated"})
# grocy.users.delete(user_id=2)
System¶
In [130]:
Copied!
# Get system info
info = grocy.system.info()
if info:
print(f"Grocy {info.grocy_version} (released {info.grocy_release_date})")
print(f"PHP {info.php_version}, SQLite {info.sqlite_version}")
print(f"OS: {info.os}")
# Get system info
info = grocy.system.info()
if info:
print(f"Grocy {info.grocy_version} (released {info.grocy_release_date})")
print(f"PHP {info.php_version}, SQLite {info.sqlite_version}")
print(f"OS: {info.os}")
Grocy 4.5.0 (released 2025-03-28) PHP 8.3.19, SQLite 3.48.0 OS: Linux 6.18.7-zen1-1-zen #1 ZEN SMP PREEMPT_DYNAMIC Sat, 24 Jan 2026 00:47:26 +0000 x86_64
In [131]:
Copied!
# Get system time
time = grocy.system.time()
if time:
print(f"Timezone: {time.timezone}")
print(f"Local: {time.time_local}")
print(f"UTC: {time.time_utc}")
# Get system time
time = grocy.system.time()
if time:
print(f"Timezone: {time.timezone}")
print(f"Local: {time.time_local}")
print(f"UTC: {time.time_utc}")
Timezone: UTC Local: 2026-02-09 11:04:48 UTC: 2026-02-09 11:04:48
In [132]:
Copied!
# Get system config
config = grocy.system.config()
if config:
print(f"Mode: {config.mode}")
print(f"Currency: {config.currency}")
print(f"Locale: {config.locale}")
print(f"Enabled features: {config.enabled_features}")
# Get system config
config = grocy.system.config()
if config:
print(f"Mode: {config.mode}")
print(f"Currency: {config.currency}")
print(f"Locale: {config.locale}")
print(f"Enabled features: {config.enabled_features}")
Mode: demo Currency: USD Locale: en Enabled features: ['FEATURE_FLAG_STOCK', 'FEATURE_FLAG_SHOPPINGLIST', 'FEATURE_FLAG_RECIPES', 'FEATURE_FLAG_CHORES', 'FEATURE_FLAG_TASKS', 'FEATURE_FLAG_BATTERIES', 'FEATURE_FLAG_EQUIPMENT', 'FEATURE_FLAG_CALENDAR', 'FEATURE_FLAG_STOCK_PRICE_TRACKING', 'FEATURE_FLAG_STOCK_LOCATION_TRACKING', 'FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING', 'FEATURE_FLAG_STOCK_PRODUCT_OPENED_TRACKING', 'FEATURE_FLAG_STOCK_PRODUCT_FREEZING', 'FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_FIELD_NUMBER_PAD', 'FEATURE_FLAG_SHOPPINGLIST_MULTIPLE_LISTS', 'FEATURE_FLAG_RECIPES_MEALPLAN', 'FEATURE_FLAG_CHORES_ASSIGNMENTS', 'FEATURE_FLAG_AUTO_TORCH_ON_WITH_CAMERA']
In [133]:
Copied!
# Get last database change time
db_time = grocy.system.db_changed_time()
print(f"DB last changed: {db_time}")
# Get last database change time
db_time = grocy.system.db_changed_time()
print(f"DB last changed: {db_time}")
DB last changed: 2026-02-09 11:04:47
Calendar¶
In [134]:
Copied!
# Get calendar in iCalendar format
ical = grocy.calendar.ical()
if ical:
# Print first 500 chars
print(ical[:500])
# Get calendar in iCalendar format
ical = grocy.calendar.ical()
if ical:
# Print first 500 chars
print(ical[:500])
BEGIN:VCALENDAR PRODID:Grocy VERSION:2.0 CALSCALE:GREGORIAN BEGIN:VEVENT UID:d426c718e6233baecb1033e000612494 DTSTAMP:20260209T110448Z SUMMARY:Product due: Cookies DESCRIPTION: DTSTART;VALUE=DATE:20260807 END:VEVENT BEGIN:VEVENT UID:3e9318f3de6b14195b09545b27757fa1 DTSTAMP:20260209T110448Z SUMMARY:Product due: Chocolate DESCRIPTION: DTSTART;VALUE=DATE:20260807 END:VEVENT BEGIN:VEVENT UID:a73810c087e138a44b821121bd5f6dcf DTSTAMP:20260209T110448Z SUMMARY:Product due: Gummy bea
In [135]:
Copied!
# Get calendar sharing link
link = grocy.calendar.sharing_link()
print(f"Sharing link: {link}")
# Get calendar sharing link
link = grocy.calendar.sharing_link()
print(f"Sharing link: {link}")
Sharing link: http://localhost:9192/api/calendar/ical?secret=0pXSK93WCfXcd6Zkyl5aYeo9jxibtNybaUGYiUIRkO7sVJPkGl
Files¶
In [136]:
Copied!
# Upload / download / delete files
# File groups: "productpictures", "recipepictures", "equipmentmanuals", etc.
# grocy.files.upload(group="productpictures", file_name="photo.jpg", data=open("photo.jpg", "rb"))
# data = grocy.files.download(group="productpictures", file_name="photo.jpg")
# grocy.files.delete(group="productpictures", file_name="photo.jpg")
# Upload / download / delete files
# File groups: "productpictures", "recipepictures", "equipmentmanuals", etc.
# grocy.files.upload(group="productpictures", file_name="photo.jpg", data=open("photo.jpg", "rb"))
# data = grocy.files.download(group="productpictures", file_name="photo.jpg")
# grocy.files.delete(group="productpictures", file_name="photo.jpg")
In [137]:
Copied!
# Upload a product picture (convenience method)
# grocy.stock.upload_product_picture(product_id=1, pic_path="/path/to/photo.jpg")
# Upload a product picture (convenience method)
# grocy.stock.upload_product_picture(product_id=1, pic_path="/path/to/photo.jpg")
In [138]:
Copied!
# List all available entity types
for et in EntityType:
print(f'{et.name} = "{et.value}"')
# List all available entity types
for et in EntityType:
print(f'{et.name} = "{et.value}"')
PRODUCTS = "products" CHORES = "chores" PRODUCT_BARCODES = "product_barcodes" BATTERIES = "batteries" LOCATIONS = "locations" QUANTITY_UNITS = "quantity_units" QUANTITY_UNIT_CONVERSIONS = "quantity_unit_conversions" SHOPPING_LIST = "shopping_list" SHOPPING_LISTS = "shopping_lists" SHOPPING_LOCATIONS = "shopping_locations" RECIPES = "recipes" RECIPES_POS = "recipes_pos" RECIPES_NESTINGS = "recipes_nestings" TASKS = "tasks" TASK_CATEGORIES = "task_categories" PRODUCT_GROUPS = "product_groups" EQUIPMENT = "equipment" USER_FIELDS = "userfields" USER_ENTITIES = "userentities" USER_OBJECTS = "userobjects" MEAL_PLAN = "meal_plan" MEAL_PLAN_SECTIONS = "meal_plan_sections"
In [139]:
Copied!
# List all objects of a type
locations = grocy.generic.list(EntityType.LOCATIONS)
for loc in locations:
print(f"{loc['id']}: {loc['name']}")
# List all objects of a type
locations = grocy.generic.list(EntityType.LOCATIONS)
for loc in locations:
print(f"{loc['id']}: {loc['name']}")
2: Fridge 3: Pantry 4: Candy cupboard 5: Tinned food cupboard 6: Freezer
In [140]:
Copied!
# Get a single object
locations = grocy.generic.list(EntityType.LOCATIONS)
if locations:
loc = grocy.generic.get(EntityType.LOCATIONS, object_id=locations[0]["id"])
print(loc)
# Get a single object
locations = grocy.generic.list(EntityType.LOCATIONS)
if locations:
loc = grocy.generic.get(EntityType.LOCATIONS, object_id=locations[0]["id"])
print(loc)
{'id': 2, 'name': 'Fridge', 'description': None, 'row_created_timestamp': '2026-02-08 13:45:00', 'is_freezer': 0, 'active': 1, 'userfields': None}
In [141]:
Copied!
# Create a new object
# result = grocy.generic.create(EntityType.LOCATIONS, {"name": "Pantry", "description": "Kitchen pantry"})
# print(result)
# Create a new object
# result = grocy.generic.create(EntityType.LOCATIONS, {"name": "Pantry", "description": "Kitchen pantry"})
# print(result)
In [142]:
Copied!
# Update an object
# grocy.generic.update(EntityType.LOCATIONS, object_id=1, data={"name": "Updated Name"})
# Update an object
# grocy.generic.update(EntityType.LOCATIONS, object_id=1, data={"name": "Updated Name"})
In [143]:
Copied!
# Delete an object
# grocy.generic.delete(EntityType.LOCATIONS, object_id=1)
# Delete an object
# grocy.generic.delete(EntityType.LOCATIONS, object_id=1)
In [144]:
Copied!
# List with query filters
# Grocy supports query filters like: "name=", "id<10", etc.
# products = grocy.generic.list(EntityType.PRODUCTS, query_filters=["name=Cookies"])
# print(products)
# List with query filters
# Grocy supports query filters like: "name=", "id<10", etc.
# products = grocy.generic.list(EntityType.PRODUCTS, query_filters=["name=Cookies"])
# print(products)
In [145]:
Copied!
# Userfields (custom fields on any entity)
# fields = grocy.generic.get_userfields("products", object_id=1)
# print(fields)
# grocy.generic.set_userfields("products", object_id=1, key="my_field", value="my_value")
# Userfields (custom fields on any entity)
# fields = grocy.generic.get_userfields("products", object_id=1)
# print(fields)
# grocy.generic.set_userfields("products", object_id=1, key="my_field", value="my_value")
Error Handling¶
In [146]:
Copied!
from grocy.errors import GrocyError
try:
grocy.stock.product(product_id=99999)
except GrocyError as e:
print(f"HTTP {e.status_code}: {e.message}")
print(f"Client error: {e.is_client_error}")
print(f"Server error: {e.is_server_error}")
from grocy.errors import GrocyError
try:
grocy.stock.product(product_id=99999)
except GrocyError as e:
print(f"HTTP {e.status_code}: {e.message}")
print(f"Client error: {e.is_client_error}")
print(f"Server error: {e.is_server_error}")
HTTP 400: Product does not exist or is inactive Client error: True Server error: False