Creativly API. Make it part of your app.

Generate images, animate references and edit video.
One API key. Your models, projects and existing credits.

See what you can build
REST API · Runs on your serverYour Creativly account
  1. 1

    Get your API key

    Open your account menu, choose Profile & Usage → API Keys, and generate your key.

    Keep it on your server as CREATIVLY_API_KEY. Your existing credits cover generation.

    Your key is shown once.
    Rotate or revoke it whenever you need.
  2. 2

    Make something

    A model, a prompt and a credit cap. No project setup required.

    Create an image
    curl "https://api-v2.creativly.ai/v1/generations" \
      -H "Authorization: Bearer $CREATIVLY_API_KEY" \
      -H "Content-Type: application/json" \
      -H "Idempotency-Key: $(uuidgen)" \
      -d '{
      "model": "nano-banana-2.1",
      "prompt": "A sky-blue espresso cup on an ivory plinth",
      "max_credits": 1
    }'
  3. 3

    Bring the result back

    The response returns a batch_id. Check its status, then retrieve each finished asset.

    Status → original file
    GET /v1/jobs/{batch_id}
    GET /v1/assets/{asset_id}

    Get a private playback URL and the full original download. Your work also stays saved in Creativly.

One API. Image and video. Your existing balance.Markdown guide Creating in an AI assistant? Use MCP
Build with Creativly

One request. So many directions.

Start from a prompt, an image or a video. The same workflow takes you from an idea to the finished file.

POST /v1/generationsExample request

An idea. A finished campaign.

Describe the shot and choose your model. Get an image you can download, edit or build on.

JSON body
{
  "model": "nano-banana-2.1",
  "prompt": "Create a launch image for a moss-green trail shoe on rain-dark volcanic rock. Give it a strong editorial feel, with a bright orange lace and low mist behind it.",
  "max_credits": 1
}
Set a credit cap. Keep your original. Continue in the same session.
Image previewIllustrative example
A moss-green trail shoe with orange laces on wet volcanic rock, with low mist and pine trees behind.
Trail launchNano Banana 2.1
Existing Creativly examples. Request bodies illustrate the API; previewing never submits a job.
From your own file

Upload it. Edit it. Bring it back.

A complete workflow for a product photo or a video clip. Private references in, a finished original out.

STEP 1 OF 5

Start with your own file.

Reserve a private upload, then send the exact original bytes to its signed URL. Keep your API key out of that storage request.

JPEG, PNG, WebP or GIF · Up to 10 MiB

Each API request needs your Bearer key. POST bodies use JSON. The storage PUT uses only its signed ticket.
Image edit · HTTP example
POST /v1/uploads
Idempotency-Key: your-stable-upload-id

{
  "filename": "product.png",
  "content_type": "image/png",
  "size_bytes": 123456
}

PUT <upload_url>
# Original bytes + the returned upload headers
See the complete runnable Python workflow Standard library · Python 3.8+

Save this as edit.py next to product.png and set CREATIVLY_API_KEY on your server. Running it submits one edit within the shown cap; previewing this page spends nothing. Keep the printed request ID for an exact retry after a timeout.

Upload → edit → download
import json, os, time, uuid
from pathlib import Path
from urllib.request import Request, urlopen

BASE = "https://api-v2.creativly.ai/v1"
KEY = os.environ["CREATIVLY_API_KEY"]
RUN = os.environ.get("CREATIVLY_REQUEST_ID") or str(uuid.uuid4())
print("Request ID (reuse if retrying):", RUN, flush=True)
FILE = Path("product.png")
MIME = "image/png"
CAP = 1  # Ceiling, not the quoted price.

def api(method, path, payload=None, key=None):
    headers = {"Authorization": "Bearer " + KEY,
               "Content-Type": "application/json"}
    if key:
        headers["Idempotency-Key"] = key
    data = json.dumps(payload).encode() if payload is not None else None
    request = Request(BASE + path, data=data, headers=headers, method=method)
    with urlopen(request, timeout=75) as response:
        return json.load(response)

# 1. Reserve the original file. Uploading does not spend generation credits.
raw = FILE.read_bytes()
ticket = api("POST", "/uploads", {
    "filename": FILE.name, "content_type": MIME, "size_bytes": len(raw)
}, key=RUN + "-upload")

