API Endpoints¶
Complete reference for all API endpoints.
Image Serving¶
GET /albums/[...path]¶
Serves original images from the albums directory.
Parameters:
path- Path to image file (e.g.,2025/vacation/photo.jpg)
Response:
200- Image file with caching headers400- Invalid path (path traversal attempt)404- File not found
Headers:
Thumbnail Generation¶
GET /api/thumbnail¶
Generates and serves optimized thumbnails.
Query Parameters:
| Param | Required | Description |
|---|---|---|
path | Yes | Relative path to original image |
size | Yes | Size preset: small, medium, large |
Example:
Response:
200- JPEG thumbnail400- Missing or invalid parameters404- Original image not found500- Processing error
Size Presets:
| Size | Max Width |
|---|---|
| small | 400px |
| medium | 1200px |
| large | 1920px |
Metadata Extraction¶
GET /api/exif¶
Extracts EXIF metadata from photos.
Query Parameters:
| Param | Required | Description |
|---|---|---|
path | Yes | Relative path to image |
Example:
Response:
{
"Make": "Canon",
"Model": "EOS R5",
"LensModel": "RF 24-70mm F2.8",
"FocalLength": 35,
"FNumber": 2.8,
"ExposureTime": 0.004,
"ISO": 100,
"DateTimeOriginal": "2025:06:15 14:30:00",
"ImageWidth": 8192,
"ImageHeight": 5464
}
GET /api/video-info¶
Extracts video metadata via ffprobe.
Query Parameters:
| Param | Required | Description |
|---|---|---|
path | Yes | Relative path to video |
Response:
Authentication¶
POST /api/unlock¶
Primary authentication endpoint for password-protected albums.
Content-Type: application/x-www-form-urlencoded
Form Fields:
| Field | Required | Description |
|---|---|---|
albumPath | Yes | Path to the album |
password | Yes | User-entered password |
returnUrl | No | Redirect URL after success |
Response:
302- Redirect to album (with cookie set)302- Redirect with?error=wrong-password302- Redirect with?error=rate-limited
Cookie Set on Success:
POST /api/check-password¶
Legacy JSON API for password validation.
Content-Type: application/json
Request:
Response (Success):
Response (Failure):
Response (Rate Limited):
{
"success": false,
"error": "Too many attempts. Please try again in 15 minutes.",
"rateLimited": true
}
POST /api/check-access¶
Checks if user has access to an album.
Content-Type: application/json
Request:
{
"albumPath": "2025/weddings/party",
"password": "optional",
"unlockedTokens": ["token1", "token2"],
"providedToken": "optional-url-token"
}
Response (Access Granted):
Response (Password Required):
{
"success": false,
"requiresPassword": true,
"blockingAlbum": {
"path": "2025/weddings",
"title": "2025 Weddings"
}
}
Album Download¶
GET /api/download-album¶
Creates a ZIP archive of album photos.
Query Parameters:
| Param | Required | Description |
|---|---|---|
path | Yes | Path to the album |
Headers Required:
| Header | Description |
|---|---|
X-Album-Token | Album access token (for protected albums) |
Response:
200- ZIP file download401- Unauthorized (missing or invalid token)404- Album not found
Response Headers:
Error Responses¶
All endpoints return consistent error responses:
400 Bad Request¶
401 Unauthorized¶
404 Not Found¶
429 Too Many Requests¶
500 Internal Server Error¶
Security Notes¶
Path Validation¶
All endpoints validate paths to prevent directory traversal:
- Paths containing
..are rejected - Paths starting with
/are rejected - Only files within
src/content/albums/are accessible
Rate Limiting¶
Password endpoints are rate-limited:
- 10 attempts per IP
- 15-minute lockout after exceeding
- Rate limit cleared on successful login
Cookie Security¶
Authentication cookies have security flags:
httpOnly- Not accessible via JavaScriptsecure- HTTPS only (in production)sameSite: strict- CSRF protection- 24-hour expiration
Related¶
- Architecture - Technical overview
- Password Protection - Auth details