Creativly API. Make it part of your app.
Generate images, animate references and edit video.
One API key. Your models, projects and existing credits.
- 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
Make something
A model, a prompt and a credit cap. No project setup required.
Create an imagecurl "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
Bring the result back
The response returns a batch_id. Check its status, then retrieve each finished asset.
Status → original fileGET /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 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.
An idea. A finished campaign.
Describe the shot and choose your model. Get an image you can download, edit or build on.
{
"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
}
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.
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.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 headersSee 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.
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)Bring private references.
Upload an image or video, wait for moderation, then use it in a generation or edit.
Upload workflowChoose your model.
Discover available models, supported operations and settings from the live catalog.
Models and parametersKeep spending predictable.
Quote before you generate, set a maximum cost and retry safely with the same request key.
Credits and retriesA 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/jsonUse 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-previewThe 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.

