NemlAI CLI Documentation

Use NemlAI from CLI, API, SDK or AI agents to automate grocery shopping on nemlig.com in Denmark. Search the catalogue, generate smart baskets, and send them to Nemlig — all from code or the terminal.

The NemlAI CLI gives you four ways to interact with the platform:

Quick Start

1. Create an account

curl -X POST https://nemlai.milops.org/cli/api/auth/signup \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Simon",
    "email": "[email protected]",
    "password": "your-password",
    "nemlig_email": "[email protected]",
    "nemlig_password": "nemlig-password"
  }'

2. Log in and get a token

curl -X POST https://nemlai.milops.org/cli/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email": "[email protected]", "password": "your-password"}'

# Response:
STATUS: ok
TOKEN nemlai_abc123...def456
EXPIRES 2026-03-31T00:00:00Z

3. Use the token

# Save your token
export NEMLAI_TOKEN="nemlai_abc123...def456"

# Use it with any endpoint
curl https://nemlai.milops.org/cli/api/whoami \
  -H "Authorization: Bearer $NEMLAI_TOKEN"

Web Terminal

Open /cli in your browser. No installation needed. You get a full terminal with command history (up/down arrows), tab completion, and Ctrl+C to cancel.

The web terminal uses a WebSocket connection, so commands execute instantly with live feedback.

Local Terminal

Install the nemlai package and get the same terminal experience running on your own machine. All logic stays on the server — the local CLI is just a thin client that sends commands over HTTP.

Install

# PyPI (all platforms)
pip install nemlai

# Arch Linux (AUR)
yay -S nemlai

# Ubuntu / Debian
sudo add-apt-repository ppa:milops/nemlai
sudo apt install nemlai

Run

# Start the interactive terminal
nemlai

# Or run a single command
nemlai help
nemlai whoami
nemlai basket items

Features

Configuration

# Point to a different server
nemlai --url http://localhost:8090

# Use a specific token
nemlai --token nemlai_abc123...

# Or use environment variables
export NEMLAI_TOKEN="nemlai_abc123..."
export NEMLAI_URL="http://localhost:8090"
nemlai

Config is stored in ~/.config/nemlai/:

FilePurpose
tokenSaved auth token (chmod 600)
base_urlCustom server URL
historyCommand history

HTTP API

All endpoints live under /cli/api/. Responses are plain text with a consistent format:

STATUS: ok
<response body>

or on error:

STATUS: error
ERROR <message>
HINT <suggestion>

Public endpoints (no auth required)

EndpointDescription
GET /cli/api/healthHealth check
GET /cli/api/helpList all commands
POST /cli/api/auth/signupCreate account
POST /cli/api/auth/loginLog in, get token

Authenticated endpoints

Include Authorization: Bearer <token> header.

EndpointDescription
GET /cli/api/whoamiCurrent user info
POST /cli/api/auth/logoutInvalidate token
GET /cli/api/householdHousehold details
POST /cli/api/orders/refreshRefresh order history
GET /cli/api/basket/statusBasket unlock status
POST /cli/api/basket/generateGenerate a smart basket
GET /cli/api/basket/itemsList basket items
POST /cli/api/basket/addAdd item to basket
POST /cli/api/basket/removeRemove item
POST /cli/api/basket/adjustAdjust item quantity
POST /cli/api/basket/sendSend basket to Nemlig
POST /cli/api/basket/searchSearch products
GET /cli/api/settingsShow settings
POST /cli/api/settingsUpdate a setting
GET /cli/api/preferencesShow food preferences
POST /cli/api/preferencesUpdate a preference
POST /cli/api/v2/analytics/spendAggregate spending by product/category over a date range
POST /cli/api/v2/analytics/topTop N products or categories by spend
POST /cli/api/v2/analytics/trendTime-series spend buckets (day/week/month)
POST /cli/api/v2/analytics/drillRaw order-line breakdown for a product or order
GET /cli/api/achievementsList achievements
GET /cli/api/notifications/statusPush notification status
POST /cli/api/notifications/testSend test notification

Generic command endpoint

You can also send any command as text:

curl -X POST https://nemlai.milops.org/cli/api/command \
  -H "Authorization: Bearer $NEMLAI_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"command": "basket generate --size medium --date 2026-03-15"}'
Tip for AI agents: The generic /cli/api/command endpoint accepts any command as plain text. This is the simplest integration path — just send commands the same way you would type them in the terminal.

Python SDK

Install

pip install nemlai

Usage

from nemlai import NemlAI

# Log in (returns a client with token)
client = NemlAI.login("[email protected]", "password")

# Or use an existing token
client = NemlAI(token="nemlai_abc123...")

# Account
me = client.whoami()
print(me)  # {'user': 'Simon', 'email': '...', ...}

# Generate a basket
result = client.basket.generate(size="medium", date="2026-03-15")
print(result)

# List basket items
items = client.basket.items()

# Search and add a product
results = client.basket.search("oat milk")
client.basket.add(query="oat milk")

# Remove or adjust items
client.basket.remove(item=3)
client.basket.adjust(item=7, quantity=2)

# Send basket to Nemlig
url = client.basket.send()
print(url)  # https://nemlig.com/basket?skus=...

