REST API (routes/api.py): Three endpoints behind API key auth for programmatic image generation via presets — list presets, queue generation with optional overrides, and poll job status. Shared generation logic extracted from routes/presets.py into services/generation.py so both web UI and API use the same code path. Fallback covers: library index pages now show a random generated image at reduced opacity when no cover is assigned, instead of "No Image". Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
220 lines
5.1 KiB
Markdown
220 lines
5.1 KiB
Markdown
# GAZE REST API Guide
|
||
|
||
## Setup
|
||
|
||
1. Open **Settings** in the GAZE web UI
|
||
2. Scroll to **REST API Key** and click **Regenerate**
|
||
3. Copy the key — you'll need it for all API requests
|
||
|
||
## Authentication
|
||
|
||
Every request must include your API key via one of:
|
||
|
||
- **Header (recommended):** `X-API-Key: <your-key>`
|
||
- **Query parameter:** `?api_key=<your-key>`
|
||
|
||
Responses for auth failures:
|
||
|
||
| Status | Meaning |
|
||
|--------|---------|
|
||
| `401` | Missing API key |
|
||
| `403` | Invalid API key |
|
||
|
||
## Endpoints
|
||
|
||
### List Presets
|
||
|
||
```
|
||
GET /api/v1/presets
|
||
```
|
||
|
||
Returns all available presets.
|
||
|
||
**Response:**
|
||
|
||
```json
|
||
{
|
||
"presets": [
|
||
{
|
||
"preset_id": "example_01",
|
||
"slug": "example_01",
|
||
"name": "Example Preset",
|
||
"has_cover": true
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
### Generate Image
|
||
|
||
```
|
||
POST /api/v1/generate/<preset_slug>
|
||
```
|
||
|
||
Queue one or more image generations using a preset's configuration. All body parameters are optional — when omitted, the preset's own settings are used.
|
||
|
||
**Request body (JSON):**
|
||
|
||
| Field | Type | Default | Description |
|
||
|-------|------|---------|-------------|
|
||
| `count` | int | `1` | Number of images to generate (1–20) |
|
||
| `checkpoint` | string | — | Override checkpoint path (e.g. `"Illustrious/model.safetensors"`) |
|
||
| `extra_positive` | string | `""` | Additional positive prompt tags appended to the generated prompt |
|
||
| `extra_negative` | string | `""` | Additional negative prompt tags |
|
||
| `seed` | int | random | Fixed seed for reproducible generation |
|
||
| `width` | int | — | Output width in pixels (must provide both width and height) |
|
||
| `height` | int | — | Output height in pixels (must provide both width and height) |
|
||
|
||
**Response (202):**
|
||
|
||
```json
|
||
{
|
||
"jobs": [
|
||
{ "job_id": "783f0268-ba85-4426-8ca2-6393c844c887", "status": "queued" }
|
||
]
|
||
}
|
||
```
|
||
|
||
**Errors:**
|
||
|
||
| Status | Cause |
|
||
|--------|-------|
|
||
| `400` | Invalid parameters (bad count, seed, or mismatched width/height) |
|
||
| `404` | Preset slug not found |
|
||
| `500` | Internal generation error |
|
||
|
||
### Check Job Status
|
||
|
||
```
|
||
GET /api/v1/job/<job_id>
|
||
```
|
||
|
||
Poll this endpoint to track generation progress.
|
||
|
||
**Response:**
|
||
|
||
```json
|
||
{
|
||
"id": "783f0268-ba85-4426-8ca2-6393c844c887",
|
||
"label": "Preset: Example Preset – preview",
|
||
"status": "done",
|
||
"error": null,
|
||
"result": {
|
||
"image_url": "/static/uploads/presets/example_01/gen_1773601346.png",
|
||
"relative_path": "presets/example_01/gen_1773601346.png",
|
||
"seed": 927640517599332
|
||
}
|
||
}
|
||
```
|
||
|
||
**Job statuses:**
|
||
|
||
| Status | Meaning |
|
||
|--------|---------|
|
||
| `pending` | Waiting in queue |
|
||
| `processing` | Currently generating |
|
||
| `done` | Complete — `result` contains image info |
|
||
| `failed` | Error occurred — check `error` field |
|
||
|
||
The `result` object is only present when status is `done`. Use `seed` from the result to reproduce the exact same image later.
|
||
|
||
**Retrieving the image:** The `image_url` is a path relative to the server root. Fetch it directly:
|
||
|
||
```
|
||
GET http://<host>:5782/static/uploads/presets/example_01/gen_1773601346.png
|
||
```
|
||
|
||
Image retrieval does not require authentication.
|
||
|
||
## Examples
|
||
|
||
### Generate a single image and wait for it
|
||
|
||
```bash
|
||
API_KEY="your-key-here"
|
||
HOST="http://localhost:5782"
|
||
|
||
# Queue generation
|
||
JOB_ID=$(curl -s -X POST \
|
||
-H "X-API-Key: $API_KEY" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{}' \
|
||
"$HOST/api/v1/generate/example_01" | python3 -c "import sys,json; print(json.load(sys.stdin)['jobs'][0]['job_id'])")
|
||
|
||
echo "Job: $JOB_ID"
|
||
|
||
# Poll until done
|
||
while true; do
|
||
RESULT=$(curl -s -H "X-API-Key: $API_KEY" "$HOST/api/v1/job/$JOB_ID")
|
||
STATUS=$(echo "$RESULT" | python3 -c "import sys,json; print(json.load(sys.stdin)['status'])")
|
||
echo "Status: $STATUS"
|
||
if [ "$STATUS" = "done" ] || [ "$STATUS" = "failed" ]; then
|
||
echo "$RESULT" | python3 -m json.tool
|
||
break
|
||
fi
|
||
sleep 5
|
||
done
|
||
```
|
||
|
||
### Generate 3 images with extra prompts
|
||
|
||
```bash
|
||
curl -X POST \
|
||
-H "X-API-Key: $API_KEY" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{
|
||
"count": 3,
|
||
"extra_positive": "smiling, outdoors",
|
||
"extra_negative": "blurry"
|
||
}' \
|
||
"$HOST/api/v1/generate/example_01"
|
||
```
|
||
|
||
### Reproduce a specific image
|
||
|
||
```bash
|
||
curl -X POST \
|
||
-H "X-API-Key: $API_KEY" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"seed": 927640517599332}' \
|
||
"$HOST/api/v1/generate/example_01"
|
||
```
|
||
|
||
### Python example
|
||
|
||
```python
|
||
import requests
|
||
import time
|
||
|
||
HOST = "http://localhost:5782"
|
||
API_KEY = "your-key-here"
|
||
HEADERS = {"X-API-Key": API_KEY, "Content-Type": "application/json"}
|
||
|
||
# List presets
|
||
presets = requests.get(f"{HOST}/api/v1/presets", headers=HEADERS).json()
|
||
print(f"Available presets: {[p['name'] for p in presets['presets']]}")
|
||
|
||
# Generate
|
||
resp = requests.post(
|
||
f"{HOST}/api/v1/generate/{presets['presets'][0]['slug']}",
|
||
headers=HEADERS,
|
||
json={"count": 1},
|
||
).json()
|
||
|
||
job_id = resp["jobs"][0]["job_id"]
|
||
print(f"Queued job: {job_id}")
|
||
|
||
# Poll
|
||
while True:
|
||
status = requests.get(f"{HOST}/api/v1/job/{job_id}", headers=HEADERS).json()
|
||
print(f"Status: {status['status']}")
|
||
if status["status"] in ("done", "failed"):
|
||
break
|
||
time.sleep(5)
|
||
|
||
if status["status"] == "done":
|
||
image_url = f"{HOST}{status['result']['image_url']}"
|
||
print(f"Image: {image_url}")
|
||
print(f"Seed: {status['result']['seed']}")
|
||
```
|