Imagine API docs

ConsoleGet a key

Overview

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.

Authentication

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"

Client examples

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);

Domain security

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).

  • Allowed domains — empty list = any origin allowed. Add yours to lock the key down.
  • Allow localhost — off by default; enable while developing.
  • Allow server-to-server — requests without an Origin header (curl, backend code). Keep on for server integrations.
Requests from this docs page are same-origin, so they behave like server-to-server calls — they need “Allow server-to-server” enabled on your key.

Endpoints

GET

/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.

ParameterTypeDefaultDescription
qstring ≤200—Search text — matches title, description and tags
categorystring—Category slug, e.g. mountains, abstract
orientationenum—landscape · portrait · square
min_widthint—Minimum source width in pixels
min_heightint—Minimum source height in pixels
sortenumrelevancerelevance · newest · oldest
pageint1Page number (1–10000)
per_pageint30Results 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 }
}
GET

/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"]
  }
}
GET

/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 }
  ]
}
GETno auth

/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, … }
}

Rate limits & usage

PlanLimitPrice
Trial2,000 total requests, then 10/hrFree
Hobby1,200 req/hour$4.99
Pro5,000 req/hour$24.99
Plus10,000 req/hour$49.99
EnterpriseCustom limitsContact 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.

Errors

{
  "error": {
    "code": "RATE_LIMITED",
    "message": "Rate limit exceeded",
    "requestId": "0c370da5-…"
  }
}
StatusCodeMeaning
400INVALID_QUERY / INVALID_BODYBad or missing parameters
401UNAUTHORIZEDMissing, invalid, or revoked API key
403FORBIDDENOrigin not allowed, or account suspended
404NOT_FOUNDImage or resource does not exist
429RATE_LIMITEDPlan limit exceeded — check X-RateLimit-Reset
500INTERNAL_ERRORUnexpected 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