# Orders
client.orders.refresh(password="nemlig-password")

# Settings and preferences
client.settings.show()
client.preferences.show()

# Household
client.household.info()
Tip: The SDK is a thin client that wraps the HTTP API. It doesn't include any server code, models, or internal logic — just convenient Python methods for calling the API.

Custom base URL

# For local development
client = NemlAI(token="nemlai_...", base_url="http://localhost:8090")

All Commands

Account

CommandDescription
signupCreate a new account
loginLog in and get API token
logoutInvalidate your token
whoamiShow your account info
profile updateUpdate display name or Nemlig email
password changeChange your app password
password reset-nemligRequest a Nemlig password reset
account deleteDelete your account

Basket (Generer kurv)

CommandDescription
basket statusCheck if basket generation is unlocked
basket generate --size <s> [--date <d>]Generate a smart basket
basket itemsList items in current basket
basket add --query <q>Search and add item
basket remove --item <#>Remove item by number
basket adjust --item <#> --quantity <n>Set item quantity
basket sendSend basket to Nemlig
basket search --query <q>Search products

Orders

CommandDescription
orders refresh --password <p>Refresh your order history
orders refresh-member --email <e> --password <p>Refresh household member's orders

Settings & Preferences

CommandDescription
settings showShow current settings
settings update --key <k> --value <v>Update a setting
preferences showShow food preferences
preferences update --key <k> --value <v>Update a preference

Analytics

Ask questions about your own spending. Filters accept either repeated flags or a comma-separated string, so multi-product or multi-category reports take one call instead of N.

CommandDescription
analytics guideLocal cheat sheet — date grammar, scopes, formats, multi-value syntax
analytics spend [--product <name>]... [--category <name>]... [--exclude <name>]... [--last <expr>] [--compare previous-period] Aggregate spend over a date range. --product and --category are mutually exclusive but each accepts multiple values. Use --exclude to filter out noise.
analytics top <products|categories> [--last <expr>] [--limit <n>] [--sub-categories]Top N products or categories by spend.
analytics trend [--product <name>]... [--category <name>]... --bucket <day|week|month> [--last <expr>]Spend over time as buckets; renders with sparklines in table mode.
analytics drill [--product <name>]... [--category <name>]... [--order <id>] [--last <expr>] [--limit <n>]Raw order-line entries for a product, category, or specific order.

Multi-value examples — both forms are equivalent and combine in one round-trip:

# Repeated flags (kubectl / gh / git convention; shell-safe for any name)
nemlai analytics spend --last 12m \
    --product vandmelon --product jordbær --product hindbær --product gifler

# Comma-separated (ergonomic shortcut)
nemlai analytics spend --last 12m --product "vandmelon,jordbær,hindbær,gifler"

# Categories work the same way
nemlai analytics spend --last 3m --category "Grønt,Frost"
nemlai analytics trend --category Mejeri --category Drikke --bucket month --last 12m
Comma-split caveat: ~1.7% of Danish product names use comma as a decimal separator (e.g. "Letmælk 1,5%"). For search terms that may contain commas, use repeated flags rather than the comma-string shortcut. Category names are comma-free, so --category is always safe with either form.

Date range grammar--last 7d, --last 30d, --last 3m, --last 12m, --last 2y, this-month, last-month, ytd, this-year, last-year, or --from 2026-01-01 --to 2026-03-31. All terms must be in Danish (Nemlig stores names in Danish: mælk not milk).

Other

CommandDescription
household infoShow household details + invite code
notifications statusPush notification status
notifications testSend a test notification
achievementsList your achievements

Authentication

The CLI uses token-based authentication. Tokens are long-lived (30 days by default) and can be revoked with logout.

For the Web Terminal

Just type login --email [email protected] --password yourpass. The token is stored in your WebSocket session automatically.

For the HTTP API

# Get a token
TOKEN=$(curl -s -X POST https://nemlai.milops.org/cli/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email": "[email protected]", "password": "pass"}' \
  | grep TOKEN | awk '{print $2}')

# Use it
curl https://nemlai.milops.org/cli/api/whoami \
  -H "Authorization: Bearer $TOKEN"

For the Python SDK

# Login returns a client with token
client = NemlAI.login("email", "password")

# Or reuse a saved token
client = NemlAI(token="nemlai_...")
Security: Tokens are hashed (SHA-256) before storage. The raw token is only shown once at login. Store it securely.

For AI Agents

The CLI is designed to be easy for autonomous agents to use:

Recommended agent flow

# 1. Check health
curl https://nemlai.milops.org/cli/api/health

# 2. Log in once, save the token
curl -X POST https://nemlai.milops.org/cli/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email": "...", "password": "..."}'

# 3. Generate a basket
curl -X POST https://nemlai.milops.org/cli/api/basket/generate \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"size": "medium", "date": "2026-03-15"}'

# 4. Review and adjust items
curl https://nemlai.milops.org/cli/api/basket/items \
  -H "Authorization: Bearer $TOKEN"

# 5. Send to Nemlig
curl -X POST https://nemlai.milops.org/cli/api/basket/send \
  -H "Authorization: Bearer $TOKEN"

NemlAI CLI v1.0 — Open Terminal