-
-
Notifications
You must be signed in to change notification settings - Fork 5
API Reference
ankerctl exposes a REST API and WebSocket endpoints from the Flask web server. The default address is 127.0.0.1:4470.
All endpoints are served by web/__init__.py. For implementation details (which service handles each route, how state is mutated) see Architecture.
When no API key is set, all endpoints are public (default, backwards compatible). When an API key is set:
- GET requests are unauthenticated by default
- POST / DELETE requests always require auth
- A small allow-list of GET paths also requires auth (see Protected GET paths below)
-
Setup paths (
/api/ankerctl/config/upload,/api/ankerctl/config/login) are exempt when no printer is configured yet -
All
/api/debug/*paths require auth (prefix match)
Three ways to authenticate:
| Method | Use it for |
|---|---|
X-Api-Key: <key> header |
Slicers (PrusaSlicer, OrcaSlicer fill this from the API Key field) |
?apikey=<key> URL parameter |
Browser bootstrap; sets a session cookie automatically |
| Session cookie | Browser, after first parameter-based auth |
These GET endpoints require auth even though they are reads:
/api/ankerctl/server/reload-
/api/debug/state,/api/debug/logs,/api/debug/services(and all other/api/debug/*) /api/settings/mqtt/api/notifications/settings
| Method | Path | Auth | Description |
|---|---|---|---|
GET |
/ |
No | Render main UI page |
GET |
/api/health |
No | Liveness probe — always returns {"status": "ok"}
|
GET |
/api/version |
No | OctoPrint-compatible version info |
GET |
/video |
No | Raw H.264 video stream (use ?for_timelapse=1 for timelapse client) |
| Method | Path | Auth | Description |
|---|---|---|---|
POST |
/api/ankerctl/config/upload |
Setup-exempt | Upload login.json file |
POST |
/api/ankerctl/config/login |
Setup-exempt | Email/password login |
GET |
/api/ankerctl/server/reload |
Yes | Reload config + restart all services |
POST |
/api/ankerctl/config/upload-rate |
Yes | Set upload rate; form field upload_rate_mbps (5/10/25/50/100) |
| Method | Path | Auth | Description |
|---|---|---|---|
POST |
/api/printer/gcode |
Yes | Send G-code; JSON {"gcode": "G28"}. Motion blocked while printing (returns 409). |
POST |
/api/printer/control |
Yes | Print state control; JSON {"value": N} — see table below |
POST |
/api/printer/autolevel |
Yes | Start auto-leveling (G29); blocked while printing |
GET |
/api/printer/bed-leveling |
No | Read 7×7 bed level grid via M420 V (~15 s) |
GET |
/api/printer/bed-leveling/last |
No | Most recently saved bed level grid from log dir |
GET |
/api/snapshot |
No | Capture a JPEG snapshot via ffmpeg from /video; returns file download |
Print Control values (POST /api/printer/control, JSON body {"value": N}):
N |
Action |
|---|---|
2 |
Pause |
3 |
Resume |
4 |
Stop / cancel |
0 |
Restart from beginning |
| Method | Path | Auth | Description |
|---|---|---|---|
GET |
/api/notifications/settings |
Yes | Return current Apprise config (contains API keys) |
POST |
/api/notifications/settings |
Yes | Update Apprise config; JSON {"apprise": {...}}
|
POST |
/api/notifications/test |
Yes | Send test notification; optionally pass {"apprise": {...}} to override settings |
| Method | Path | Auth | Description |
|---|---|---|---|
GET |
/api/settings/timelapse |
No | Return current timelapse config |
POST |
/api/settings/timelapse |
Yes | Update timelapse config; JSON {"timelapse": {...}}; auto-reloads service |
GET |
/api/settings/mqtt |
Yes | Return current Home Assistant MQTT config (contains broker password) |
POST |
/api/settings/mqtt |
Yes | Update HA MQTT config; JSON {"home_assistant": {...}}; auto-reloads service |
| Method | Path | Auth | Description |
|---|---|---|---|
GET |
/api/history |
No | List entries; query ?limit= (default 50), ?offset= (default 0). Returns {"entries": [...], "total": N}
|
DELETE |
/api/history |
Yes | Clear all history entries |
| Method | Path | Auth | Description |
|---|---|---|---|
GET |
/api/timelapses |
No | List videos; returns {"videos": [{filename, size_bytes, created_at}], "enabled": bool}
|
GET |
/api/timelapse/<filename> |
No | Download a video file (MP4) |
DELETE |
/api/timelapse/<filename> |
Yes | Delete a video |
| Method | Path | Auth | Description |
|---|---|---|---|
GET |
/api/filaments |
No | List all filament profiles |
POST |
/api/filaments |
Yes | Create a profile; JSON body with profile fields |
PUT |
/api/filaments/<id> |
Yes | Update a profile; JSON body with fields to change |
DELETE |
/api/filaments/<id> |
Yes | Delete a profile |
POST |
/api/filaments/<id>/apply |
Yes | Send M104 / M140 to printer with profile temperatures |
POST |
/api/filaments/<id>/duplicate |
Yes | Duplicate profile (appends (copy) to name) |
| Method | Path | Auth | Description |
|---|---|---|---|
GET |
/api/printers |
No | List configured printers + active_index + locked flag |
POST |
/api/printers/active |
Yes | Switch active printer; JSON {"index": N}. Blocked during print (409) or when PRINTER_INDEX env var is set (403). |
| Method | Path | Auth | Description |
|---|---|---|---|
POST |
/api/files/local |
Yes | OctoPrint-compatible file upload; form fields: file (G-code), print (bool) |
This endpoint is the one PrusaSlicer / OrcaSlicer / SuperSlicer / Bambu Studio use when configured as a print host.
| Path | Direction | Description |
|---|---|---|
/ws/mqtt |
Server → Client | Raw MQTT message stream (JSON objects from MqttQueue) |
/ws/video |
Server → Client | Raw H.264 video frames |
/ws/pppp-state |
Server → Client | PPPP connection status ({"status": "connected"|"disconnected"}) |
/ws/upload |
Server → Client | File upload progress events from FileTransferService
|
/ws/ctrl |
Bidirectional | Light, video quality, video enable/disable. Inline auth — see below. |
before_request does not fire for WebSocket routes, so /ws/ctrl enforces auth inline: when ANKERCTL_API_KEY is set, the first message must include the key. The endpoint accepts JSON commands like:
{"light": true}
{"light": false}
{"video_profile": "sd"}
{"video_profile": "hd"}
{"video_enabled": true}
{"video_enabled": false}Available video profiles:
| ID | Resolution |
|---|---|
sd |
848 × 480 |
hd |
1280 × 720 (default) |
fhd |
1920 × 1080 (snapshot only — too high for live stream) |
| Method | Path | Auth | Description |
|---|---|---|---|
GET |
/api/debug/state |
Yes |
MqttQueue internal state JSON |
POST |
/api/debug/config |
Yes | Set debug logging; JSON {"debug_logging": bool}
|
POST |
/api/debug/simulate |
Yes | Fire simulated event; JSON {"type": "...", "payload": {...}}
|
GET |
/api/debug/services |
Yes | Service health summary (state, refs, type) |
POST |
/api/debug/services/<name>/restart |
Yes | Restart a named service asynchronously |
GET |
/api/debug/logs |
Yes | List log files in ANKERCTL_LOG_DIR
|
GET |
/api/debug/logs/<filename> |
Yes | Tail log file; query ?lines=N (default 500). Path-traversal protected. |
GET |
/api/debug/bed-leveling |
Yes | Same as /api/printer/bed-leveling (debug alias) |
type |
Payload fields | Description |
|---|---|---|
start |
filename |
Simulate print start |
finish |
filename |
Simulate print finish |
fail |
filename |
Simulate print failure |
progress |
progress (0–100), filename, elapsed, remaining
|
Emit fake progress |
temperature |
temp_type (nozzle/bed), current, target (1/100 °C) |
Emit fake temperature |
speed |
speed (mm/s) |
Emit fake speed |
layer |
current_layer, total_layers
|
Emit fake layer notification |
curl -X POST http://localhost:4470/api/printer/gcode \
-H "X-Api-Key: my-secret-key" \
-H "Content-Type: application/json" \
-d '{"gcode": "G28"}'curl -X POST http://localhost:4470/api/printer/control \
-H "X-Api-Key: my-secret-key" \
-H "Content-Type: application/json" \
-d '{"value": 2}'curl -X POST http://localhost:4470/api/printers/active \
-H "X-Api-Key: my-secret-key" \
-H "Content-Type: application/json" \
-d '{"index": 1}'Returns 409 if a print is active, 403 if PRINTER_INDEX env var is set.
curl -X POST http://localhost:4470/api/files/local \
-H "X-Api-Key: my-secret-key" \
-F "file=@my.gcode" \
-F "print=true"curl http://localhost:4470/api/timelapses{
"enabled": true,
"videos": [
{"filename": "my-print_20260509_120000.mp4", "size_bytes": 1234567, "created_at": "2026-05-09T12:00:00Z"}
]
}const ws = new WebSocket("ws://localhost:4470/ws/mqtt");
ws.onmessage = (e) => console.log(JSON.parse(e.data));ankerctl follows standard HTTP semantics with a few project-specific cases:
| Code | When |
|---|---|
200 |
Success |
400 |
Bad request body / invalid JSON |
401 |
Auth required and missing/invalid |
403 |
Operation forbidden — typically POST /api/printers/active while PRINTER_INDEX is set |
404 |
Unknown route or unknown filament/timelapse ID |
409 |
Conflict — e.g. switching printers or sending motion G-code while printing |
413 |
Upload exceeds UPLOAD_MAX_MB
|
500 |
Server error (check logs) |
CORS is not enabled by default. The web UI and the Flask backend are served from the same origin, so cross-origin XHR is not needed. To embed the API in another web app, run a reverse proxy (nginx / Caddy) that handles CORS headers.