Source: https://spooly.eu/en/docs/api

# API reference

The REST API reads and changes spools, slots, prints, maintenance and statistics of your own account. Everything comes as JSON, only the spreadsheet link returns CSV. It delivers no payment or customer data, and it does not control printers.

## Base URL and sign-in

Every request goes to the base URL and carries a key in the header. [Keys and sign-in](https://spooly.eu/en/docs/authentication) explains how to create one.

The examples read the key from the environment variable `SPOOLY_KEY`.

Base URL:

```
https://spooly.eu/api/v1
```

Header:

```
Authorization: Bearer spk_dein_schluessel
```

## Formats

- Requests and responses are JSON in UTF-8.
- Timestamps are ISO 8601 in UTC, for example `2026-10-30T14:02:00Z`.
- Weights are in grams, money is in the currency of the account (`currency` from `GET /me`).
- Identifiers are strings. Treat them as opaque: their format differs by entity.
- A missing value is `null`.

## Paginated lists

Spools, prints and the maintenance log come in pages. `total` gives the full count. Increase `offset` by `limit` until you reach `total`.

```bash
curl "https://spooly.eu/api/v1/spools?limit=100&offset=100" \
  -H "Authorization: Bearer $SPOOLY_KEY"
```

Response (200):

```
{
  "items": [
    "…"
  ],
  "total": 230,
  "limit": 100,
  "offset": 100
}
```

## Response to changes

Every change responds in the same format. `change_id` is its entry in the change list of the connection, `summary` is a sentence for humans. It also carries what changed: `spool`, `slot`, `task` or `printer`, and for weight `old_grams` and `new_grams`.

You can undo weight, slots and completed maintenance in the app, not through the API.

Response (200):

```
{
  "change_id": 381,
  "spool": {
    "id": "7f3c2a10-5e1b-4c8e-9a44-2d6b1f0c7e21",
    "number": 1,
    "title": "Jade White",
    "name": null,
    "manufacturer": "Bambu Lab",
    "material": "PLA Matte",
    "finish": "matt",
    "color_hex": "#E8E9DF",
    "color_name": "Jade White",
    "remaining_grams": 300,
    "spool_size_grams": 1000,
    "empty_spool_grams": 250,
    "fill_level": "low",
    "location": "Schrank 01 · Fach vorne · Platz 3",
    "location_id": "loc_3c9b7e1a5d60",
    "place": 3,
    "archived": false,
    "price": 19.99,
    "notes": null
  },
  "slot": null,
  "old_grams": 342,
  "new_grams": 300,
  "replayed": false,
  "task": null,
  "printer": null,
  "stock_empty": null,
  "summary": "Spule #1 Jade White: 342 auf 300 g"
}
```

## Idempotency-Key

Send your own `Idempotency-Key` with every change if your program retries after an interruption. The same key with the same content runs only once. You then get the response of the first call again, with `replayed` set to true.

The same key with different content returns 422 `idempotency_conflict`. So use a new key for each change, for example a UUID. Allowed are 1 to 128 visible ASCII characters.

```bash
curl -X POST "https://spooly.eu/api/v1/spools/7f3c2a10-5e1b-4c8e-9a44-2d6b1f0c7e21/weight" \
  -H "Authorization: Bearer $SPOOLY_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 9e3c7a52-1d48-4b6f-8e0a-52f7b3c1d96e" \
  -d '{"gross_grams":742}'
```

## OpenAPI schema

The schema describes all endpoints, parameters and responses in machine-readable form. Use it to generate a client for your language or to give your AI the exact description.

[Download the OpenAPI schema](https://spooly.eu/api/v1/openapi.json)

`/api/v1/openapi.json`

## What the API does not deliver

- Customer and company data from orders.
- Credentials for printers and accounts.
- Payment data.
- Permanent deletion. Archiving and restoring work.
- Controlling printers.
- Your account and your subscription.

Responses contain only fields from a fixed list. A new column in the database never shows up in the API on its own. AI assistants get the same data through [MCP](https://spooly.eu/en/docs/ai).
