Imagine is a hosted image catalog API: search curated photos from Unsplash, Pexels and Pixabay, served from our CDN with consistent size variants. All endpoints are JSON over HTTPS.
Base URL https://imagine.rtools.in/api/v1 Local dev http://localhost:3000/api/v1 Every request: Authorization: Bearer imk_live_…
Responses use a consistent envelope — data on success, error on failure — and every response carries an x-request-id header for support.
Every account gets one API key at signup — send it as a Bearer token on each request. Keys are stored as hashes server-side; the plaintext is shown once, so copy it when you see it. Rotate it anytime from the dashboard — the old key stops working immediately.
curl -H "Authorization: Bearer imk_live_…" \ "https://imagine.rtools.in/api/v1/images/search?q=mountains"
The API is plain JSON over HTTPS — any HTTP client works. Keep the key in an environment variable, send it as a Bearer token, and read the error envelope on non-2xx responses. For server-side calls, enable “Allow server-to-server” on your key; for browser apps, add your domain to the allowlist instead.
// Node 18+ — built-in fetch, no dependencies const API_KEY = process.env.IMAGINE_API_KEY; // imk_live_… const BASE = "https://imagine.rtools.in"; async function searchImages(query, page = 1) { const url = new URL("/api/v1/images/search", BASE); url.searchParams.set("q", query); url.searchParams.set("per_page", "10"); url.searchParams.set("page", String(page)); const res = await fetch(url, { headers: { Authorization: "Bearer " + API_KEY }, }); const body = await res.json(); if (!res.ok) { // envelope: { error: { code, message, requestId } } const e = body.error ?? {}; throw new Error(res.status + " " + (e.code ?? "") + ": " + (e.message ?? res.statusText)); } return body; // { data: ImageDTO[], pagination: {...} } } const { data, pagination } = await searchImages("mountains"); for (const img of data) { console.log(img.title, "→", img.urls.medium ?? img.urls.original); } console.log(pagination.total + " results, page " + pagination.page);
Keys can be locked to your domains. On browser requests the API checks the Origin/Referer against your allowlist — listed domains match themselves and all subdomains (example.com covers app.example.com).
/api/v1/images/search
Search images
Full-text and filtered search over the catalog. Results are paginated and include only the variants that actually exist for each image.
| Parameter | Type | Default | Description |
|---|---|---|---|
| q | string ≤200 | — | Search text — matches title, description and tags |
| category | string | — | Category slug, e.g. mountains, abstract |
| orientation | enum | — | landscape · portrait · square |
| min_width | int | — | Minimum source width in pixels |
| min_height | int | — | Minimum source height in pixels |
| sort | enum | relevance | relevance · newest · oldest |
| page | int | 1 | Page number (1–10000) |
| per_page | int | 30 | Results per page (1–100) |
Example
GET /api/v1/images/search?q=mountains&orientation=landscape&per_page=5
Response
{
"data": [
{
"id": "9f2c…",
"title": "Mountain lake at dawn",
"width": 5472, "height": 3648,
"orientation": "landscape",
"color": "#4a6b8a",
"thumbnailUrl": "https://cdn…/9f2c/thumb.webp",
"previewUrl": "https://cdn…/9f2c/medium.webp",
"urls": {
"thumb": "…/thumb.webp", "small": "…/small.webp",
"medium": "…/medium.webp", "large": "…/large.webp",
"original": "…/original.jpg"
},
"author": { "name": "Jane Doe", "url": "https://…" },
"source": { "name": "unsplash", "url": "https://…" },
"license": "unsplash",
"tags": ["mountains", "lake", "unsplash"]
}
],
"pagination": { "page": 1, "perPage": 5, "total": 812, "hasNext": true }
}/api/v1/images/{id}
Get image
Fetch a single image by its catalog UUID. Same object shape as a search result item.
{id} — Image UUID from a search result
No parameters.
Example
GET /api/v1/images/9f2c9b1e-…
Response
{
"data": {
"id": "9f2c9b1e-…",
"title": "Mountain lake at dawn",
"width": 5472, "height": 3648,
"orientation": "landscape",
"urls": { "thumb": "…", "small": "…", "medium": "…", "large": "…", "original": "…" },
"author": { "name": "Jane Doe", "url": "https://…" },
"license": "unsplash",
"tags": ["mountains", "lake"]
}
}/api/v1/categories
List categories
Every catalog category with its image count. Use the slug as the category filter in search.
No parameters.
Example
GET /api/v1/categories
Response
{
"data": [
{ "id": "…", "slug": "mountains", "name": "Mountains",
"description": null, "imageCount": 612 }
]
}/api/v1/health
Health check
Service status and which subsystems are configured. No authentication required.
No parameters.
Example
GET /api/v1/health
Response
{
"status": "ok", "service": "imagine", "version": "1.0.0",
"configured": { "supabase": true, "redis": true, "storage": true, … }
}| Plan | Limit | Price |
|---|---|---|
| Trial | 2,000 total requests, then 10/hr | Free |
| Hobby | 1,200 req/hour | $4.99 |
| Pro | 5,000 req/hour | $24.99 |
| Plus | 10,000 req/hour | $49.99 |
| Enterprise | Custom limits | Contact sales |
Every response reports your quota:
x-ratelimit-limit 1200 # hourly cap x-ratelimit-remaining 1187 # left this window x-ratelimit-reset 1735689600 # epoch seconds x-usage-limit 2000 # trial: lifetime cap x-usage-remaining 437 # trial: lifetime remaining
A 429 means you hit the cap — wait for x-ratelimit-reset or upgrade. Trial keys keep working after 2,000 requests at a 10/hr trickle so your integration never hard-fails during testing.
{
"error": {
"code": "RATE_LIMITED",
"message": "Rate limit exceeded",
"requestId": "0c370da5-…"
}
}| Status | Code | Meaning |
|---|---|---|
| 400 | INVALID_QUERY / INVALID_BODY | Bad or missing parameters |
| 401 | UNAUTHORIZED | Missing, invalid, or revoked API key |
| 403 | FORBIDDEN | Origin not allowed, or account suspended |
| 404 | NOT_FOUND | Image or resource does not exist |
| 429 | RATE_LIMITED | Plan limit exceeded — check X-RateLimit-Reset |
| 500 | INTERNAL_ERROR | Unexpected server error — retry with requestId |
Request console
API KEY
Shown once at signup / regeneration — manage at /dashboard/key
GET /api/v1/images/search?q=mountains&per_page=5