ZSky API + MCP
Developer API and MCP access for active Max subscribers. Both use the same generation pipeline as the web app: photographic image generation, video with synchronized audio, AI Creative Director output. $99/mo ($79.20/mo annual) includes 300 videos + 1000 images per calendar month base. Allowance resets on the 1st of each calendar month UTC. Opt-in overage at $0.50/video and $0.05/image, with a user-set $5-$1000 monthly cap. Sustained higher volume on Enterprise quote.
Authentication
The public API base URL is https://zsky.ai/api. Every API request and MCP
connection uses a dedicated API key through the X-API-Key header. Create or
rotate the key from Settings → API + MCP. A rotated key is shown
only once and immediately invalidates the previous key, so rotate only when you are ready
to update every client that uses it.
curl https://zsky.ai/api/v1/usage \
-H "X-API-Key: zsky_live_..."
MCP setup
ZSky MCP is a local stdio adapter for compatible MCP clients, including Claude Code and Claude Desktop. It connects to the live ZSky API; it is not a separate hosted endpoint. The adapter requires Python 3.10 or newer and an active Max entitlement.
- Copy your API key from Settings → API + MCP. Do not email it or paste it into a chat.
- Install the official
zsky_mcp-2.0.0-py3-none-any.whlpackage supplied by ZSky. If you still need the package, email [email protected] from your Max account email. - Configure your MCP client to launch
zsky-mcpwith the key inZSKY_API_TOKEN. - Restart the MCP client, then call
zsky_aboutorzsky_usageto confirm the connection.
Install the adapter
python3 -m venv .venv
.venv/bin/pip install ./zsky_mcp-2.0.0-py3-none-any.whl
On Windows, use .venv\Scripts\python -m pip install .\zsky_mcp-2.0.0-py3-none-any.whl.
Claude Code
claude mcp add zsky -s user \
-e ZSKY_API_TOKEN=YOUR_ZSKY_API_KEY \
-e ZSKY_MCP_MEDIA_ROOTS=/absolute/path/to/approved/media \
-- /absolute/path/to/.venv/bin/zsky-mcp
ZSKY_MCP_MEDIA_ROOTS is optional for text-to-image and text-to-video. It is
required for image-to-video and must name only directories containing images you want
ZSky MCP to read. Supported inputs are PNG, JPEG, and WebP files up to 12 MiB.
Claude Desktop and other stdio clients
Add the following server to your MCP client configuration, using absolute paths:
{
"mcpServers": {
"zsky": {
"command": "/absolute/path/to/.venv/bin/zsky-mcp",
"env": {
"ZSKY_API_TOKEN": "YOUR_ZSKY_API_KEY",
"ZSKY_MCP_MEDIA_ROOTS": "/absolute/path/to/approved/media"
}
}
}
}
Available tools are zsky_about, zsky_generate_image,
zsky_generate_video, zsky_animate_image,
zsky_check_status, zsky_usage, and
zsky_delete_output. Generation is asynchronous: generation tools return a
job ID, and zsky_check_status returns progress and the signed result URL.
zsky_delete_output.
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/images/generate
curl -X POST https://zsky.ai/api/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": "/api/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, 0–2147483647) — for reproducible outputs
POST /v1/videos/generate
curl -X POST https://zsky.ai/api/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",
"duration": 5
}'
# Response (HTTP 200):
{
"job_id": "8a1...",
"status": "queued",
"kind": "video",
"poll_url": "/api/v1/jobs/8a1..."
}
Parameters:
prompt(required, string, ≤2000 chars)width(optional, int, 256–2560, default 1280)height(optional, int, 256–2560, default 720)duration(optional, number, 1–10, default 5) — preferred duration in secondsseed(optional, int, 0–2147483647)init_image_base64(optional, strict base64 string) — PNG, JPEG, or WebP up to 12 MiB before encoding
Video generation is asynchronous. Use the poll_url to check status.
For image-to-video, include init_image_base64 with the motion prompt. The MCP
adapter handles this conversion through zsky_animate_image after validating
that the local file is inside ZSKY_MCP_MEDIA_ROOTS.
GET /v1/jobs/<id>
curl https://zsky.ai/api/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/api/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"
}
API key management
Create or rotate the dedicated key from Settings → API + MCP. Save it immediately: the plaintext value is shown only once, and the previous key is invalidated as soon as rotation succeeds.
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 and choose a cap in Settings → API + MCP, or contact [email protected] for a higher-volume arrangement.
Beyond the monthly base, overage is opt-in only. Default state is hard-stop at the allowance. To enable it, set a monthly budget cap from Settings → API + MCP.
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/api"
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 API surface is stable. Breaking API changes will ship under /v2.
The current Max MCP adapter version is 2.0.0.
Subscribe to changes by emailing [email protected].
Support
Bugs, questions, MCP package delivery, and daily-cap lifts — email [email protected]. Mention your account email so we can verify ownership.