Durango API

Programmatic access to every chat, image-generation, and video-generation model in Durango. Requests authenticate with a personal API key and consume the same usage credits as the app — there is no separate billing.

Durango API documentation

Base URL
https://durango.sh
Machine-readable
OpenAPI 3.1 spec · no auth required
llms.txt · this reference as plain markdown

All endpoints accept and return JSON, except image/video downloads (raw bytes) and streaming chat (Server-Sent Events). CORS is enabled on every /api/v1/* endpoint, so the API can be called from browsers as well as servers.

POST/api/v1/chat/completionsChat completion (OpenAI-compatible, optional streaming)
POST/api/v1/images/generationsGenerate an image (synchronous)
GET/api/v1/images/{imageId}Download a generated image
POST/api/v1/videos/generationsStart a video generation (async)
GET/api/v1/videos/generations/{runId}Poll video generation status
GET/api/v1/videos/{videoId}Download a generated video
GET/api/v1/modelsList available chat + image + video models
GET/api/v1/creditsCredit balance, tier, and reset date
GET/api/v1/openapi.jsonOpenAPI 3.1 specification (no auth)

Authentication

Create an API key in Settings → API Keys (or at /settings/api-keys). Keys look like dgo_… and are shown once at creation time — store them securely. Revoking a key disables it immediately.

Send the key on every request, either way works:

Request headers
Authorization: Bearer dgo_your_key_here

# or

X-API-Key: dgo_your_key_here

A key acts on behalf of the account that created it: every request spends that account's credits, and generated images/videos land in that account's gallery. Missing, invalid, or revoked keys return 401 with an error body (see Errors).

Credits & billing

API usage draws from the same monthly credit balance as the Durango app and appears in the same usage history.

1 credit = $0.01 USD. Balances are tracked fractionally — a cheap chat message might cost 0.02 credits.
Monthly allowance by tier: Free 10 credits · Pro 500 credits · Ultra 5,000 credits. Credits reset at the start of each billing period.
Chat cost = input tokens × model input price + output tokens × model output price (see pricing in the model list). The exact charge is returned on every non-streaming response as usage.credits_used.
Image cost is token-based when the model reports usage, with per-model fallback pricing otherwise (roughly 3–18 credits per image).
Video cost is reserved when the async run starts, then finalized from provider usage or per-model fallback pricing when the run completes.
Tier gating: some models require a minimum subscription (required_tier of pro or ultra); calling them from a lower tier returns 403. Image and video generation require Pro or Ultra.
Out of credits: requests fail with 402 insufficient_credits until your monthly reset or an upgrade.

List models

Lists every model callable through the API, with capabilities, required tier, and pricing.

GET/api/v1/modelsList available models
Query parameters
typestring
Optional filter: chat, image, or video.
Request
curl "https://durango.sh/api/v1/models?type=chat" \
  -H "Authorization: Bearer $DURANGO_API_KEY"
Response
{
  "object": "list",
  "data": [
    {
      "id": "anthropic/claude-sonnet-4.5",
      "object": "model",
      "type": "chat",
      "name": "Claude Sonnet 4.5",
      "provider": "Anthropic",
      "category": "favorites",
      "capabilities": { "vision": true, "reasoning": true, "toolUse": true, "imageGen": false },
      "required_tier": "free",
      "context_length": 200000,
      "pricing": { "prompt": "0.000003", "completion": "0.000015" }
    }
  ]
}
Response fields
idstring
Pass this as model in chat / image / video requests.
typestring
chat models go to /api/v1/chat/completions; image models go to /api/v1/images/generations; video models go to /api/v1/videos/generations.
required_tierstring
free, pro, or ultra — the minimum subscription needed to call the model.
pricingobject | null
USD per token (prompt = input, completion = output), when known. Multiply by 1,000,000 for $/1M tokens.
capabilities.visionboolean
Whether the model accepts image inputs in chat messages.
video_capabilitiesobject | null
Supported durations, resolutions, aspect ratios, and frame-image inputs for video models.

Chat completions

OpenAI-compatible chat completion. Only models present in the Durango catalog are accepted (404 model_not_found otherwise).

POST/api/v1/chat/completionsCreate a chat completion
Request body
modelstringrequired
Model id from GET /api/v1/models?type=chat.
messagesarrayrequired
Conversation messages in OpenAI format (see below).
streamboolean
true to receive an SSE stream. Default false.
temperaturenumber
Sampling temperature.
max_tokensinteger
Cap on generated tokens.
top_pnumber
Nucleus sampling.
frequency_penalty / presence_penaltynumber
Repetition controls.
stopstring | string[]
Stop sequences.
seedinteger
Deterministic sampling on supporting models.
response_formatobject
e.g. {"type": "json_object"} for JSON mode.
toolsarray
OpenAI-style function tool definitions, forwarded to the model.
tool_choiceany
Tool selection directive.

Each message is { "role": ..., "content": ... } with roles system, developer, user, assistant, or tool. content is a string — or, for multimodal input on vision-capable models, an array of parts. image_url.url accepts HTTPS URLs or data URLs. Assistant messages with tool_calls and role: "tool" results are supported for function-calling loops.

Multimodal message
{
  "role": "user",
  "content": [
    { "type": "text", "text": "What is in this image?" },
    { "type": "image_url", "image_url": { "url": "data:image/png;base64,iVBORw0..." } }
  ]
}
Request
curl https://durango.sh/api/v1/chat/completions \
  -H "Authorization: Bearer $DURANGO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "anthropic/claude-sonnet-4.5",
    "messages": [
      { "role": "system", "content": "You are a concise assistant." },
      { "role": "user", "content": "Name three uses for a paperclip." }
    ],
    "max_tokens": 200
  }'
Response
{
  "id": "gen-abc123",
  "object": "chat.completion",
  "created": 1750000000,
  "model": "anthropic/claude-sonnet-4.5",
  "choices": [
    {
      "index": 0,
      "message": { "role": "assistant", "content": "1. Holding papers..." },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 28,
    "completion_tokens": 47,
    "total_tokens": 75,
    "credits_used": 0.0789
  }
}

The response is the OpenAI chat.completion shape, extended with usage.credits_used — the exact number of Durango credits charged for the request.

Streaming

Set "stream": true. The response is text/event-stream: a sequence of data: {json} lines, each an OpenAI chat.completion.chunk with incremental choices[0].delta.content, ending with data: [DONE]. A final chunk carries usage; credits are deducted when the stream completes.

Request
curl -N https://durango.sh/api/v1/chat/completions \
  -H "Authorization: Bearer $DURANGO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "openai/gpt-5.2", "stream": true, "messages": [{"role": "user", "content": "Write a haiku about rivers."}]}'
Stream
data: {"id":"gen-x","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":"Sil"}}]}

data: {"id":"gen-x","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":"ver water"}}]}

data: {"id":"gen-x","object":"chat.completion.chunk","choices":[],"usage":{"prompt_tokens":14,"completion_tokens":19}}

data: [DONE]

OpenAI SDKs

Because the endpoint is OpenAI-compatible, official OpenAI SDKs work by overriding the base URL.

Python
from openai import OpenAI

client = OpenAI(
    base_url="https://durango.sh/api/v1",
    api_key="dgo_your_key_here",
)

completion = client.chat.completions.create(
    model="anthropic/claude-sonnet-4.5",
    messages=[{"role": "user", "content": "Hello!"}],
)
print(completion.choices[0].message.content)
JavaScript / TypeScript
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://durango.sh/api/v1",
  apiKey: process.env.DURANGO_API_KEY,
});

const completion = await client.chat.completions.create({
  model: "google/gemini-3-pro-preview",
  messages: [{ role: "user", content: "Hello!" }],
  stream: true,
});

for await (const chunk of completion) {
  process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}

Image generation

Generates an image with one model, saves it to your Durango gallery, and returns it. The call is synchronous: it responds when generation finishes, typically 10–60 seconds, so use a generous HTTP timeout (120s+ recommended). Requires a Pro or Ultra subscription (403 pro_required otherwise).

POST/api/v1/images/generationsGenerate an image
Request body
modelstringrequired
Model id from GET /api/v1/models?type=image.
promptstringrequired
Description of the image to generate.
aspect_ratiostring
1:1 (default), 16:9, 9:16, 4:3, 3:4, …
image_sizestring
1K (default) or 2K.
response_formatstring
url (default) or b64_json to additionally inline base64 image bytes.
source_imagesstring[]
Up to 4 reference images for image-to-image editing; each an HTTPS URL or data URL, max 10 MB.
privateboolean
true keeps the image out of public surfaces. Default false.
Request
curl https://durango.sh/api/v1/images/generations \
  -H "Authorization: Bearer $DURANGO_API_KEY" \
  -H "Content-Type: application/json" \
  --max-time 180 \
  -d '{
    "model": "google/gemini-3-pro-image-preview",
    "prompt": "A watercolor painting of a lighthouse at dawn",
    "aspect_ratio": "16:9",
    "response_format": "b64_json"
  }'
Response
{
  "id": "run_abc123",
  "created": 1750000000,
  "model": "google/gemini-3-pro-image-preview",
  "data": [
    {
      "id": "img_xyz789",
      "url": "https://durango.sh/api/v1/images/img_xyz789",
      "b64_json": "iVBORw0KGgoAAAANS...",
      "content_type": "image/png"
    }
  ],
  "text": null,
  "usage": { "credits_used": 12 }
}
data[].url requires the same API-key authentication to download — it is not a public link. Use response_format: "b64_json" if you want the bytes inline.
text carries any accompanying text the model produced (some models narrate their generations).
Failures (content-policy refusals, model errors) return 502 generation_failed with the upstream message; no credits are charged for failed generations.

Image download

Returns the raw image bytes for any image generated by your account.

GET/api/v1/images/{imageId}Download a generated image
Request
curl https://durango.sh/api/v1/images/img_xyz789 \
  -H "Authorization: Bearer $DURANGO_API_KEY" \
  -o lighthouse.png

Video generation

Starts an asynchronous video generation run, saves completed videos to your Durango gallery, and returns a poll URL. Requires a Pro or Ultra subscription (403 pro_required otherwise).

POST/api/v1/videos/generationsStart a video generation
Request body
modelstringrequired
Model id from GET /api/v1/models?type=video.
model_idsstring[]
Optional multi-model run; model is included with these ids.
promptstringrequired
Description of the video to generate.
aspect_ratiostring
16:9 (default), 9:16, 1:1, etc. depending on model support.
resolutionstring
720p (default), 1080p, 4K, etc. depending on model support.
durationnumber
Seconds, 1-15, depending on model support.
generate_audioboolean
Ask for audio when the selected model supports it.
source_imagesstring[]
Up to 2 reference images; HTTPS URL, data URL, or authenticated /api/v1/images/{id} URL from the same account.
source_image_idsstring[]
Up to 2 generated image ids from the same Durango account.
privateboolean
true keeps the video out of public surfaces. Default false.
Start request
curl https://durango.sh/api/v1/videos/generations \
  -H "Authorization: Bearer $DURANGO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "google/veo-3.1-fast",
    "prompt": "A slow dolly shot through a rainy neon market",
    "aspect_ratio": "16:9",
    "resolution": "720p",
    "duration": 5,
    "private": true
  }'
Start response
{
  "id": "run_abc123",
  "object": "video.generation",
  "created": 1750000000,
  "status": "running",
  "model": "google/veo-3.1-fast",
  "models": ["google/veo-3.1-fast"],
  "poll_url": "https://durango.sh/api/v1/videos/generations/run_abc123",
  "data": []
}
GET/api/v1/videos/generations/{runId}Poll video status
Completed poll response
{
  "id": "run_abc123",
  "object": "video.generation",
  "status": "completed",
  "data": [
    {
      "id": "vid_xyz789",
      "url": "https://durango.sh/api/v1/videos/vid_xyz789",
      "model": "google/veo-3.1-fast",
      "content_type": "video/mp4"
    }
  ],
  "usage": { "credits_used": 50 }
}

Video download

Returns the raw video bytes for any video generated by your account.

GET/api/v1/videos/{videoId}Download a generated video
Request
curl https://durango.sh/api/v1/videos/vid_xyz789 \
  -H "Authorization: Bearer $DURANGO_API_KEY" \
  -o market.mp4

Credit balance

Returns the authenticated account's remaining credits, monthly limit, subscription tier, and period end.

GET/api/v1/creditsGet credit balance
Request
curl https://durango.sh/api/v1/credits \
  -H "Authorization: Bearer $DURANGO_API_KEY"
Response
{
  "object": "credits",
  "balance": 412.37,
  "limit": 500,
  "tier": "pro",
  "period_end": 1751328000000
}

Errors

All errors use a single envelope.

Error envelope
{
  "error": {
    "message": "Human-readable explanation.",
    "type": "invalid_request_error",
    "code": "model_not_found"
  }
}
401missing_api_key
No key in Authorization / X-API-Key header.
401invalid_api_key
Key is malformed, unknown, or revoked.
402insufficient_credits
Credit balance is 0 — upgrade or wait for the monthly reset.
403premium_model_required
Model needs a Pro / Ultra subscription.
403pro_required
Image or video generation needs a Pro / Ultra subscription.
400model_required, invalid_messages, …
Malformed request — the message explains exactly what.
404model_not_found
Model id is not in the catalog — check GET /api/v1/models.
404image_not_found
Image id unknown or owned by another account.
404video_generation_not_found, video_not_found
Video run/id unknown or owned by another account.
4xxupstream_error
The upstream model provider rejected the request (status passed through).
502upstream_unreachable, generation_failed
Upstream failure — safe to retry.

For AI agents

Pointers for autonomous agents working against this API.

Discover capabilities in this order: GET /api/v1/openapi.json (schema) → GET /api/v1/models (what you can call) → GET /api/v1/credits (budget).
Always send Content-Type: application/json on POSTs.
Prefer stream: false unless you need incremental output; the non-streaming response includes exact credits_used.
Image generation is slow (10–60s) — set client timeouts to at least 120 seconds and do not retry while a request is in flight.
Video generation is asynchronous — start the run once, poll poll_url, and download data[].url after completion.
Treat 402 as terminal until the human upgrades or credits reset; treat 502 as retryable.
This entire reference is available as plain markdown at https://durango.sh/docs/llms.txt.