ZSky AI Developer API
Developer API access for Max-tier subscribers. Same generation pipeline as the web app: photographic image generation, video with synchronized audio, AI Creative Director output. Flat $99/mo includes 300 videos + 1000 images per calendar month. Allowance resets on the 1st of each calendar month. Higher volume available on request.
Authentication
Every request must include an X-API-Key header containing a key issued to you.
Keys are long-lived; rotate them from this dashboard or by calling
POST /v1/keys/rotate. Lost keys cannot be recovered — rotation issues a new one
and immediately invalidates the previous.
curl https://zsky.ai/v1/usage \
-H "X-API-Key: zsky_live_..."
Endpoints
| Method | Path | Purpose |
|---|---|---|
| POST | /v1/images/generate | Create an image (text-to-image) |
| POST | /v1/videos/generate | Create a video (text-to-video) |
| GET | /v1/jobs/<id> | Poll job status, get signed result URL |
| DELETE | /v1/jobs/<id> | Purge the result from storage |
| GET | /v1/usage | Current-month allowance counters |
| POST | /v1/keys/rotate | Issue a new key, invalidate current |
POST /v1/images/generate
curl -X POST https://zsky.ai/v1/images/generate \
-H "X-API-Key: zsky_live_..." \
-H "Content-Type: application/json" \
-d '{
"prompt": "A neon-lit alley in Tokyo at dusk, photorealistic",
"width": 1024,
"height": 1024
}'
# Response (HTTP 200):
{
"job_id": "5f3...",
"status": "queued",
"kind": "image",
"poll_url": "/v1/jobs/5f3..."
}
Parameters:
prompt(required, string, ≤2000 chars) — the image promptwidth(optional, int, 256–2048, default 1024)height(optional, int, 256–2048, default 1024)seed(optional, int) — for reproducible outputsnegative(optional, string) — negative prompt
POST /v1/videos/generate
curl -X POST https://zsky.ai/v1/videos/generate \
-H "X-API-Key: zsky_live_..." \
-H "Content-Type: application/json" \
-d '{
"prompt": "A solitary lighthouse beam sweeping across choppy black sea, cinematic",
"length": 121
}'
# Response (HTTP 200):
{
"job_id": "8a1...",
"status": "queued",
"kind": "video",
"poll_url": "/v1/jobs/8a1..."
}
Parameters:
prompt(required, string, ≤2000 chars)width(optional, int, 256–2560, default 1280)height(optional, int, 256–2560, default 720)length(optional, int, 25–241, default 121) — frames; ~5 seconds at defaultseed(optional, int)
Videos take ~60 seconds to render. Use the poll_url to check status.
Image-to-video (uploading a starting frame) is not yet supported in v1 — coming in v2.
GET /v1/jobs/<id>
curl https://zsky.ai/v1/jobs/8a1... -H "X-API-Key: zsky_live_..."
# While queued/running:
{ "job_id": "8a1...", "status": "queued", "kind": "video" }
# When completed:
{
"job_id": "8a1...",
"status": "completed",
"kind": "video",
"result_url": "https://...r2.cloudflarestorage.com/...&X-Amz-Signature=...",
"expires_in": 86400
}
# If blocked by safety:
{
"job_id": "8a1...",
"status": "blocked",
"kind": "video",
"error": "Sorry — our safety check stopped this video..."
}
result_url is a 24-hour signed download URL pointing directly at our storage.
Download it once and persist locally if you need the bytes longer.
DELETE /v1/jobs/<id>
Purges the generated asset from our storage before the 7-day auto-delete. Useful when you've downloaded the bytes and want them gone immediately. Returns HTTP 204 on success, 404 if the job doesn't exist or isn't yours.
GET /v1/usage
curl https://zsky.ai/v1/usage -H "X-API-Key: zsky_live_..."
# Response:
{
"videos_used": 47,
"videos_overage": 0,
"images_used": 180,
"images_overage": 0,
"overage_charged_cents": 0,
"base_videos_allowance": 300,
"base_images_allowance": 1000,
"month_start": "2026-05-01"
}
POST /v1/keys/rotate
Issues a new key and invalidates the current one. Save the new key immediately — it cannot be recovered if lost. The response includes a one-time-shown plaintext value.
Rate Limits & Allowances
| Monthly base — videos | 300 per calendar month (UTC) |
| Monthly base — images | 1,000 per calendar month (UTC) |
| Rollover | None — unused allowance does NOT carry over to next month |
| Reset | 1st of each calendar month at 00:00 UTC |
| Concurrent jobs | 3 per API key |
| Burst | 20 requests per minute per key |
| Daily soft cap | 50 videos / 200 images per UTC day per key (lift on request) |
Overage budget: does roll over. If you opt in to overage and set a monthly cap (e.g. $50), any unused overage budget carries over to the following month. Use it or save it across months.
If you need consistently higher monthly volume, enable overage (one-click via your dashboard once Plan 2 lands; until then, email [email protected]).
Beyond the monthly base, overage is opt-in only. Default state is hard-stop at the allowance. To enable overage, set a monthly budget cap from your dashboard (Plan 2 — coming soon); until then, contact [email protected].
Overage pricing when enabled: $0.50 per video, $0.05 per image.
Storage & Retention
- Signed download URLs are valid for 24 hours after issuance.
- Generated assets remain available for re-fetch via
GET /v1/jobs/<id>for 7 days, then auto-delete. - Customer owns the output — full rights and title to anything generated through your account.
- API outputs never appear in /explore or the public community feed.
- Zero-Data-Retention option: add header
X-ZSky-Store: 0to any generate request — the bytes are returned inline and never persisted. - Watermarking: Max-tier outputs include an invisible provenance watermark, no visible plate.
Safety
Every API request flows through the same safety pipeline as our web app:
input checks on prompts, output checks on generated content. Blocked generations
return status blocked with a human-readable reason — they still count
against your monthly allowance because the GPU work was performed.
Repeated rejected requests (more than 50% of the last 30 minutes' attempts, minimum 10 events) trigger an automatic 24-hour suspension on the API key. If suspended, you'll get HTTP 403 until the window clears. Email support if you believe the suspension is in error.
Error Codes
| Status | Meaning |
|---|---|
200 | OK |
204 | No content (DELETE success) |
400 | Malformed request body or invalid params |
401 | Missing or invalid API key |
403 | API key suspended |
404 | Job not found (or not owned by you) |
429 | Rate limit hit OR monthly allowance reached without overage |
451 | Generation blocked by safety filter |
500 | Internal error — please retry |
503 | Workers unavailable; queued — please retry shortly |
Sample Python Client
#!/usr/bin/env python3
"""Minimal ZSky API client — generate an image, poll, download."""
import os, time, json, urllib.request
API = "https://zsky.ai"
KEY = os.environ["ZSKY_API_KEY"]
def call(method, path, body=None):
req = urllib.request.Request(
f"{API}{path}",
data=json.dumps(body).encode() if body else None,
headers={"X-API-Key": KEY, "Content-Type": "application/json"},
method=method,
)
with urllib.request.urlopen(req, timeout=30) as r:
return json.loads(r.read())
def generate_image(prompt):
job = call("POST", "/v1/images/generate", {"prompt": prompt})
job_id = job["job_id"]
while True:
time.sleep(2)
state = call("GET", f"/v1/jobs/{job_id}")
if state["status"] in ("completed", "failed", "blocked"):
return state
state = generate_image("A coffee cup on a wooden table, photorealistic")
if state["status"] == "completed":
print(f"Result: {state['result_url']}")
urllib.request.urlretrieve(state["result_url"], "out.webp")
else:
print(f"Failed: {state.get('error')}")
Versioning & Stability
The v1 surface is stable. Breaking changes will ship under /v2.
Subscribe to changes by emailing [email protected].
Support
Bugs, questions, key issuance, daily-cap lifts — email [email protected]. Mention your account email so we can verify ownership.