# Send only the returned upload headers. Never send your API key to storage.
if ticket.get("upload_url"):
    put = Request(ticket["upload_url"], data=raw,
                  headers=ticket["headers"], method=ticket["method"])
    with urlopen(put, timeout=120) as response:
        response.read()
upload_id = ticket["upload_id"]
api("POST", "/uploads/" + upload_id + "/complete", {})

# 2. Wait for moderation. A stored file is not yet an editable reference.
deadline = time.monotonic() + 600
while True:
    source = api("GET", "/uploads/" + upload_id)
    if source["ready"]:
        break
    if (source["status"] in ("failed", "expired", "blocked")
            or source["moderation_status"] in ("failed", "blocked", "rejected")):
        raise RuntimeError("This upload cannot be used.")
    if time.monotonic() > deadline:
        raise TimeoutError("Moderation is pending. Resume using the upload ID.")
    time.sleep(source.get("poll_after_seconds") or 3)

# 3. Quote and submit one edit. The original stays intact.
edit = {
    "model": "nano-banana-2.1",
    "operation": "edit",
    "source_asset_id": source["asset_id"],
    "prompt": "Make the background warm terracotta. Keep the product, label and composition.",
    "max_credits": CAP,
}
quote = api("POST", "/quotes", edit)
print("Quoted credits:", quote["credits"], flush=True)
# Running this script submits the edit within CAP. For a preview, stop here.
receipt = api("POST", "/generations", edit, key=RUN + "-edit")
batch_id = receipt["batch_id"]
print("Accepted job:", batch_id, flush=True)

# 4. Follow the server polling interval. Never submit again to check progress.
deadline = time.monotonic() + 1800
while True:
    job = api("GET", "/jobs/" + batch_id)
    if job["terminal"]:
        break
    if time.monotonic() > deadline:
        raise TimeoutError("Job still running. Resume with GET /jobs/" + batch_id)
    time.sleep(job.get("poll_after_seconds") or 10)

# 5. Retrieve every completed result and download its full original.
for run in job["runs"]:
    if run["status"].lower() != "completed":
        raise RuntimeError(run.get("error") or "Generation did not complete.")
    for result in run["assets"]:
        asset = api("GET", "/assets/" + result["asset_id"])
        name = Path(asset["filename"]).name
        output = Path("creativly-" + result["asset_id"] + "-" + name)
        with urlopen(asset["download_url"], timeout=120) as response:
            with output.open("wb") as handle:
                while chunk := response.read(1024 * 1024):
                    handle.write(chunk)
        print("Saved:", output)
Keep your upload and job IDs to resume work. Signed download links expire; saved assets do not.Read the Markdown guide

Bring private references.

Upload an image or video, wait for moderation, then use it in a generation or edit.

Upload workflow

Choose your model.

Discover available models, supported operations and settings from the live catalog.

Models and parameters

Keep spending predictable.

Quote before you generate, set a maximum cost and retry safely with the same request key.

Credits and retries
THE DETAILS

A small API. The whole workflow.

From your first request to the original download. Everything you need to build on Creativly.

One key. Your existing balance.

Open your account menu in Creativly, choose API Keys, then Generate API key. Copy it once and keep it on your server. Regenerating or revoking it stops the old key immediately; accepted jobs keep running.

The separate BYOK tab is for your provider credentials. Those are not your Creativly API key. This REST API uses platform billing from your Creativly credit balance.

Authorization: Bearer YOUR_CREATIVLY_API_KEY
Content-Type: application/json

Use HTTPS. Never put your key in public browser code, a repository or a URL.

Your first image is one request.

Choose a model, describe the image and set a credit cap. You do not need to create a project, start a session or request a quote first. Creativly reuses your API project and session unless you provide your own IDs.

curl https://api-v2.creativly.ai/v1/generations \
  -H "Authorization: Bearer $CREATIVLY_API_KEY" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: my-first-image-001' \
  -d '{
    "model": "nano-banana-2.1",
    "prompt": "A sky-blue espresso cup on an ivory plinth, soft studio light",
    "max_credits": 1
  }'

A successful request returns 202 with batch_id, run_ids, project_id, session_id, the credits debited and a link to the saved session. Generation continues in the background.

Pick the model. Keep the interface.
GET https://api-v2.creativly.ai/v1/models?media_type=image
GET https://api-v2.creativly.ai/v1/models/gemini-omni-1.1-flash-preview

