Introduction
What this API is, what it is not, and how versioning works.
The ImagineLab API is a REST interface over HTTPS. All endpoints live under https://api.imaginelab.art/api/v1, accept and return JSON, and authenticate with a per-integration bearer token. There is no SDK or GraphQL layer — standard HTTP clients work.
Generation is always asynchronous: every creation call returns immediately with a job ID, and the actual compute happens in the background. You poll the status endpoint until the job reaches completed or failed.
Versioning
The API version is part of the URL (/v1/). Breaking changes — field removals, renamed endpoints, changed authentication — will be released under a new version prefix, and the previous version will remain available for a deprecation window that we communicate by email to all active API integrators. Non-breaking additions (new fields, new generation types) can appear in the current version without notice.
Supported generation types
| type value | Lab | Output |
|---|---|---|
| image | Image Lab / Image Studio | Image file (JPEG/PNG/WebP) |
| video | Video Lab | Video file (MP4) |
| voice | Voice Lab | Audio file (MP3/WAV) |
| music | Music Lab | Audio file (MP3) |
| text | Writing Lab | Plain text or Markdown |
| infographic | Infographic Lab | Image file (PNG/SVG) |
| chat | Imagine Chat | Text (streamed or complete) |
Quick Start
Generate your first image in under two minutes.
- Upgrade to a plan that includes API access (Ultimate Visionary Studio or Titan Studio).
- Submit an API access request — our team issues a scoped key within one business day.
- Store the key in an environment variable (
IMAGINELAB_API_KEY). Never hard-code it. - Call
POST /generationsto create a job. - Poll
GET /generations/{id}/statusat a reasonable interval (2–5 s) untilcompleted. - Retrieve the output URLs from
GET /generations/{id}.
End-to-end example
import os, time, requests
BASE = "https://api.imaginelab.art/api/v1"
HEADERS = {
"Authorization": f"Bearer {os.environ['IMAGINELAB_API_KEY']}",
"Content-Type": "application/json",
}
# 1. Create a generation job
res = requests.post(f"{BASE}/generations", headers=HEADERS, json={
"generation_type": "image",
"model_name": "seedream-5-lite",
"prompt": "A neon city skyline at dusk, ultra-detailed, cinematic lighting",
"settings": {"aspect_ratio": "16:9"},
})
res.raise_for_status()
job = res.json()
job_id = job["id"]
print(f"Job created: {job_id} (status: {job['status']})")
# 2. Poll until complete
MAX_POLLS = 60 # 60 × 3 s = 3-minute timeout
for attempt in range(MAX_POLLS):
time.sleep(3)
status_res = requests.get(f"{BASE}/generations/{job_id}/status", headers=HEADERS)
status_res.raise_for_status()
status = status_res.json()
print(f" [{attempt+1}/{MAX_POLLS}] status={status['status']} progress={status.get('progress', 0)}%")
if status["status"] == "completed":
break
if status["status"] == "failed":
raise RuntimeError(f"Generation failed: {status.get('error_message', 'unknown')}")
else:
raise TimeoutError("Generation did not complete within the polling window.")
# 3. Fetch result
result = requests.get(f"{BASE}/generations/{job_id}", headers=HEADERS).json()
for output in result["outputs"]:
print("Output URL:", output["url"])import axios from 'axios';
const BASE = 'https://api.imaginelab.art/api/v1';
const headers = {
Authorization: `Bearer ${process.env.IMAGINELAB_API_KEY}`,
'Content-Type': 'application/json',
};
async function generateImage(prompt: string): Promise<string[]> {
// 1. Create
const { data: job } = await axios.post(`${BASE}/generations`, {
generation_type: 'image',
model_name: 'seedream-5-lite',
prompt,
settings: { aspect_ratio: '1:1' },
}, { headers });
// 2. Poll with exponential back-off
const startedAt = Date.now();
const TIMEOUT_MS = 3 * 60 * 1000; // 3 minutes
let delay = 2000;
while (Date.now() - startedAt < TIMEOUT_MS) {
await new Promise(r => setTimeout(r, delay));
delay = Math.min(delay * 1.5, 8000); // cap at 8 s
const { data: s } = await axios.get(`${BASE}/generations/${job.id}/status`, { headers });
if (s.status === 'completed') break;
if (s.status === 'failed') throw new Error(`Generation failed: ${s.error_message}`);
}
// 3. Result
const { data: result } = await axios.get(`${BASE}/generations/${job.id}`, { headers });
return result.outputs.map((o: { url: string }) => o.url);
}
generateImage('A serene forest at golden hour').then(console.log);Authentication
Every request must carry a valid bearer token. Keys are per-integration and revocable.
Pass your API key in the Authorization header on every request:
Authorization: Bearer YOUR_API_KEY
Key properties
- Keys are scoped — each key is tied to one integration and one account. A key cannot access another account's generations or credits.
- Keys are revocable at any time by your account owner or by our team. Revocation takes effect immediately.
- Keys do not expire automatically, but they are deactivated if the associated plan lapses.
- A single account may hold multiple keys (one per integration). Request additional keys via support.
Never expose your key client-side
API keys must live on your server only. Do not embed them in browser JavaScript, mobile app binaries, public repositories, or CI/CD logs. If a key is leaked, contact support@imaginelab.art immediately so we can revoke and reissue it. Leaked keys can be used to drain your EDT balance.Testing without a key
The/v1/ping endpoint returns the API version and timestamp without authentication. Use it to verify network connectivity and TLS configuration before your key is issued.Rate Limits
Limits protect system stability for all users. Exceed them and you receive 429.
| Scope | Limit | Window |
|---|---|---|
| Global (per API key) | 120 requests | 1 minute |
| POST /generations | 30 creates | 1 minute |
| GET /generations/*/status | 300 polls | 1 minute |
| Concurrent active jobs | 10 jobs | at any time |
| Daily generation budget | Plan-dependent | 24 hours (UTC) |
Rate limit headers (X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset) are included in every response. After a 429, respect the Retry-After header.
Polling best practice
Poll status at 2–5 second intervals, not faster. Aggressive polling counts against your rate limit and does not speed up generation. For high-volume pipelines, increase the interval progressively (see Best Practices for an exponential back-off example).Generations
Create, track, and retrieve async generation jobs.
Health check
No auth required. Returns API version and server timestamp.
{ "status": "ok", "version": "v1", "timestamp": "2026-08-23T09:00:00Z" }List available models
Returns all models your key is authorised to use, grouped by generation type.
{
"models": [
{
"name": "seedream-5-lite",
"generation_type": "image",
"provider": "ByteDance",
"available": true
},
...
]
}Create a generation
Request body
| Field | Type | Description |
|---|---|---|
| generation_typerequired | string | One of: image · video · voice · music · text · infographic · chat |
| model_namerequired | string | Exact model name from GET /generations/models |
| promptrequired | string | The creative instruction. Max 4,000 characters for most types. |
| settings | object | Type-specific parameters (aspect_ratio, duration, voice_id, etc.). See Generation Types. |
| project_id | string|null | Optional project tag. Useful for grouping generations in your dashboard. |
| webhook_url | string|null | If provided, a POST is sent to this URL when the job finishes. Must be HTTPS. |
{
"generation_type": "image",
"model_name": "seedream-5-lite",
"prompt": "A neon city skyline at dusk, ultra-detailed, cinematic lighting",
"settings": {
"aspect_ratio": "16:9",
"quality": "high"
}
}curl -X POST https://api.imaginelab.art/api/v1/generations \
-H "Authorization: Bearer $IMAGINELAB_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"generation_type": "image",
"model_name": "seedream-5-lite",
"prompt": "A neon city skyline at dusk",
"settings": { "aspect_ratio": "16:9" }
}'{
"id": "gen_8f3a21c7",
"generation_type": "image",
"model_name": "seedream-5-lite",
"prompt": "A neon city skyline at dusk, ultra-detailed, cinematic lighting",
"status": "pending",
"credit_cost": 5,
"settings": { "aspect_ratio": "16:9", "quality": "high" },
"created_at": "2026-08-23T09:01:00Z",
"outputs": []
}Poll status
Lightweight endpoint. Poll at 2–5 second intervals.
{
"id": "gen_8f3a21c7",
"status": "processing",
"progress": 62,
"estimated_remaining_seconds": 14
}{
"id": "gen_8f3a21c7",
"status": "completed",
"progress": 100,
"estimated_remaining_seconds": 0
}Fetch result
Returns the full generation record including output URLs. Available once status is 'completed'.
{
"id": "gen_8f3a21c7",
"generation_type": "image",
"status": "completed",
"credit_cost": 5,
"outputs": [
{
"url": "https://cdn.imaginelab.art/outputs/gen_8f3a21c7/result.png",
"mime_type": "image/png",
"width": 1920,
"height": 1080,
"expires_at": "2026-09-22T09:01:00Z"
}
],
"created_at": "2026-08-23T09:01:00Z",
"completed_at": "2026-08-23T09:01:24Z"
}Output URL expiry
Output URLs are signed and expire after 30 days. Download and store the files in your own infrastructure before the URL expires. Expired URLs return 403. We do not re-issue URLs for deleted or expired outputs.List generations
Paginated history. Default limit: 20. Max: 100.
{
"generations": [ { "id": "gen_8f3a21c7", "status": "completed", ... }, ... ],
"cursor": "eyJpZCI6MTgzMH0=",
"has_more": true
}Generation Types
Type-specific settings passed in the 'settings' object of a create request.
imageImage Generation
| Field | Type | Description |
|---|---|---|
| aspect_ratio | "1:1" | "16:9" | "4:3" | "3:4" | "9:16" | Output aspect ratio. Default: 1:1. |
| quality | "standard" | "high" | "ultra" | Rendering quality. Higher = more credits. |
| style | string | Optional style hint (e.g. "photorealistic", "anime"). Model-dependent. |
| negative_prompt | string | Elements to avoid. Supported by select models. |
videoVideo Generation
| Field | Type | Description |
|---|---|---|
| aspect_ratio | "16:9" | "9:16" | "1:1" | "4:3" | Video frame ratio. |
| duration | "5s" | "10s" | "15s" | "30s" | Clip length. Available options vary by model. |
| resolution | "720p" | "1080p" | "4K" | Output resolution. Model-dependent. |
| input_image | string (URL) | Source image for image-to-video workflows. |
voiceVoice / TTS
| Field | Type | Description |
|---|---|---|
| voice_id | string | Voice identifier from GET /voices. Required. |
| language | "en-US" | "en-GB" | … | BCP-47 language tag. Default: en-US. |
| stability | number 0–1 | Voice consistency (0 = expressive, 1 = stable). Default: 0.75. |
| speed | number 0.5–2.0 | Playback speed multiplier. Default: 1.0. |
musicMusic Generation
| Field | Type | Description |
|---|---|---|
| genre | string | Musical genre (e.g. "Electronic Pop", "Orchestral", "Lo-Fi"). |
| mood | string | Emotional tone (e.g. "Uplifting", "Melancholic", "Energetic"). |
| duration | "15s" | "30s" | "60s" | "120s" | Track length. Generation time varies by model. |
| instrumental | boolean | Set true to suppress vocals. Default: false. |
| lyrics | string | Custom lyrics for lyric-to-song workflows. Mutually exclusive with prompt. |
textWriting (text)
| Field | Type | Description |
|---|---|---|
| tone | string | "Professional" | "Casual" | "Persuasive" | "Academic" | custom. |
| length | "short" | "medium" | "long" | Approximate output length. |
| output_language | string | BCP-47 code. Default: en-US. |
| writing_task | string | Task hint: "blog-post" | "email" | "product-description" | "paraphrase" | "humanize". |
infographicInfographic
| Field | Type | Description |
|---|---|---|
| infographic_type | "data-overview" | "comparison" | "timeline" | "process-flow" | "mind-map" | Visual layout type. |
| color_scheme | string | Palette hint (e.g. "corporate-blue", "vibrant"). Optional. |
| aspect_ratio | "1:1" | "4:3" | "16:9" | "A4" | Canvas ratio. |
Credits & Wallet
Every generation spends EDT from your wallet. Insufficient balance returns 402.
Returns current balance and plan information.
{
"balance": 1240,
"plan": "Ultimate Visionary Studio",
"plan_edt_reset_at": "2026-09-01T00:00:00Z",
"topup_balance": 500,
"currency": "EDT"
}Lists available top-up packages for the authenticated account.
Paginated ledger of credit events (generation spends, top-ups, refunds).
Credit cost reference
| Generation type | Base cost (EDT) | Variable factors |
|---|---|---|
| text | 2–4 | Model, output length, writing task |
| voice | 3–8 | Character count, model, language |
| image | 5–20 | Model, quality, resolution |
| music | 8–18 | Duration, model, lyrics complexity |
| infographic | 10–25 | Model, data complexity, type |
| video | 15–60 | Duration, resolution, model, input type |
The exact cost for each job is confirmed in the create response (credit_cost) and reserved immediately. If the job fails, credits are refunded automatically.
Error Reference
The API uses standard HTTP status codes. Every error body contains a machine-readable 'code' and a human-readable 'detail'.
{
"code": "INSUFFICIENT_CREDITS",
"detail": "Your wallet balance (3 EDT) is below the required cost (5 EDT).",
"request_id": "req_f7c3a9b1"
}| HTTP | code | Meaning & action |
|---|---|---|
| 200 / 201 | SUCCESS | Request succeeded. For creates, the job is queued. |
| 400 | VALIDATION_ERROR | Missing or invalid field. Fix the request body before retrying. |
| 401 | UNAUTHORIZED | Token is missing, malformed, or revoked. Check the Authorization header. |
| 402 | INSUFFICIENT_CREDITS | Balance too low. Top up your wallet or wait for the plan reset. |
| 403 | FORBIDDEN | Action not permitted for your key scope or plan tier. |
| 404 | NOT_FOUND | Generation ID does not exist or belongs to a different account. |
| 409 | CONFLICT | Duplicate request detected. Check if the job already exists. |
| 422 | UNPROCESSABLE | Body is valid JSON but semantically incorrect (e.g. unsupported model for type). |
| 429 | RATE_LIMITED | Too many requests. Respect the Retry-After header before retrying. |
| 500 | SERVER_ERROR | Internal error. Retry with back-off. If persistent, contact support with request_id. |
| 502 / 503 | UPSTREAM_ERROR | Upstream AI provider unavailable. Retry with back-off. |
Failed generation
If a job reaches status: "failed", the full record includes an error_message field explaining the failure. Credits reserved for a failed job are automatically refunded to your wallet — no manual action required.
{
"id": "gen_9b2c11f4",
"status": "failed",
"error_message": "The upstream model returned an empty result. The prompt may contain content that cannot be processed.",
"credit_cost": 0,
"outputs": []
}Best Practices
Follow these to build a reliable, secure, and cost-efficient integration.
Security
- Server-side only. Your API key must never reach the browser. Proxy all ImagineLab calls through your own backend.
- Rotate keys periodically. Even if not compromised, rotate every 90 days as part of standard hygiene.
- Scope your keys. Use one key per integration (one for dev, one for staging, one for prod). Revoke individually if one is compromised.
- Validate webhook signatures. If you use
webhook_url, verify theX-ImagineLab-Signatureheader before processing the payload. - Do not log prompts. User-submitted prompts may contain personal data. Follow your own data-protection obligations before persisting them.
Polling & retry
Use exponential back-off when polling and when handling transient errors (429, 502, 503). A fixed 2-second interval at scale will hit rate limits.
import time, math, requests
def poll_until_done(base_url, job_id, headers, timeout=300):
"""Poll status with exponential back-off. Raises on timeout or failure."""
deadline = time.monotonic() + timeout
delay = 2.0
attempt = 0
while time.monotonic() < deadline:
r = requests.get(f"{base_url}/generations/{job_id}/status", headers=headers)
if r.status_code == 429:
wait = int(r.headers.get("Retry-After", delay))
time.sleep(wait)
continue
r.raise_for_status()
data = r.json()
if data["status"] == "completed":
return data
if data["status"] == "failed":
raise RuntimeError(data.get("error_message", "Generation failed"))
delay = min(delay * 1.5, 10.0) # cap at 10 s
attempt += 1
time.sleep(delay)
raise TimeoutError(f"Job {job_id} did not complete within {timeout}s")Credit management
- Call
GET /credits/walletat app startup and cache the balance. Refresh it after every generation. - Gate generation calls client-side if the remaining balance is too low to avoid a round trip that results in a 402.
- Trust the
credit_costin the create response as the definitive cost for that job — not the table in this documentation, which is approximate.
Output handling
- Download outputs within 30 days. Signed CDN URLs expire. Store the files in your own storage as soon as possible.
- Always check the
mime_typefield before rendering — some model/setting combinations produce different file formats. - For video outputs, serve from your own CDN rather than hot-linking the ImagineLab CDN URL. Hot-linking at scale may be rate-limited.
Usage Policy
Your API integration is subject to the same Terms of Service as the web platform — plus the constraints below.
Prohibited use — these will result in immediate key revocation
- Generating or storing non-consensual intimate imagery (NCII) or content that sexually exploits minors (CSAM).
- Generating deepfakes of identifiable private individuals without clear, documented consent.
- Using the API to train, fine-tune, or distill any AI model without a written enterprise license.
- Reselling raw API access to third parties (white-label integrations require a separate enterprise agreement).
- Automated abuse of the generation pipeline — bots, credential stuffing, or any attempt to circumvent credit accounting.
- Generating content that promotes violence, terrorism, or incites hatred against protected groups.
- Scraping, reverse-engineering, or probing internal API behavior beyond what is documented here.
Content rights via the API
Outputs generated through the API carry the same rights as those generated through the web platform — governed by your current plan's Terms of Service. Commercial-use rights apply where your plan includes them. The API does not grant additional rights beyond what your subscription covers.
Data handling
Prompts, settings, and generation metadata are stored on ImagineLab infrastructure and governed by the Privacy Policy. If your users' data flows through your integration, you are responsible for ensuring they have consented under your own privacy framework. Do not transmit personally identifiable information (PII) inside prompt text unless your users have explicitly consented.
Reporting abuse
If you discover that your API key has been misused — whether by your team or a third party — revoke access immediately by contacting support@imaginelab.art. Include your account email and any relevant request IDs. We will investigate and can restore service once the issue is contained.
Ready to integrate?
API access is available on Ultimate Visionary Studio and Titan Studio plans. Contact our team to request a key and get started.
For urgent issues, email support@imaginelab.art with subject line API: for priority routing.
