The Spisko REST API

The Spisko REST API is the one the app itself uses to change your items, lists and stores. This page lists its endpoints, the fields each one takes, and what comes back. To connect a script or an AI assistant, the MCP server is the easier way in.

Before you start

The REST API takes the access token of a signed-in Spisko session, which lasts an hour. Personal access tokens don't work here: they're for the MCP server, which covers lists, items and stock, and suits scripts and assistants better.

  • Base URL: https://spisko.com/api
  • Format: JSON in and JSON out, with Content-Type: application/json on every request that has a body.
  • Auth: Authorization: Bearer <access_token> on every request. Without a valid token the answer is 401.
  • Whose data: what your account can see in the app. That is your own items, lists and stores, and the ones shared with you in a space. Something you can’t see answers 404.
List your items
curl https://spisko.com/api/items \
  -H "Authorization: Bearer $ACCESS_TOKEN"

Answers and errors

A read answers 200 with the record or a list of them. A create answers 201 with what it made, and an update 200 with the record as it is now.

An error answers with { "error": "…" }, in English. When the app explains an error in its own words, a code comes with it:

  • 400: the body isn’t JSON, or a field is missing or wrong. The message names the field.
  • 401: no token, or one that has expired.
  • 403: not yours to change. plus_required means the change needs Spisko Plus. space_cap, member_cap and store_cap mean a free account’s limit: one space, six people in it, three stores.
  • 404: not there, or not visible to you.
  • 409: it already exists, or a request with the same Idempotency-Key is still running.

Retrying safely. Writes to items, categories, lists and what’s on them take an optional Idempotency-Key header, a UUID you make. Send the same key again (after a timeout, say) and you get the first answer back instead of the change happening twice. Keys are kept for seven days.

Put bananas on a list, safely retried
curl -X POST https://spisko.com/api/shopping-lists/$LIST_ID/items \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{ "name": "Bananas", "quantity": 3 }'

Items

Items are your inventory: what you keep at home, how much you have (current_quantity) and how much you like to keep (goal_quantity).

  • GET /api/items

    Your items, newest first, each with its categories.

  • POST /api/items

    Add an item.

    name*, description, current_quantity, goal_quantity, unit, barcode

  • GET /api/items/{id}

    One item, with its categories.

  • PATCH /api/items/{id}

    Change an item: any of the fields above, at least one.

  • PATCH /api/items/{id}/quantity

    Change how much there is. delta adds or takes away (it can be negative); the other two set a value.

    delta, current_quantity, goal_quantity (at least one)

  • DELETE /api/items/{id}

    Remove an item from your inventory. Answers { "deleted": true, "id": … }.

  • POST /api/items/{id}/restore

    Bring back a removed item, the same one, with its categories.

  • POST /api/items/{id}/categories

    Put an item in a category.

    category_id*

  • DELETE /api/items/{id}/categories/{categoryId}

    Take an item out of a category.

  • GET /api/shopping/suggestions

    Items running low, down to half their goal quantity or less: what to buy next.

Categories

Categories group the inventory: where things are kept, or what they are.

  • GET /api/categories

    Your categories.

  • POST /api/categories

    Add a category. type says what kind it is, for example storage or room.

    name*, type*, is_shareable

  • PATCH /api/categories/{id}

    Rename or retype a category.

  • DELETE /api/categories/{id}

    Delete a category.

Shopping lists

A list’s kind decides what finishing it does: permanent (bought things come off the list), recurring (everything stays, unticked for next time) or adhoc (the list is deleted and the inventory isn’t touched). space_id shares a list with a space; null makes it private again.

  • GET /api/shopping-lists

    Your lists and the ones shared with you, the most recently changed first.

  • POST /api/shopping-lists

    Make a list. status is active, completed or archived.

    name*, description, kind, status, space_id, location_id

  • GET /api/shopping-lists/{id}

    One list, with everything on it.

  • PATCH /api/shopping-lists/{id}

    Rename, retype, share or archive a list. Only its owner can.

    name, description, kind, status, space_id

  • DELETE /api/shopping-lists/{id}

    Delete a list.

  • POST /api/shopping-lists/{id}/finish

    Finish a trip: what’s ticked goes into the inventory, and the list changes as its kind says. location_id records the store you were at.

    location_id

  • POST /api/shopping-lists/{id}/locations

    Attach a saved store to a list.

    location_id*

  • DELETE /api/shopping-lists/{id}/locations/{locationId}

    Detach a store from a list.

What’s on a list

An entry is an item from your inventory (item_id) or just a name. A named entry with add_to_inventory joins the inventory when it’s ticked and the trip is finished. {entryId} is the entry’s own id, from the list.

  • POST /api/shopping-lists/{id}/items

    Put something on a list: item_id or name, not both.

    item_id or name*, quantity, unit, add_to_inventory

  • PATCH /api/shopping-lists/{id}/items/{entryId}

    Tick it off (checked: true) or untick it, change how many, or move it to another aisle. aisle: null goes back to the aisle guessed from its name.

    checked, quantity, name, unit, aisle (at least one)

  • DELETE /api/shopping-lists/{id}/items/{entryId}

    Take it off the list.

The aisles are produce, bakery, meat, dairy, frozen, pantry, snacks, drinks, household, personal, babyPet and other.

Stores

radius_meters is how close counts as “near” for reminders. aisle_order lists all twelve aisles in the order you walk the store, or is null for the usual order. category is the kind of store, such as grocery_store or pharmacy.

  • GET /api/locations

    Your saved stores and the ones shared with you.

  • POST /api/locations

    Save a store. A free account keeps up to three.

    name*, address, latitude, longitude, radius_meters, category, space_id, aisle_order

  • PATCH /api/locations/{id}

    Change a store. Only its owner can set the aisle order.

  • DELETE /api/locations/{id}

    Delete a store.

Frequently asked questions

Can I use a personal access token with the REST API?

Not yet. The REST endpoints take a signed-in session’s access token. Personal access tokens and OAuth work with the MCP server, which reads and changes the same lists and items.

Does the API need Spisko Plus?

No, apart from what needs Plus in the app too, such as recipes and photos. Personal access tokens and the MCP server do need Plus.

Can the API see other people’s lists?

Only the ones they share with you in a space, as in the app. Every request acts as your account, and the database decides what that account can see.

Want to try it on your own lists?

No credit card required • Works on all devices