Docs API reference
Markdown Get an API key

API referenceGetting started

Getting Started

Everything on this page applies to every endpoint in the reference: where the API lives, how you authenticate, how errors and rate limits behave, and how spend is billed. Read it once, then go build.

#Introduction

The Sogni REST API lives at https://api.sogni.ai. One surface covers an OpenAI-compatible chat endpoint, synchronous creative tools, a durable agent runtime, a creative-workflow engine, project status, account balances, worker analytics, and model discovery. Hosted chat and workflows stream over Server-Sent Events; everything else is an ordinary JSON read or write.

#What's here

#Quick start

The shortest path to a result: a chat completion that runs Sogni creative tools server-side.

curl https://api.sogni.ai/v1/chat/completions \
  -H "Authorization: Bearer $SOGNI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "messages": [
      {"role": "user", "content": "Generate a cinematic image of a neon alley in Tokyo during rain."}
    ]
  }'
Want the bigger picture? The Sogni Intelligence guides explain how chat, tools, and workflows fit together.

#Authentication

Authenticated endpoints accept a bearer token in the Authorization header. Create API keys on the API Keys page of your Sogni dashboard. Public endpoints, including aggregate worker analytics and generation-model discovery, do not require a key.

Authorization: Bearer YOUR_API_KEY

#Two credential types

  • API keys belong to one account and are meant for servers, SDKs and agents. They are required for durable chat runs and creative workflow execution. An account can hold up to 99: developer keys (sogni_dev_…) for apps and the API, worker keys (sogni_wkr_…) for GPU workers only, and the account's Original key, a UUID that keeps working everywhere. Developer keys can have daily, monthly and lifetime spending limits. See API Keys.
  • Session tokens are short-lived browser JWTs issued by the Sogni auth flow. Most read endpoints accept either credential type.

The api-key: YOUR_API_KEY header is also accepted. Never send a key in a URL.

Treat API keys like passwords. They authorize spend on your behalf. Keep them on your server, never in a browser bundle. Give each app or agent its own developer key with spending limits. If a key is exposed, revoke or rotate it on the API Keys page; a revoked key stops working immediately.

#Versioning

