Creative SaaS

API Documentation

Generate creatives programmatically. Every generated image spends one credit from your subscription — the same balance the web app uses.

Authentication

Create an API key in Settings → API Keys. Pass it as a Bearer token on every request:

Authorization: Bearer cs_live_xxxxxxxxxxxxxxxxxxxx

Keys are shown once at creation. Store them securely. Revoke a key anytime from Settings.

Generate creatives

Generation is asynchronous. You POST a request, get a jobId back immediately, then poll the job until it completes.

1. Create a job

POST creative-saas.vercel.app/api/v1/generate

Body parameters:

Optional header Idempotency-Key: repeating a request with the same key returns the original job instead of creating a new one (and does not double-spend credits).

curl -X POST creative-saas.vercel.app/api/v1/generate \
  -H "Authorization: Bearer cs_live_xxxxxxxx" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: my-unique-id-123" \
  -d '{"claim":"Best protein supplements","language":"EN","count":3}'

Response (202 Accepted):

{
  "jobId": "f1e2d3c4-...",
  "status": "queued",
  "pollUrl": "/api/v1/jobs/f1e2d3c4-...",
  "result": { "requested": 3, "produced": 0, "progress": { "done": 0, "total": 3 } }
}

2. Poll the job

GET creative-saas.vercel.app/api/v1/jobs/{jobId}

curl creative-saas.vercel.app/api/v1/jobs/f1e2d3c4-... \
  -H "Authorization: Bearer cs_live_xxxxxxxx"

Poll every ~2 seconds. status moves through queued → processing → completed (or partial / failed).

{
  "jobId": "f1e2d3c4-...",
  "status": "completed",
  "creditsConsumed": 3,
  "result": {
    "requested": 3,
    "produced": 3,
    "failed": 0,
    "images": ["https://.../image_1.png", "https://.../image_2.png", "..."],
    "formats": ["F1", "F4", "F10"],
    "failures": []
  }
}

Status values

Example — Python

import time, requests

BASE = "creative-saas.vercel.app"
KEY  = "cs_live_xxxxxxxx"
headers = {"Authorization": f"Bearer {KEY}"}

job = requests.post(
    f"{BASE}/api/v1/generate",
    headers=headers,
    json={"claim": "Best protein supplements", "language": "EN", "count": 3},
).json()

job_id = job["jobId"]
while True:
    time.sleep(2)
    j = requests.get(f"{BASE}/api/v1/jobs/{job_id}", headers=headers).json()
    if j["status"] in ("completed", "partial", "failed"):
        print(j["result"])
        break

Example — Node.js

const BASE = "creative-saas.vercel.app";
const KEY = "cs_live_xxxxxxxx";
const headers = { Authorization: `Bearer ${KEY}` };

const sleep = (ms) => new Promise((r) => setTimeout(r, ms));

const start = await fetch(`${BASE}/api/v1/generate`, {
  method: "POST",
  headers: { ...headers, "Content-Type": "application/json" },
  body: JSON.stringify({ claim: "Best protein supplements", language: "EN", count: 3 }),
}).then((r) => r.json());

let job;
do {
  await sleep(2000);
  job = await fetch(`${BASE}/api/v1/jobs/${start.jobId}`, { headers })
    .then((r) => r.json());
} while (["queued", "processing"].includes(job.status));

console.log(job.result);

Errors

Rate limits are governed by your credit balance — you can't spend faster than you have credits. Need higher throughput? Upgrade to Business or contact us.