nemlai
CLI and Python SDK for automated grocery replenishment on Nemlig.com — Denmark's largest online grocery delivery service.
NemlAI predicts what groceries you'll need next based on your order history, then builds ready-to-checkout basket links. Especially useful for OpenClaw and other AI agents that interact with the NemlAI platform.
Platform: nemlai.milops.org
Install
pip install nemlai
On Arch Linux:
yay -S nemlai
On Ubuntu:
sudo add-apt-repository ppa:milops/nemlai
sudo apt install nemlai
Quick start
# Log in to your NemlAI instance
nemlai login
# Interactive mode
nemlai
# Run a single command
nemlai predict
nemlai search "mælk"
nemlai basket
Python SDK
from nemlai import NemlAI
client = NemlAI.login("you@example.com", "password") # or NemlAI(token="...")
basket = client.basket.generate(size="medium") # predicts, polls, returns JSON
for item in client.basket.items()["items"]:
print(item["name"], item["quantity"], item["explanation"])
client.basket.adjust("5001234", 2) # by product_number
client.basket.compare() # price the basket at every enabled shop
client.basket.send() # returns basket_url
Basket methods return JSON by default since 0.10.0 (remove/adjust take a product_number). Pass structured=False for the old text replies.
| Resource | Methods |
|---|---|
client.basket |
generate, items, add, add_batch, remove, adjust, send, search, categories, why, dish, status; other shops: shops, shop_prices, alternatives, choose, unchoose, choices, send_to_shop, compare |
client.exclusions |
list, items(order_id), exclude_order, include_order, hide_lines(order_id, product), unhide_lines, set |
client.shops |
status, link(email, password, shops=["bilka", "foetex"]), refresh(shop_id) |
client.settings |
show, update(order_cutoff_date=..., auto_learn_from_purchases=...) |
client.preferences |
get, update(prefer_eco=True, exclude_products=[...]), recalculate |
client.orders |
refresh(password), fetch_status |
client.household |
info, members |
client.analytics |
see below |
client |
whoami, export_data, feedback, command(text) |
The basket at other shops
The same basket priced at Bilka, Netto, Føtex and REMA 1000 (the shop tabs on /kurv). Where a shop has no validated match you get a suggestion (status: "suggestion", with differs), and you can pick another product for this basket only:
prices = client.basket.shop_prices() # every shop tab, current basket
for shop in prices["shops"]:
print(shop["name"], shop["total"], shop["counts"])
milk = client.basket.items()["items"][0]
alts = client.basket.alternatives("netto", canonical_id=milk["canonical_id"], quantity=milk["quantity"])
client.basket.choose(milk["product_number"], "netto", alts["items"][0]["product_number"])
client.basket.shop_prices(shops=["netto"]) # the pick comes back as status "chosen"
client.basket.send_to_shop("bilkatogo") # your own BilkaToGo cart; never checks out
Picks are never stored on the server and are dropped by generate(). send_to_shop only works for shops whose send_mode is basket_push in client.basket.shops() (today BilkaToGo, after nemlai shops link).
nemlai basket prices # totals per shop
nemlai basket prices --shop netto # every line at Netto
nemlai basket alternatives 3 --shop netto # or --search "rugbrød"
nemlai basket choose 3 --shop netto --product 123456
nemlai basket send-shop bilkatogo
Picks made in the CLI are kept in ~/.config/nemlai/basket_picks.json until the next basket generate.
Exclusions and other shops
Leave orders or single products out of predictions and analytics, and link Bilka/Føtex so their receipts count too:
nemlai exclusions list
nemlai exclusions items 4211
nemlai exclusions exclude 4211 # whole order
nemlai exclusions hide 4211 5001234 # one product in that order
nemlai shops status
nemlai shops link --email you@example.com --shops bilka,foetex # prompts for the password
nemlai shops refresh bilka
nemlai settings update --cutoff 2026-01-01 --auto-learn true
Spending analytics
Ask where the grocery money went: from the CLI (nemlai analytics guide for the full reference) or from Python.
nemlai analytics overview --last this-month
nemlai analytics spend --product mælk --category Mejeri --last 12m --compare previous-period
nemlai analytics top categories --level sub --from 2026-01-01 --to 2026-03-31
nemlai analytics trend --bucket month --last 12m
nemlai analytics drill --order 0042
nemlai analytics receipts --product jordbær --last 3m --detail full --save
nemlai analytics categories-tree --last 12m
Tables are rendered by the SDK (canonical product variants, comparison changes, saved amounts, truncation notices); --format json|csv|markdown returns the server's own output.
from nemlai import NemlAI, AnalyticsError
client = NemlAI(token="your-token")
overview = client.analytics.overview(last="3m")
print(overview.spend.total, overview.spend.saved, overview.spend.comparison)
milk = client.analytics.spend(product="mælk", compare="previous-period")
for match in milk.matches:
print(match.name, match.total, match.product_numbers)
try:
client.analytics.trend("fortnight")
except AnalyticsError as err:
print(err.status_code, err.message, err.hint)
Methods: overview, spend, top, trend, drill, receipts, categories_tree (typed dataclass results), plus text(endpoint, payload, format="csv") for server-rendered output.
What it does
- Fetches order history from Nemlig.com
- Uses a cloud ML model trained on your purchase patterns
- Predicts quantities for your next basket
- Builds Nemlig basket URLs you can review and checkout
- Supports manual item search and hybrid baskets (manual + ML)
For agents
NemlAI has an MCP server for AI agents (Claude, ChatGPT, Codex, Grok, Hermes, OpenClaw and others)
at https://nemlai.milops.org/mcp, and a nemlai skill that teaches them the workflows. Sign in with OAuth where the
client supports it, or with the token from nemlai login (stored in ~/.config/nemlai/token).
Connect in one minute
export NEMLAI_TOKEN=$(cat ~/.config/nemlai/token) # the configs read the token from here
nemlai mcp install claude-code # or: codex, hermes, openclaw
nemlai mcp doctor # checks the URL, the token (whoami) and the tool list
nemlai mcp install <client>writes the client's MCP config and installs the skill. It backs up the file first, never changes other entries, and replaces an existingnemlaientry only with--force.--dry-runshows the diff;--embed-tokenwrites the token itself instead of${NEMLAI_TOKEN}.nemlai mcp config <client>prints the config instead (claude-code,codex,hermes,openclaw,claude-desktop,generic).- claude.ai, Claude Desktop, ChatGPT and Grok: add a custom connector with the URL and sign in (OAuth, Giv adgang).
The server explains itself: its instructions and the nemlai_guide tool cover the data model, the
rules (show the basket and total before sending, send only on the user's go-ahead, NemlAI never
checks out, never put a password in chat) and the workflows.
When MCP fails, this CLI is the fallback. Every MCP tool has a CLI command, and --json prints
one JSON object per command:
nemlai --json whoami
nemlai --json basket items
nemlai --json analytics spend --last 30d
If the CLI fails too, the web app at https://nemlai.milops.org has every feature. The full map (every tool's CLI command and web page), the rules and the workflows: https://nemlai.milops.org/agents.
License
MIT
Metadata
Release files for nemlai 0.11.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| nemlai-0.11.0.tar.gz | 92.7 kB | Without provenance | Details for nemlai-0.11.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| nemlai-0.11.0-py3-none-any.whl | Python 3 | none | any | Details for nemlai-0.11.0-py3-none-any.whl |
Total release size: 206.5 kB
Release files / nemlai-0.11.0.tar.gz
| Download URL | nemlai-0.11.0.tar.gz |
|---|---|
| Size | 92.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
8553077a8e53c611e0bfeb09b231886d5218d898357d1b90e16f2b9ce211b5a8
|
|
BLAKE2b-256 checksum How to use checksums |
30b499295d52e07ecf4be88a2097379cb47ea42ffb0bcb671896b25f884b104b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.18
|
Release files / nemlai-0.11.0-py3-none-any.whl
| Download URL | nemlai-0.11.0-py3-none-any.whl |
|---|---|
| Size | 113.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
2cd4a23b5f77118763d27298825c263e92087a51a2d744707ee36519c65fc3bc
|
|
BLAKE2b-256 checksum How to use checksums |
a54db0a3558e0f53e671f3a65fd7947039f0a05797bcd578f2564d47629b7a6b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.18
|
Security for nemlai 0.11.0
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.
No provenance found
None of the files in this release were uploaded with provenance metadata.
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.