API Reference
The Wallie server exposes a REST API over HTTP. All endpoints are available on the port your server is running on (default 9000).
No authentication is required — the API is designed for local network use. Don’t expose it to the internet.
Frame IDs
Section titled “Frame IDs”Each frame gets a short ID assigned automatically by the server the first time it checks in. The desktop app doesn’t display this ID anywhere in the UI — the easiest way to find yours is:
curl -s http://192.168.1.42:9000/admin/api/frames | python3 -m json.toolLook for the "id" field on each frame in the response.
Server status
Section titled “Server status”Quick health check. Returns server version and basic counts.
curl http://192.168.1.42:9000/{ "version": "1.9.1", "running": true, "port": 9000, "sources": 2, "frames": 3, "total_images": 847}GET /health
Section titled “GET /health”Returns 200 OK with {"healthy": true}. Useful for uptime monitors.
GET /admin/api/status
Section titled “GET /admin/api/status”Full server status including image index state and timezone info.
{ "version": "1.9.1", "running": true, "frames_count": 3, "sources_count": 2, "total_images": 847, "server_time": "2026-05-06T14:23:00.000Z", "index": { "is_scanning": false, "initial_scan_complete": true, "image_count": 847 }}Frames
Section titled “Frames”GET /admin/api/frames
Section titled “GET /admin/api/frames”Returns all registered frames with their current status. Use this to find your frame IDs.
curl http://192.168.1.42:9000/admin/api/frames{ "frames": [ { "id": "35506C", "name": "Frame 35506C", "battery_percentage": 72, "usb_power_connected": false, "firmware_version": "2.4.1", "next_check_in": 1746540000, "seconds_until_check_in": 3420, "current_display": { "image_name": "vacation-2024-001.jpg", "served_at": "2026-05-06T10:00:00.000Z" } } ]}| Field | Description |
|---|---|
id | Unique identifier assigned by the server when the frame first checks in. |
battery_percentage | 0–100. Only present after the frame has checked in at least once. |
usb_power_connected | true if the frame is currently charging. |
next_check_in | Unix timestamp of the frame’s next scheduled check-in. |
seconds_until_check_in | Seconds until next check-in (convenience field). |
current_display.image_name | Filename of the photo currently on the display. |
GET /admin/api/frames/{id}
Section titled “GET /admin/api/frames/{id}”Same as above for a single frame.
curl http://192.168.1.42:9000/admin/api/frames/35506CPUT /admin/api/frames/{id}
Section titled “PUT /admin/api/frames/{id}”Update a frame’s settings, including per-source photo order.
curl -X PUT http://192.168.1.42:9000/admin/api/frames/35506C \ -H "Content-Type: application/json" \ -d '{"refresh_minutes": 120}'To change the photo rotation order for one or more sources, include a source_configs array. Each entry must have a sourceId matching an existing source and a sort_mode value:
sort_mode | Behaviour |
|---|---|
lru | Rotate All — shows every photo before repeating. Default. |
random | Shuffle — picks randomly each rotation. |
newest_first | Most recently added photos first. |
oldest_first | Oldest photos first. |
curl -X PUT http://192.168.1.42:9000/admin/api/frames/35506C \ -H "Content-Type: application/json" \ -d '{ "source_configs": [ { "sourceId": "family-photos", "sourceType": "local_folder", "label": "Family Photos", "enabled": true, "weight": 1.0, "timeHint": "anytime", "sort_mode": "random", "config": {} } ] }'To find your sourceId values, check the sources list:
curl -s http://192.168.1.42:9000/admin/api/sources | python3 -m json.toolDELETE /admin/api/frames/{id}
Section titled “DELETE /admin/api/frames/{id}”Remove a frame from the server.
POST /admin/api/frames/{id}/sources
Section titled “POST /admin/api/frames/{id}/sources”Add a photo source to a frame.
curl -X POST http://192.168.1.42:9000/admin/api/frames/35506C/sources \ -H "Content-Type: application/json" \ -d '{"source_id": "family-photos"}'GET /admin/api/frames/{id}/pin
Section titled “GET /admin/api/frames/{id}/pin”POST /admin/api/frames/{id}/pin
Section titled “POST /admin/api/frames/{id}/pin”DELETE /admin/api/frames/{id}/pin
Section titled “DELETE /admin/api/frames/{id}/pin”Get, set, or clear a pinned image for a frame. A pinned image is shown instead of the normal rotation until unpinned.
Photo sources
Section titled “Photo sources”GET /admin/api/sources
Section titled “GET /admin/api/sources”List all configured photo sources.
{ "sources": [ { "id": "family-photos", "type": "local_folder", "path": "/photos/family", "enabled": true, "image_count": 412 } ]}POST /admin/api/sources
Section titled “POST /admin/api/sources”Add a new source.
curl -X POST http://192.168.1.42:9000/admin/api/sources \ -H "Content-Type: application/json" \ -d '{"type": "local_folder", "path": "/photos/vacation", "enabled": true}'DELETE /admin/api/sources/{id}
Section titled “DELETE /admin/api/sources/{id}”Remove a source.
Images
Section titled “Images”GET /admin/api/images
Section titled “GET /admin/api/images”List indexed images. Supports filtering:
| Query param | Description |
|---|---|
source_id | Filter by source |
limit | Max results (default 100) |
offset | Pagination offset |
POST /admin/api/upload
Section titled “POST /admin/api/upload”Upload a photo directly to a source folder.
curl -X POST \ "http://192.168.1.42:9000/admin/api/upload?source_id=family-photos&filename=photo.jpg" \ --data-binary @photo.jpgDELETE /admin/api/delete_image
Section titled “DELETE /admin/api/delete_image”Remove an image from the library.
curl -X DELETE \ "http://192.168.1.42:9000/admin/api/delete_image?image_id=photo.jpg"GET /admin/api/thumbnail
Section titled “GET /admin/api/thumbnail”Fetch a thumbnail for an image.
curl "http://192.168.1.42:9000/admin/api/thumbnail?image_id=photo.jpg&width=200"GET /admin/api/logs
Section titled “GET /admin/api/logs”Returns recent server log output. Useful for debugging frame connectivity.
Examples
Section titled “Examples”Find your frame IDs
Section titled “Find your frame IDs”curl -s http://192.168.1.42:9000/admin/api/frames | \ python3 -c "import json, sysfor f in json.load(sys.stdin)['frames']: print(f\"{f['name']} → id: {f['id']}\")"Check battery on all frames
Section titled “Check battery on all frames”curl -s http://192.168.1.42:9000/admin/api/frames | \ python3 -c "import json, sysfor f in json.load(sys.stdin)['frames']: pct = f.get('battery_percentage', 'unknown') usb = ' (charging)' if f.get('usb_power_connected') else '' print(f\"{f['id']}: {pct}%{usb}\")"Alert when a frame drops below 20%
Section titled “Alert when a frame drops below 20%”#!/bin/bashcurl -s http://192.168.1.42:9000/admin/api/frames | python3 -c "import json, sysfor f in json.load(sys.stdin)['frames']: pct = f.get('battery_percentage') if pct is not None and pct < 20: print(f\"LOW BATTERY: {f['id']} is at {pct}%\")"Run this from a cron job to get notified before a frame goes dark.
Timezone
Section titled “Timezone”GET /admin/api/timezone
Section titled “GET /admin/api/timezone”POST /admin/api/timezone
Section titled “POST /admin/api/timezone”Get or set the timezone used for wake-hour calculations. The desktop app sets this automatically; you only need this if scripting directly against the server.
curl -X POST http://192.168.1.42:9000/admin/api/timezone \ -H "Content-Type: application/json" \ -d '{"offset_seconds": -25200, "name": "America/Los_Angeles"}'