You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
error is a stable PascalCase code (e.g. MediaNotFound) safe to branch on.
message is for display and may change.
Cached GETs also carry an X-Cache: HIT|MISS header.
Authentication
A Bearer token in the Authorization header:
Authorization: Bearer <token>
Obtain a token from POST /auth/login (valid 24 hours). The rule:
All GET requests are public.
State-changing methods require a token, with an intentionally-public
allowlist matched by (method, path): POST /media/by-ids (batched read),
PATCH /media/{id}/tags, and DELETE /media/{id}/tags/{tag_id} (anyone may tag),
plus POST /auth/login itself.
In the tables below: 🌐 = public, 🔒 = token required.
Rate limiting, CORS & CSRF
Rate limit: 120 requests / 60 s per IP; POST /auth/login is throttled
separately at 10 attempts / 5 min. A 429 carries Retry-After.
CORS: responses echo an allowed Origin (GALLERY_ALLOWED_ORIGINS); credentials allowed.
CSRF: state-changing requests must carry an Origin/Referer matching an
allowed origin, else 403 ForbiddenOrigin.
System
Method
Path
Auth
Returns
GET
/version
🌐
{ version, api_version }
GET
/openapi.json
🌐
The OpenAPI 3.1 document
GET
/docs
🌐
Scalar API reference (HTML)
Auth
Method
Path
Auth
Body
Returns
POST
/auth/login
🌐
{ password }
{ token }
Media
Method
Path
Auth
Body / Params
Returns
GET
/media/page/{page}[/{per_page}]
🌐
per_page clamped 1–200
MediaPage
GET
/media/untagged/{page}[/{per_page}]
🌐
—
MediaPage
GET
/media/with-tags/{tag_list}/{page}[/{per_page}]
🌐
tag_list comma-separated; leading - excludes (e.g. cat,-dog)
Duplicate detection is image-only, based on perceptual fingerprinting.
Method
Path
Auth
Body
Returns
GET
/duplicates/report
🔒
—
DuplicateReport (dismissed pairs filtered out)
POST
/duplicates/scan
🔒
—
ScanResult
POST
/duplicates/dismissals
🔒
{ media_id_1, media_id_2 }
{ dismissed, media_id_1, media_id_2 } (201)
Resource shapes
Full JSON schemas for every resource (Media, Tag, TagCategory, MediaPage,
TagListItem, TagImplication, CategoryMapping, TagMapping, DuplicateReport,
UploadSummary, DanbooruFetchResult, …) live in the OpenAPI document under
components.schemas. The frontend consumes them as generated TypeScript types
(frontend/src/types/api.generated.ts, via npm run gen:types).