Get Started with API

Get Started with API

Start making API calls to WaveSpeedAI in minutes.

Step 1: Get Your API Key

  1. Go to API Keys
  2. Enter a name and click Generate
  3. Copy your key and save it securely
API Keys Page

Important: API keys require a top-up to activate. Keys generated without a top-up will not work.

Step 2: Submit a Task

Send a POST request to generate content. Here’s an example using the Z-Image Turbo model:

cURL

curl --fail-with-body --connect-timeout 10 --max-time 60 \
  -X POST "https://api.wavespeed.ai/api/v3/wavespeed-ai/z-image/turbo" \
  -H "Authorization: Bearer $WAVESPEED_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"prompt": "A cat wearing a space suit", "size": "1024*1024"}'

Python

import requests
import os
 
response = requests.post(
    "https://api.wavespeed.ai/api/v3/wavespeed-ai/z-image/turbo",
    headers={
        "Authorization": f"Bearer {os.environ['WAVESPEED_API_KEY']}",
        "Content-Type": "application/json"
    },
    json={"prompt": "A cat wearing a space suit", "size": "1024*1024"},
    timeout=(10, 60),
)
response.raise_for_status()
body = response.json()
if body.get("code") != 200:
    raise RuntimeError(body.get("message", "Task submission failed"))
print(body["data"])

JavaScript

const apiKey = process.env.WAVESPEED_API_KEY;
if (!apiKey) throw new Error("Set WAVESPEED_API_KEY");
 
const response = await fetch("https://api.wavespeed.ai/api/v3/wavespeed-ai/z-image/turbo", {
    method: "POST",
    headers: {
        "Authorization": `Bearer ${apiKey}`,
        "Content-Type": "application/json"
    },
    body: JSON.stringify({
        prompt: "A cat wearing a space suit",
        size: "1024*1024"
    }),
    signal: AbortSignal.timeout(60_000)
});
const text = await response.text();
let body;
try { body = JSON.parse(text); } catch { body = { message: text }; }
if (!response.ok || body.code !== 200) {
    throw new Error(body.message || `HTTP ${response.status}`);
}
console.log(body.data);

The submission examples intentionally do not retry POST automatically. If the connection drops after the server accepts a request, blindly retrying can create and bill a duplicate prediction. Safe GET result queries can be retried.

Response:

{
  "code": 200,
  "message": "success",
  "data": {
    "id": "abc123-task-id",
    "status": "created",
    "urls": {
      "get": "https://api.wavespeed.ai/api/v3/predictions/abc123-task-id/result"
    }
  }
}

Save the id or urls.get — you’ll need it to retrieve your result.

Step 3: Get the Result

Poll the result URL until the task is complete:

Start with a 2-second polling interval. For long-running tasks, gradually increase it to 5–10 seconds to reduce unnecessary traffic. Stop on any terminal status: completed, failed, cancelled, or timeout.

cURL

curl --fail-with-body --connect-timeout 10 --max-time 30 \
  --retry 4 --retry-all-errors --retry-delay 1 \
  "https://api.wavespeed.ai/api/v3/predictions/abc123-task-id/result" \
  -H "Authorization: Bearer $WAVESPEED_API_KEY"

Python

import time
import os
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
 
task_id = "abc123-task-id"  # From Step 2 response
session = requests.Session()
session.mount("https://", HTTPAdapter(max_retries=Retry(
    total=5,
    backoff_factor=0.5,
    status_forcelist=(429, 500, 502, 503, 504),
    allowed_methods={"GET"},
    respect_retry_after_header=True,
)))
headers = {"Authorization": f"Bearer {os.environ['WAVESPEED_API_KEY']}"}
deadline = time.monotonic() + 3600
poll_interval = 2.0
 
while time.monotonic() < deadline:
    response = session.get(
        f"https://api.wavespeed.ai/api/v3/predictions/{task_id}/result",
        headers=headers,
        timeout=(10, 30),
    )
    response.raise_for_status()
    body = response.json()
    if body.get("code") != 200:
        raise RuntimeError(body.get("message", "Result query failed"))
    data = body["data"]
 
    if data["status"] == "completed":
        print("Done!", data["outputs"])
        break
    if data["status"] in {"failed", "cancelled", "timeout"}:
        raise RuntimeError(data.get("error") or f"Task ended with {data['status']}")
 
    time.sleep(poll_interval)
    poll_interval = min(10.0, poll_interval + 1.0)
else:
    raise TimeoutError(f"Timed out waiting for prediction {task_id}")

JavaScript (Node.js 18+)

const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
const headers = { Authorization: `Bearer ${process.env.WAVESPEED_API_KEY}` };
const retryAfterMs = (value) => {
  if (!value) return null;
  const seconds = Number(value);
  if (Number.isFinite(seconds)) return seconds * 1_000;
  const date = Date.parse(value);
  return Number.isNaN(date) ? null : Math.max(0, date - Date.now());
};
 
async function getResult(taskId, maxWaitMs = 60 * 60 * 1000) {
  const url = `https://api.wavespeed.ai/api/v3/predictions/${taskId}/result`;
  const deadline = Date.now() + maxWaitMs;
  let pollDelay = 2_000;
  let transientFailures = 0;
 
  while (Date.now() < deadline) {
    let response;
    try {
      response = await fetch(url, { headers, signal: AbortSignal.timeout(30_000) });
    } catch (error) {
      if (++transientFailures > 5) throw error;
      await sleep(Math.min(10_000, 500 * 2 ** (transientFailures - 1)) + Math.random() * 250);
      continue;
    }
 
    const text = await response.text();
    let body;
    try { body = JSON.parse(text); } catch { body = { message: text }; }
    if (!response.ok) {
      const transient = response.status === 429 || response.status >= 500;
      if (!transient || ++transientFailures > 5) {
        throw new Error(body.message || `HTTP ${response.status}`);
      }
      const retryAfter = retryAfterMs(response.headers.get("retry-after"));
      const delay = retryAfter ?? 500 * 2 ** (transientFailures - 1);
      await sleep(Math.max(2_000, Math.min(10_000, delay)) + Math.random() * 250);
      continue;
    }
    if (body.code !== 200) throw new Error(body.message || "Result query failed");
 
    transientFailures = 0;
    const data = body.data;
    if (data.status === "completed") return data.outputs;
    if (["failed", "cancelled", "timeout"].includes(data.status)) {
      throw new Error(data.error || `Task ended with ${data.status}`);
    }
 
    await sleep(pollDelay);
    pollDelay = Math.min(10_000, pollDelay + 1_000);
  }
  throw new Error(`Timed out waiting for prediction ${taskId}`);
}
 
getResult("abc123-task-id")
  .then(console.log)
  .catch(error => { console.error(error); process.exitCode = 1; });

Response (completed):

{
  "code": 200,
  "message": "success",
  "data": {
    "id": "abc123-task-id",
    "status": "completed",
    "outputs": [
      "https://cdn.wavespeed.ai/outputs/image-xxxxx.png"
    ]
  }
}

The outputs array contains URLs to your generated content.

API Reference

ItemValue
Base URLhttps://api.wavespeed.ai/api/v3
Auth HeaderAuthorization: Bearer YOUR_API_KEY
Content Typeapplication/json

Task Status Values

StatusDescription
createdTask is accepted and queued
processingTask is running
completedTask finished successfully
failedTask failed (check error field)
cancelledTask was cancelled
timeoutTask exceeded its execution limit

Next Steps

© 2026 WaveSpeedAI. All rights reserved.