Imgen Cache API
A semantic image search and retrieval API. Describe what you are looking for in natural language and receive images ranked by relevance. Queries are matched against machine-generated visual descriptions of every image, the images themselves, and the generation prompts — then fused, reranked and diversified.
Semantic and lexical, fused
Dense vector retrieval, BM25 full-text and tag overlap are combined with reciprocal rank fusion, then reranked. Prose queries and raw generation tag soup both work.
Taxonomy filters
Narrow by character, model or style in any combination. Filters are applied inside the vector index, so recall does not degrade when you filter.
Stable URLs
Every result carries a direct full-resolution URL and a thumbnail URL. Both are stable and safe to embed or re-host.
Curated results only
The public API returns only human-reviewed images. Rejected and duplicate images are excluded, and near-identical batch siblings are capped so results stay varied.
Quick start
Every request carries your API key in an Authorization header. A minimal search:
curl -X POST https://example.com/api/search \
-H "Authorization: Bearer $IMGEN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query":"sunset over mountains","limit":5}'- Obtain an API key from your account owner.
POSTto /api/search with a natural-languagequery.- Render
thumbnailin grids andurlon detail views. - Populate filter controls from the taxonomy endpoints and cache them locally.
- Verify your integration interactively in the Playground.
Authentication
All endpoints authenticate with a bearer token:
Authorization: Bearer YOUR_API_KEYKeys are stored server-side only as SHA-256 digests and are compared in constant time.
A revoked key begins returning 401 immediately and cannot be reactivated.
Base URL
All paths in this reference are relative to a single origin:
https://example.com/apiRate limits & caching
| Endpoint | Limit | Window | Scope |
|---|---|---|---|
| /api/search | 240 requests | 60 s | API key + client IP |
| /api/generate | 120 requests | 60 s | API key + client IP |
| taxonomy endpoints | Not limited. Cached for 5 minutes at the edge. | ||
Exceeding a limit returns 429 with a Retry-After header.
Identical search requests are served from an edge cache; a served response carries
X-Cache: HIT. Treat cache headers as advisory.
Search
Search the library with a natural-language query. Returns a ranked list, most relevant first.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
| query | string | required | What you are looking for. Free-form prose works best; raw generation tag soup is accepted and normalised automatically. 1–2000 characters. |
| character | string | optional | Restrict to one character. See GET /api/characters. |
| model | string | optional | Restrict to one model identifier. |
| style | string | optional | Restrict to one style identifier. |
| search_in | string | optional | Which text surface the query is compared against: both,
caption or prompt. Default both. |
| limit | integer | optional | Maximum results, 1–50. Default 10. |
| only_top | boolean | optional | Restrict to images a human reviewer flagged as best-in-class. Default false. |
Example request
{
"query": "cinematic portrait at golden hour",
"character": "aya",
"search_in": "both",
"limit": 5
}Example response
{
"results": [
{
"id": "a1b2c3d4",
"gen_id": "g-8817",
"url": "https://cdn.example.com/08-26/2450c4bc/5c5be245.jpg",
"thumbnail": "https://cdn.example.com/08-26/2450c4bc/5c5be245_t.jpg",
"width": 832,
"height": 1216,
"character": "aya",
"model": "sdxl",
"style": "photoreal",
"prompt": "portrait, shallow depth of field, warm sunset light",
"caption": "A close-up portrait in warm golden-hour light with a soft blurred background.",
"score": 0.842,
"top": true
}
],
"count": 1
}score is a relative
relevance value; higher is more relevant. It is not calibrated across queries. Use it to order
results within a single response, never as an absolute threshold.Generate
Return the single best match for a prompt. Use this when you want “something that looks like this” rather than a list of candidates.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
| prompt | string | required | Natural-language description. Maximum 500 characters. |
| character | string | optional | Restrict to one character. |
| model | string | optional | Restrict to one model. |
| style | string | optional | Restrict to one style. |
| search_in | string | optional | both, caption or prompt. Default both. |
Example response
{
"image": {
"id": "a1b2c3d4",
"url": "https://cdn.example.com/08-26/2450c4bc/5c5be245.jpg",
"thumbnail": "https://cdn.example.com/08-26/2450c4bc/5c5be245_t.jpg",
"width": 832,
"height": 1216,
"character": "",
"model": "sdxl",
"style": "photoreal",
"prompt": "misty lake, golden dawn light, reflections on still water",
"caption": "A misty lake at dawn with pale golden light and still, reflective water."
}
}404 with {"error":"No match found"}. This is not a
server error — the filter combination was too narrow, or the library has no close match.
Do not retry.List characters
Character identifiers currently available as filter values.
{ "characters": ["aya", "mira", "noa"] }List models
{ "models": ["sdxl", "flux"] }List styles
{ "styles": ["photoreal", "illustration", "cinematic"] }The image object
Every image returned by the API shares this shape. Individual endpoints omit fields that are not relevant to their response.
| Field | Type | Description |
|---|---|---|
| id | string | Stable opaque identifier. Safe to store and reference. |
| gen_id | string | Upstream generation identifier. |
| url | string | Absolute URL to the full-resolution asset. |
| thumbnail | string | Absolute URL to the preview derivative. Preferred for grids. |
| width | integer | Full-resolution pixel width. |
| height | integer | Full-resolution pixel height. |
| character | string | Character identifier, or empty string. |
| model | string | Model identifier, or empty string. |
| style | string | Style identifier, or empty string. |
| prompt | string | The prompt the image was generated from. |
| edited_prompt | string | Curator override. Present only when set. |
| caption | string | Visual description. Suitable for alt text and display. |
| edited_caption | string | Curator override. Present only when set. Prefer this over caption. |
| score | number | Relative relevance. Present on search responses only. |
| top | boolean | Flagged best-in-class by a reviewer. Search responses only. |
Errors
Every error response is JSON and shares one shape:
{ "error": "human-readable description of what went wrong" }Branch on the HTTP status code. The error string is for logs and developer
display, and its wording may change without notice.
Status codes
| 200 | Success. A result body is returned. |
| 400 | Malformed body, or a required field was missing or invalid. |
| 401 | The Authorization header was missing, or the key is invalid or revoked. |
| 404 | Nothing matched. For generate, no image satisfied the prompt plus filters. |
| 429 | Rate limit exceeded. Honour Retry-After. |
| 502 | A transient upstream dependency failed. Safe to retry with backoff. |
| 500 | Unexpected server error. Contact support if it persists. |
429
and 502 with exponential backoff (500 ms, 1.5 s, 4 s). Never retry
4xx other than 429 — the request will not succeed without a
change.Code examples
Node.js
const BASE = 'https://example.com';
const KEY = process.env.IMGEN_API_KEY;
async function search(query, opts = {}) {
const res = await fetch(BASE + '/api/search', {
method: 'POST',
headers: {
Authorization: 'Bearer ' + KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
query,
character: opts.character,
model: opts.model,
style: opts.style,
search_in: opts.searchIn || 'both',
limit: opts.limit || 10,
}),
signal: AbortSignal.timeout(15000),
});
if (res.status === 429) {
throw new Error('rate limited; retry after ' + res.headers.get('Retry-After') + 's');
}
if (!res.ok) {
const err = await res.json().catch(() => ({ error: res.statusText }));
throw new Error('search failed (' + res.status + '): ' + err.error);
}
const { results } = await res.json();
return results;
}
for (const img of await search('sunset over mountains', { limit: 5 })) {
console.log(img.id, img.score.toFixed(3), img.thumbnail);
}Python
import os
import requests
BASE = "https://example.com"
KEY = os.environ["IMGEN_API_KEY"]
session = requests.Session()
session.headers.update({
"Authorization": f"Bearer {KEY}",
"Content-Type": "application/json",
})
def search(query, **opts):
r = session.post(
f"{BASE}/api/search",
json={
"query": query,
"character": opts.get("character"),
"model": opts.get("model"),
"style": opts.get("style"),
"search_in": opts.get("search_in", "both"),
"limit": opts.get("limit", 10),
},
timeout=15,
)
r.raise_for_status()
return r.json()["results"]
for img in search("sunset over mountains", limit=5):
print(img["id"], round(img["score"], 3), img["thumbnail"])Integration guidance
- Cache the taxonomy endpoints locally. They change infrequently.
- Keep
limitas low as your interface actually renders. Smaller responses are faster. - Use
thumbnailin grids; loadurlonly on detail or zoom. - Store
idwhen you need a stable reference across sessions. - Prefer
edited_captionovercaptionwhen both are present. - Always set a client-side timeout. 15 seconds is a sensible default.
- If you are probing this API as a generation cache, send the raw generation prompt verbatim: exact prompt matches are detected and short-circuited without any model inference.