The catalog comes from Creativly’s current model registry and your account access. Model details report supported operations, reference inputs and the parameters available through this API. Availability can depend on your plan.

Place settings inside parameters. The current API supports size, aspect_ratio, duration, resolution, quality, audio_enabled and seed where the selected model supports them. Read that model’s allowed values and defaults. Other provider-specific parameters are rejected rather than silently ignored.

count requests one to four generation outputs. Edits take one source and return one output. Omit model for Nano Banana 2.1 images; choose a video model explicitly for video.

Animate an image. Edit what you have.

Use owned image asset IDs as references. The source’s project and session are reused when you omit them, so images and derived videos can stay together.

POST https://api-v2.creativly.ai/v1/generations
{
  "model": "gemini-omni-1.1-flash-preview",
  "prompt": "A gentle camera push toward the cup",
  "image_asset_ids": ["YOUR_IMAGE_ASSET_ID"],
  "parameters": { "duration": 4, "resolution": "360p" },
  "max_credits": 4
}

For an edit, use the same endpoint with operation: "edit" and source_asset_id. The source can be a completed generation or a clean upload. Use a compatible image or video model; the original stays intact.

POST https://api-v2.creativly.ai/v1/generations
{
  "model": "nano-banana-2.1",
  "operation": "edit",
  "source_asset_id": "YOUR_IMAGE_ASSET_ID",
  "prompt": "Make the cup terracotta red; preserve everything else",
  "max_credits": 1
}

Video edits use the source’s verified duration, with a current maximum of 30 seconds. Check the selected model’s capabilities and quote before choosing a cap. All POST generation examples also need the authorization, JSON and idempotency headers shown above.

Bring your own private references.

Reserve a file, upload its exact bytes directly to the returned signed URL, then complete it. Images can be up to 10 MiB; videos up to 50 MiB. Your account’s storage limit still applies. Uploading does not spend generation credits.

POST https://api-v2.creativly.ai/v1/uploads
Idempotency-Key: reference-photo-001
{ "filename": "product.jpg", "content_type": "image/jpeg", "size_bytes": 123456 }

PUT <upload_url>  # exact file bytes, using the returned headers
POST https://api-v2.creativly.ai/v1/uploads/<upload_id>/complete
{}
GET https://api-v2.creativly.ai/v1/uploads/<upload_id>

Wait for ready: true after moderation, then use the returned asset_id. The reservation lasts two hours. Accepted types are JPEG, PNG, WebP, GIF, MP4, WebM and QuickTime; individual edit models may accept fewer types. Arbitrary remote URL imports are not supported.

Saved work. Full original files.
GET https://api-v2.creativly.ai/v1/jobs/<batch_id>
GET https://api-v2.creativly.ai/v1/assets/<asset_id>

Follow poll_after_seconds until the job reports terminal: true. Each run has its own status and result assets. A completed batch can contain multiple results; inspect every run, including failures.

Asset details include a private original_url and a separate download_url for the full original file. These links expire after fifteen minutes; request the asset again for fresh links. Originals are not made public. Results also open in your normal Creativly session.

To use existing work, browse GET /v1/projects, GET /v1/projects/<project_id>/sessions and GET /v1/assets?project_id=…. Supply owned project_id and session_id to continue a particular session.

A clear cap. Safe retries.

max_credits is a ceiling, not a price. Creativly validates the real cost before accepting a job and uses your existing balance. Set an explicit cap for your workflow; if you omit it, the request ceiling is 1,000 credits. If you want to see the price first, send the same generation body to POST /v1/quotes; you may include the resulting quote_id in your generation request.

Reuse the same Idempotency-Key and arguments after a timeout. An exact retry returns the same accepted job without a second debit. Changed arguments with the same key are rejected. Use a new key for genuinely new work. Keys must be 8–160 characters, using letters, numbers, dots, colons, underscores or hyphens.

Insufficient credits, a cap below the price, inaccessible sources and unsupported parameters are rejected before acceptance. Accepted failures follow Creativly’s existing refund rules. A revoked API key cannot submit new jobs. Rate limits apply to your account even if you regenerate the key.

GET /v1/account shows your current balance. Keep error codes when reporting a problem, but never share your API key or signed file URLs publicly.

Creativly APIhttps://api-v2.creativly.ai/v1Prefer to ask an assistant?
Creativly API — Image and video generation | Creativly AI