SharkAPI Documentation
Complete reference for the SharkAPI image generation API. Generate 1024×1024 AI images asynchronously — submit a job, poll for the result.
Overview
SharkAPI is an asynchronous image generation API. You submit a prompt (and optionally a reference image), receive a job_id immediately, then poll every 2 seconds until your image is ready. Generation typically takes 30–120 seconds.
Billing is wallet-based — you load credit in advance and are charged only when a job successfully completes with an image. Failed jobs are never charged.
Base URL
https://www.sharkapi.dev
All endpoints are under this base URL. Always use HTTPS.
Authentication
Every request must include your API key in the X-API-Key header. Get your key from Dashboard → API Tokens.
X-API-Key: sk_live_your_token_here
| Parameter | Type | Required | Description |
|---|---|---|---|
| 401 Unauthorized | HTTP | Optional | X-API-Key header is missing or the key is invalid. |
| 403 Forbidden | HTTP | Optional | Key exists but has been revoked, or account is suspended. |
How it works
Every image generation follows this async pattern:
POST /api/v1/image
Submit your prompt → receive job_id instantly (~200ms)
Worker picks up job
Background worker claims the job, starts generation (30–120s)
GET /api/v1/jobs/:id
Poll every 2 seconds — status moves from queued → processing
status: completed
image_url is ready. Wallet charged. Job done.
Quick start
# Step 1 — submit a job
curl -X POST https://www.sharkapi.dev/api/v1/image \
-H "X-API-Key: sk_live_••••••••" \
-H "Content-Type: application/json" \
-d '{"prompt": "A great white shark in bioluminescent deep ocean, cinematic, 8K"}'
# → 202 Accepted
# { "job_id": "3a8f2c1d-...", "status": "queued", "poll_url": "/api/v1/jobs/3a8f2c1d-..." }
# Step 2 — poll every 2 seconds until completed
curl https://www.sharkapi.dev/api/v1/jobs/3a8f2c1d-... \
-H "X-API-Key: sk_live_••••••••"
# → { "status": "completed", "image_url": "https://...supabase.co/.../generated-images/..." }Pricing & wallet
1024 × 1024 px
Fast standard resolution. Currently available.
MOST POPULAR2048 × 2048 px
High-resolution output.
COMING SOONPOST — Generate image
Submit an image generation job. Returns 202 Accepted with a job_id immediately. The job is processed asynchronously.
Request headers
| Parameter | Type | Required | Description |
|---|---|---|---|
| X-API-Key | string | Required | Your API key. Example: "sk_live_..." |
| Content-Type | string | Required | "application/json" |
Request body
| Parameter | Type | Required | Description |
|---|---|---|---|
| prompt | string | Required | Text description of the image to generate. Maximum 2000 characters. |
| variant | string | Optional | Resolution variant. Currently only "1k" (1024×1024) is available. Defaults to "1k" if omitted. |
| image | string | Optional | Base64-encoded reference image for image-to-image. JPEG, PNG, or WebP. Max 10 MB. Cannot be combined with image_url. |
| image_url | string | Optional | Publicly accessible URL of a reference image. Max 10 MB. Cannot be combined with image. |
image and image_url are mutually exclusive. Send one or the other — never both in the same request.Response — 202 Accepted
| Parameter | Type | Required | Description |
|---|---|---|---|
| job_id | string | Required | Unique job ID. Use this to poll for the result. |
| status | string | Required | Always "queued" on creation. |
| poll_url | string | Required | Relative URL to poll: GET {poll_url} |
| price_usd | number | Required | Cost of this job in USD. Charged only on success. |
| created_at | string | Required | ISO 8601 creation timestamp. |
{
"job_id": "3a8f2c1d-7b4e-4f9a-b2d6-1c5e8f3a9b7d",
"status": "queued",
"poll_url": "/api/v1/jobs/3a8f2c1d-7b4e-4f9a-b2d6-1c5e8f3a9b7d",
"price_usd": 0.03,
"created_at": "2025-01-15T10:23:44Z"
}Text-only cURL
curl -X POST https://www.sharkapi.dev/api/v1/image \
-H "X-API-Key: sk_live_••••••••" \
-H "Content-Type: application/json" \
-d '{
"prompt": "A great white shark in bioluminescent deep ocean, cinematic, 8K",
"variant": "1k"
}'Sending a reference image
To use image-to-image generation, attach a reference image using either base64 or a URL. We mirror all inputs to our own storage before sending to the generation engine — your original file is never exposed externally.
Option A — Base64
Encode your file as base64 and pass it in the image field. Both raw base64 and the data:image/...;base64, prefix format are accepted.
# Encode your file to base64 first
IMAGE_B64=$(base64 -w 0 ./reference.jpg) # Linux
# IMAGE_B64=$(base64 -i ./reference.jpg) # macOS
curl -X POST https://www.sharkapi.dev/api/v1/image \
-H "X-API-Key: sk_live_••••••••" \
-H "Content-Type: application/json" \
-d "{
\"prompt\": \"Transform this into a watercolor painting\",
\"image\": \"$IMAGE_B64\"
}"Option B — URL
Pass a publicly accessible URL in the image_url field. The URL must be reachable from our servers without authentication. For private images, use base64.
curl -X POST https://www.sharkapi.dev/api/v1/image \
-H "X-API-Key: sk_live_••••••••" \
-H "Content-Type: application/json" \
-d '{
"prompt": "Transform this into a watercolor painting",
"image_url": "https://your-cdn.com/photo.jpg"
}'GET — Job status
Poll this endpoint every 2 seconds. Replace :id with the job_id from the generate response. Returns 200 OK with the current job state — check the status field to know if generation is done.
Request
| Parameter | Type | Required | Description |
|---|---|---|---|
| X-API-Key | string | Required | Your API key. Example: "sk_live_..." |
| :id (path) | string | Required | The job_id from the POST response. |
curl https://www.sharkapi.dev/api/v1/jobs/3a8f2c1d-7b4e-4f9a-b2d6-1c5e8f3a9b7d \ -H "X-API-Key: sk_live_••••••••"
Response fields
| Parameter | Type | Required | Description |
|---|---|---|---|
| job_id | string | Required | Job identifier. |
| status | string | Required | "queued" | "processing" | "completed" | "failed" |
| image_url | string | null | Required | Permanent image URL. Set only when status is "completed". |
| cost | string | null | Required | Final cost, e.g. "$0.03". Set only when status is "completed". |
| error | string | null | Required | Error description when status is "failed". Null otherwise. |
| queued_at | string | Required | ISO 8601 timestamp when job entered the queue. |
| started_at | string | null | Required | ISO 8601 timestamp when the worker began. Null if still queued. |
| completed_at | string | null | Required | ISO 8601 timestamp of completion. Null if not finished. |
| created_at | string | Required | ISO 8601 timestamp of job creation. |
While queued or processing
{
"job_id": "3a8f2c1d-7b4e-4f9a-b2d6-1c5e8f3a9b7d",
"status": "queued",
"image_url": null,
"cost": null,
"error": null,
"queued_at": "2025-01-15T10:23:44Z",
"started_at": null,
"completed_at": null,
"created_at": "2025-01-15T10:23:44Z"
}Completed — image ready
{
"job_id": "3a8f2c1d-7b4e-4f9a-b2d6-1c5e8f3a9b7d",
"status": "completed",
"image_url": "https://your-project.supabase.co/storage/v1/object/public/generated-images/user-id/3a8f2c1d-xxxx.png",
"cost": "$0.03",
"error": null,
"queued_at": "2025-01-15T10:23:44Z",
"started_at": "2025-01-15T10:23:46Z",
"completed_at": "2025-01-15T10:24:18Z",
"created_at": "2025-01-15T10:23:44Z"
}Failed — no charge applied
{
"job_id": "9b3c1a2d-4f8e-4c7b-a1e5-2d9f6b4a8c3e",
"status": "failed",
"image_url": null,
"cost": null,
"error": "Provider request timed out after 180s.",
"queued_at": "2025-01-15T10:30:00Z",
"started_at": "2025-01-15T10:30:02Z",
"completed_at": null,
"created_at": "2025-01-15T10:30:00Z"
}Job statuses
Job received and waiting for the generation worker to pick it up.
Worker is actively generating the image. Typically 30–120 seconds.
Done. image_url is a permanent URL. Wallet has been charged.
Generation failed (timeout or provider error). No charge. Safe to retry.
completed after that, it will be automatically marked failed with no charge.Errors
All error responses are JSON with an error string field. Some errors also include a code field for programmatic handling.
{
"error": "Insufficient wallet balance."
}
// Some errors also include a machine-readable code:
{
"error": "Insufficient wallet balance.",
"code": "INSUFFICIENT_BALANCE"
}Bad request — missing or invalid body fields (e.g. no prompt, both image and image_url sent, image too large).
Unauthorized — X-API-Key header is missing or the key is invalid.
Payment required — wallet balance is below the cost of this request. Top up your account and retry.
Forbidden — API key has been revoked or account is suspended.
Not found — job ID does not exist or belongs to a different account.
Unprocessable — no active model for the requested variant. Only "1k" is currently available.
Server error — unexpected failure. Retry after a few seconds. Your wallet is not charged.
Full examples
Each example below implements the complete flow: submit a job, poll until done, print the image URL.
JavaScript / Node.js
// ── SharkAPI — complete JavaScript / Node.js example ──────────────
const SHARKAPI_KEY = process.env.SHARKAPI_KEY; // sk_live_...
const BASE_URL = "https://www.sharkapi.dev";
// 1. Submit a generation job
async function generateImage(prompt, options = {}) {
const body = { prompt, variant: "1k" };
// Optional: attach a reference image (base64 or URL, not both)
if (options.imageBase64) body.image = options.imageBase64;
if (options.imageUrl) body.image_url = options.imageUrl;
const res = await fetch(`${BASE_URL}/api/v1/image`, {
method: "POST",
headers: {
"X-API-Key": SHARKAPI_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify(body),
});
if (!res.ok) {
const err = await res.json();
throw new Error(err.error ?? `HTTP ${res.status}`);
}
return res.json(); // { job_id, status: "queued", poll_url, price_usd }
}
// 2. Poll until completed or failed
async function waitForJob(jobId, timeoutMs = 180_000) {
const url = `${BASE_URL}/api/v1/jobs/${jobId}`;
const headers = { "X-API-Key": SHARKAPI_KEY };
const deadline = Date.now() + timeoutMs;
while (Date.now() < deadline) {
const res = await fetch(url, { headers });
if (!res.ok) throw new Error(`Poll failed: HTTP ${res.status}`);
const job = await res.json();
if (job.status === "completed") return job; // job.image_url is ready
if (job.status === "failed") throw new Error(job.error ?? "Job failed");
// queued / processing — wait and retry
await new Promise(r => setTimeout(r, 2000));
}
throw new Error("Job timed out after 3 minutes");
}
// 3. Full flow
async function main() {
console.log("Submitting job...");
const { job_id } = await generateImage(
"A great white shark in bioluminescent deep ocean, cinematic, 8K"
);
console.log(`Job created: ${job_id} — polling...`);
const job = await waitForJob(job_id);
console.log("Done!");
console.log("Image URL:", job.image_url);
console.log("Cost: ", job.cost);
}
main().catch(console.error);Python
# ── SharkAPI — complete Python example ────────────────────────────
import os, time, httpx
SHARKAPI_KEY = os.environ["SHARKAPI_KEY"] # sk_live_...
BASE_URL = "https://www.sharkapi.dev"
def generate_image(prompt: str, image_path: str | None = None, image_url: str | None = None) -> dict:
"""Submit a generation job. Returns the job dict with job_id."""
headers = {
"X-API-Key": SHARKAPI_KEY,
"Content-Type": "application/json",
}
body = {"prompt": prompt, "variant": "1k"}
if image_path:
import base64
with open(image_path, "rb") as f:
body["image"] = base64.b64encode(f.read()).decode()
elif image_url:
body["image_url"] = image_url
res = httpx.post(f"{BASE_URL}/api/v1/image", headers=headers, json=body, timeout=30)
res.raise_for_status()
return res.json() # { job_id, status, poll_url, price_usd }
def wait_for_job(job_id: str, timeout: int = 180) -> dict:
"""Poll until completed or failed. Returns the completed job dict."""
url = f"{BASE_URL}/api/v1/jobs/{job_id}"
headers = {"X-API-Key": SHARKAPI_KEY}
deadline = time.time() + timeout
while time.time() < deadline:
res = httpx.get(url, headers=headers, timeout=10)
res.raise_for_status()
job = res.json()
if job["status"] == "completed":
return job # job["image_url"] is ready
if job["status"] == "failed":
raise RuntimeError(job.get("error") or "Job failed — no charge applied")
time.sleep(2) # queued / processing — keep polling
raise TimeoutError("Job timed out after 3 minutes")
# Full flow
if __name__ == "__main__":
print("Submitting job...")
job_info = generate_image(
"A great white shark in bioluminescent deep ocean, cinematic, 8K"
)
job_id = job_info["job_id"]
print(f"Job created: {job_id} — polling...")
job = wait_for_job(job_id)
print("Done!")
print("Image URL:", job["image_url"])
print("Cost: ", job["cost"])Shell (bash / cURL)
#!/bin/bash
# ── SharkAPI — complete shell script example ──────────────────────
TOKEN="sk_live_••••••••"
BASE="https://www.sharkapi.dev"
# 1. Submit job
RESPONSE=$(curl -s -X POST "$BASE/api/v1/image" \
-H "X-API-Key: $TOKEN" \
-H "Content-Type: application/json" \
-d '{"prompt":"A great white shark in bioluminescent deep ocean, cinematic, 8K"}')
JOB_ID=$(echo "$RESPONSE" | grep -o '"job_id":"[^"]*"' | cut -d'"' -f4)
echo "Job created: $JOB_ID"
# 2. Poll every 2 seconds
while true; do
POLL=$(curl -s "$BASE/api/v1/jobs/$JOB_ID" \
-H "X-API-Key: $TOKEN")
STATUS=$(echo "$POLL" | grep -o '"status":"[^"]*"' | cut -d'"' -f4)
echo "Status: $STATUS"
if [ "$STATUS" = "completed" ]; then
echo "Image URL: $(echo $POLL | grep -o '"image_url":"[^"]*"' | cut -d'"' -f4)"
break
fi
if [ "$STATUS" = "failed" ]; then
echo "Job failed — no charge applied"
break
fi
sleep 2
doneComing soon
2K Mode — 2048×2048 px at $0.05/image
High-resolution output for print, detailed renders, and premium applications. When 2K launches, you will be able to pass variant: "2k" and the API behaviour stays identical — just poll the same way. No integration changes needed on your side.