Each endpoint has its own versioned path. Most use /v1/*; newer-shape replacements include owner-scoped project status at /v2/projects/:id and account balances at /v4/account/balance. Use the exact path shown for each endpoint. Do not change the version prefix across your whole integration.

#Errors

The API uses standard HTTP status codes. Successful responses return 200, 201, or 202; failures return 4xx for caller mistakes and 5xx for server-side problems. Error bodies are JSON.

200OK. Synchronous success.
201Created. A workflow (inline start or reseed), a workflow template (create or fork), or a replay record was saved.
202Accepted. A durable chat run or a personal LoRA import was accepted, a workflow template run is waiting for cost approval, or a workflow resume was scheduled.
304Not modified. A cached public read matched your If-None-Match tag; reuse your cached body.
400Validation error. A body or query parameter failed validation.
401Authentication missing or invalid, or the API key is paused, expired or revoked.
402Insufficient balance. The selected token can't cover the request, or a vendor-model step needs Premium Spark.
403Forbidden. The credential or account isn't allowed to perform the action, such as a subscriber-only feature without an active plan, publishing a public workflow template, or paid work with a worker key. A plain-text error code: 1010 body means the request never reached the API; see 403 with error code 1010.
404Resource not found, or hidden from the caller.
409Conflict. The run or workflow is in the wrong state for the action (for example not awaiting cost approval, a stale or expired cost preview, or already finished), an idempotency key was reused with a different confirm-cost body, too many workflows are active, or a resource already exists.
413Payload too large. A replay record exceeds 1 MB, or a workflow template exceeds its size limit.
422Unprocessable entity. A workflow template failed schema validation or could not compile.
429Rate limited. Wait for the Retry-After header or the retryAfter body field (seconds) when present; otherwise back off with jitter. A chat-completions 429 of type api_key_limit_reached is an API key spending limit instead, and retrying will not help.
500Internal error. Retry after backoff; report persistent failures.
502Upstream model error, such as a failed model stream or a malformed tool call. Retry.
503Temporarily unavailable. Retry with exponential backoff and jitter; do not treat it as data loss.
504Upstream timeout. Retry with backoff.

#Error envelopes

LLM routes (/v1/chat/completions, /v1/models) emit the OpenAI-compatible error shape:

{
  "error": {
    "message": "'messages' is required and must be a non-empty array",
    "type": "invalid_request_error",
    "param": null,
    "code": "invalid_request_error"
  }
}

All other endpoints emit the Sogni envelope:

{
  "status": "error",
  "errorCode": 102,
  "message": "Creative workflow requires input.steps"
}

Sogni envelopes may also include details (structured context, such as why a 409 happened) and retryAfter in seconds. OpenAI-shaped errors may include details. The API's rate limiter uses the Sogni envelope on 429 and also keeps "error": "Too Many Requests" for older clients. Workflow template validation failures return { "status": "error", "data": { "validation": { "issues": [ … ] } } }. Branch on the HTTP status first.

#403 with error code: 1010

A 403 whose body is the plain text error code: 1010, not one of the JSON envelopes above, comes from Cloudflare, which sits in front of the API. Cloudflare's Browser Integrity Check rejects the default User-Agent header that Python's standard library sends (Python-urllib/3.x), so urllib.request calls are refused before they reach the API. The API key is never checked, so this error says nothing about your key or account. Some anonymous read-only routes, such as the public model catalog, skip the check. That is why they can succeed from the same script while authenticated calls fail.

Send an explicit User-Agent that names your app on every request:

import os, urllib.request

req = urllib.request.Request(
    "https://api.sogni.ai/v2/account/me",
    headers={
        "User-Agent": "my-app/1.0",
        "Authorization": f"Bearer {os.environ['SOGNI_API_KEY']}",
    },
)
with urllib.request.urlopen(req) as res:
    print(res.status, res.read().decode())

HTTP clients that send their own User-Agent, such as requests and httpx, are not affected. Neither are the official SDKs: sogni-client on PyPI and @sogni-ai/sogni-client on npm. If a request with an explicit User-Agent returns 401, it reached the API, and the key or account is the problem. Check that the Authorization header carries a current key from dashboard.sogni.ai/api-key.

#Rate limits

When a request is rate-limited, the API returns 429 Too Many Requests. Honor the Retry-After header when present. Otherwise, retry with exponential backoff and jitter. Reduce concurrency instead of immediately repeating failed requests.

Active-workflow cap. If a workflow start returns 409 because too many workflows are active, cancel or finish an existing run before submitting another, or batch work into a single multi-step workflow. Check the error message: other conflicts also use 409. The active-workflow 409 carries details.activeWorkflowCount and details.activeWorkflowLimit, and it does not use up your start allowance. A 429 on a workflow start means starts arrived too quickly or overall workflow capacity is full. A start-rate refusal carries the wait in the Retry-After header and, in seconds, in the body's retryAfter; a start sent before then is refused again. Retry with the same Idempotency-Key.

Default limits for workflows, render concurrency, and SDK connections, and how to design around them, are in Production Integrations.

#Idempotency

Write endpoints that can produce side effects accept an idempotency key. Reusing the same key for the same caller returns the original result instead of starting a duplicate run. Use it to make retries safe across network failures and double-clicks.

#Headers

Idempotency-Key: 7c9e6f7c-23a1-4f06-9d33-2dd5d6c8f5fb

X-Idempotency-Key is accepted as an alias.

#Supported endpoints

Scope. The key is scoped to the calling wallet, so the same key from a different wallet does not collide. Chat-run keys longer than 200 characters are ignored, and workflow keys longer than 192 characters are truncated; a UUID is ideal for both.

#Billing and tokens

Spend is denominated in two token types. Pick one explicitly with token_type on a request, or let the API choose.

TokenHow acquiredUsed for
sogniNative. Earned through Supernet participation or staking.Pays for Sogni-native models (such as Krea, Qwen, LTX, WAN, MiniMax H3, and ACE-Step) when selected, or as the fallback for auto.
sparkPurchased with cash, or earned.Paid first under auto. Premium Spark is required for external vendor models: gpt-image-2, gpt-image-2.5-sunburst, gpt-image-2.5-flare, seedream-5-0-pro-uncensored, seedance2, seedance2-mini, seedance2-5, seedance2-5-uncensored, seedance2-mini-uncensored, happyhorse-1.1-*, wan3.0-video, and wan3.0-spicy-video.

#Selecting a token

Accepted values are auto (default), sogni, and spark. When tokens are billed, auto pays with Spark first and falls back to SOGNI.

{ "token_type": "auto" }

/v1/chat/completions also accepts an X-Token-Type header (the body field wins); other endpoints read token_type from the body only. Vendor-model jobs are normalized to spark automatically, regardless of preference.

#Plan or tokens

token_type only picks which token pays. Whether an active Unlimited plan or your token balance pays is a separate field, billing_mode, accepted by chat completions, tool execution, chat runs, and workflows (start, resume, and reseed).

billing_modeBehavior
autoDefault. An active Unlimited plan covers the work it can, and no tokens are spent on that work. Everything else bills your token balance according to token_type.
subscriptionBill eligible work against the plan only. Work the plan can't cover fails instead of billing tokens.
tokensAlways bill your token balance, even when a plan is active.

With an active plan, changing token_type between spark and auto does not move a job off the plan. To spend Premium Spark you hold, which gets fastest-priority queue access ahead of plan-covered jobs, send billing_mode: "tokens". Vendor models are never plan-covered and always bill Premium Spark. A request that asks to pay in SOGNI (token_type: "sogni") is not plan-covered either.

{ "token_type": "spark", "billing_mode": "tokens" }

#Vendor model gating

Models from external vendors (OpenAI GPT Image 2 and GPT Image 2.5 Sunburst / Flare, ByteDance Seedance 2.0 / Mini, Seedance 2.5, Seedance 2.5 Uncensored, Seedance 2.0 Mini Uncensored, Seedream 5.0 Pro Uncensored, Alibaba HappyHorse 1.1, Wan 3, and Wan 3 Uncensored) bill Premium Spark. Hosted chat may choose a GPT Image model for storyboard or character-sheet work; if the account is not eligible for Premium Spark, chat uses a Sogni-hosted model instead. Workflows never substitute: a workflow that names a vendor model returns 402 with details.vendorModels before any step runs, including a confirm_cost: false estimate.

#Cost approval

  • Creative workflows use a two-step confirmation. Submit with confirm_cost: false to get a 400 whose details.estimatedCapacity holds the estimate, then resubmit with confirm_cost: true. max_estimated_capacity_units is a hard cap: plans over it are rejected with 400 before anything is saved. Template runs (workflow_id) always return 202 with a preview and wait for POST /v1/creative-agent/workflows/:id/confirm-cost.
  • Chat runs opt in with runtime_config.requireJobConfirmation: true. When the model calls a paid media tool, the run pauses in waiting_for_user with reason cost_approval_required, holds every paid call from that round, and emits run_awaiting_cost_confirmation for each held call. Resume or decline with POST /v1/chat/runs/:id/confirm-cost, echoing waiting.details.costApprovalPreview. confirm_cost and max_estimated_capacity_units are recorded on chat runs but not enforced.