STOP WASTING PAID TOKENS. START POOLING ACCOUNTS TODAY. [ GET YOUR VAULT ]

API Reference

This is the complete list of SpiderGate endpoints. Everything the dashboard, the SDKs, and your agents do goes through these. They're OpenAI-compatible, so an OpenAI client pointed at the base URL works against them directly.

Base URL

https://spideriq.ai/api/gate/v1

All paths below are relative to that base. For the OpenAI SDK, set base_url="https://spideriq.ai/api/gate/v1".

Authentication

Endpoints that read or write your data require a bearer token. The model-listing endpoints and /catalog/leaderboard are public; the rest of the catalog surface needs a token. See Authentication for the two token formats and Model Catalog for that surface.

Authorization: Bearer <client_id>:<api_key>:<api_secret>
Authorization: Bearer spideriq_pat_…

Chat & generation

These require authentication.

POST /chat/completions

Create a chat completion (streaming or non-streaming). The body is the OpenAI chat-completion request; model may be a task alias or a model id.

  • Query: include_route_trace (boolean, default false) — inline a per-attempt route trace in the response (non-streaming).

  • Response headers: X-SpiderGate-Fallback-From — present (non-streaming) when the served model differs from the alias's first choice.

  • Full parameters: Chat Completions. Streaming: Streaming.

POST /images/generations

Generate images with dall-e-3, dall-e-2, or gpt-image-1. Requires an OpenAI key in the vault. See Images, Audio & Embeddings.

POST /audio/speech

Text-to-speech with tts-1, tts-1-hd, or gpt-4o-mini-tts. Returns audio bytes. Voices: alloy, echo, fable, onyx, nova, shimmer. Formats: mp3, opus, aac, flac, wav, pcm.

POST /audio/transcriptions

Speech-to-text with whisper-1, gpt-4o-transcribe, or gpt-4o-mini-transcribe. multipart/form-data; file cap 25 MB.

POST /embeddings

Vector embeddings with text-embedding-3-large, text-embedding-3-small, or text-embedding-ada-002. dimensions up to 3072. Also accepts the SpiderGate aliases agent/embed-small, agent/embed-large and agent/embed, which resolve server side to the matching model. See Images, Audio & Embeddings.

Media (schema-aware)

The two endpoints below are not the OpenAI passthrough above. They route through SpiderGate's adapter-backed pipeline, honour each model's declared parameter schema, and return a stored URL rather than raw bytes. Every media model is paid tier and never pooled, so your brand must hold its own provider key.

GET /media/models

List the media models an agent can call, each with the inputs schema it declares. Read a model's inputs before calling POST /media/generations — parameters the model does not declare are dropped before the provider call.

Parameters

  • modality (string, optional) — filter to one modality, e.g. text-to-image, text-to-video, image-to-video, tts, lipsync.

  • include_inactive (boolean, optional, default false) — also return coming_soon and disabled models, which are not generatable.

Example

curl "https://spideriq.ai/api/gate/v1/media/models?modality=text-to-video" \
  -H "Authorization: Bearer $CLIENT_ID:$API_KEY:$API_SECRET"

Response

{
  "models": [
    {
      "id": "kie/veo-3-fast",
      "provider": "kie_ai",
      "modality": "text-to-video",
      "modality_group": "video",
      "status": "active",
      "inputs": {
        "prompt":       { "type": "string", "required": true, "control": "textarea" },
        "aspect_ratio": { "type": "enum", "default": "16:9",
                          "enum": ["16:9", "9:16", "1:1"], "control": "segmented" }
      }
    }
  ],
  "total": 1
}

Errors:

Status

Code

Trigger

Resolution

401

authentication_error

the bearer token is missing, malformed, or revoked

check the token format in Authentication

429

—

the token's per-minute or per-day limit is exhausted

back off, or raise the limit in Rate Limits & Budgets

POST /media/generations

Generate an image, video, or speech clip from a <provider>/<model> descriptor id. Returns a stored SpiderMedia URL plus a cost breakdown. Video generation is synchronous and can take minutes.

Parameters

  • model (string, required, 1–128 chars) — the descriptor id from GET /media/models, e.g. fal/flux-dev, kie/veo-3-fast, openai/tts-1.

  • params (object, optional) — the per-model tunables, validated against that model's declared inputs. Extra top-level fields are folded into params, so {"model": …, "seed": 7} and {"model": …, "params": {"seed": 7}} are equivalent; an explicit params entry wins on a clash.

  • prompt (string, optional) — a saved-prompt reference, either prompt:<public_id> or prompt.<slug>. The server expands the stored prompt text, model and settings; anything you pass explicitly overrides it.

  • project_id (string, optional) — required only to resolve a prompt.<slug> reference.

Example

curl -X POST "https://spideriq.ai/api/gate/v1/media/generations" \
  -H "Authorization: Bearer $CLIENT_ID:$API_KEY:$API_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "kie/veo-3-fast",
    "params": {
      "prompt": "a slow pan across an empty server room",
      "aspect_ratio": "16:9"
    }
  }'

Response

{
  "id": "media-6ff96279d9894632b730aa92",
  "model": "kie/veo-3-fast",
  "provider": "kie_ai",
  "request_kind": "video",
  "cost_usd": 0,
  "billed_usd": 0,
  "est_cost": {
    "provider_cost_usd": 0,
    "billed_usd": 0,
    "markup_pct": 0,
    "key_ownership": "byok",
    "basis": "byok_monthly_package",
    "currency": "USD"
  },
  "stored": true,
  "data": [ { "url": "https://media.spideriq.ai/client-…/gate-media/….png" } ],
  "ignored_params": [],
  "warnings": []
}

ignored_params names any parameter the model did not declare, so a silently dropped field is still visible to you. billed_usd is 0 when the generation ran on your own paid key.

Errors:

Status

Code

Trigger

Resolution

400

missing_text

a text-to-speech call arrived with no text input

pass the model's declared text field

404

model_not_found

no media model has that descriptor id

list valid ids with GET /media/models

422

invalid_param

a declared parameter carried an illegal value

the message names the field and lists accepted values; raised before a key is selected, so nothing is spent

501

adapter_not_available

the model is active but its provider has no generation adapter

pick another model of the same modality

502

generation_failed

the provider failed to generate

retry once, then try another model

502

empty_result

the provider returned no image or audio bytes

retry once, then try another model

502

decode_failed

the provider returned an undecodable image

retry once, then try another model

503

model_not_available

the model is coming_soon or disabled

pick an active model from GET /media/models

503

no_<provider>_key

your brand holds no key for that provider, e.g. no_kie_ai_key

register one at The Key Vault

503

malformed_credential

the stored credential for that provider has no api_key

re-enter the key in the vault

504

generation_timeout

the run exceeded the wall-clock cap: 600 s image and speech, 900 s video

retry, or choose a faster model

Note that a long video generation can outlive an intermediate proxy timeout even though the run completes and is billed. If a call returns a gateway timeout, check GET /media/models usage or your Traces before resending — the asset may already exist.

Catalog (public, no auth)

GET /models

List the model catalog in OpenAI list format, each entry with a spidergate_info block. Query params: provider, free_only, configured_only, search, capability, sort_by, limit (1–2000), offset. See Models.

GET /models/{model_id}

Return one model, or 404 model_not_found if unknown.

GET /aliases

List every task alias with its ranked model fallback chain and its 30-day usage. Public, no auth. This is the authoritative list: the 32 aliases and 91 chain slots below move as providers change, so read them here rather than hardcoding them. See Task Aliases.

Parameters: none.

Example

curl "https://spideriq.ai/api/gate/v1/aliases"

Response — 200, an OpenAI-style list. data holds 32 entries; models is the fallback chain in priority order, index 0 first.

{
  "object": "list",
  "data": [
    {
      "id": "spideriq/lead-analysis",
      "description": "B2B lead analysis and scoring",
      "use_case": "Company vitals, pain points, CHAMP scoring, team extraction",
      "models": [
        { "provider": "cerebras", "model": "gpt-oss-120b", "spidergate_id": null },
        { "provider": "groq", "model": "llama-3.3-70b-versatile", "spidergate_id": "llama-3.3-70b-groq" },
        { "provider": "minimax", "model": "MiniMax-M2.5", "spidergate_id": "minimax-m2.5-qwen" }
      ],
      "usage": { "requests_30d": 43863, "tokens_30d": 301092346, "cost_30d": 203.16232776 }
    }
  ]
}

Errors:

::table
Status | Code | When and what to do
`503` | `service_unavailable` | The routing engine is still starting up. Retry after a short backoff; no request body change is needed.

A spidergate_id of null means the model is served under its provider-native id rather than a SpiderGate catalog id. Read models[0] to know which model an alias currently leads with, and the array length to know how deep its chain runs.

GET /providers

List the upstream providers SpiderGate can route to, with model counts and key status.

GET /health

Process liveness check.

curl "https://spideriq.ai/api/gate/v1/health"
# {"status":"healthy","service":"spidergate","version":"…"}

Subscriptions (what YOUR brand can pin)

GET /subscriptions

Every LLM subscription your brand holds, the concrete model ids you may pin, whether each one is usable right now, how the subscription is reached, and — for a leased credential — how many accounts are free.

This is not the catalog. GET /models lists every model SpiderGate knows about, for everybody; this endpoint answers the narrower question a routing planner actually has: *of those, which may this brand complete on, with which of its own subscriptions?* Before it existed, the only way to find out was to send a completion and read the 400 — and a 400 cannot tell a model you are not entitled to from a model that does not exist.

The brand is the credential's. There is no brand_id parameter on this route and no header that sets one; you get your own brand or nothing.

Parameters

::table
Name | Type | Default | What it does
`include_unconfigured` | boolean | `false` | Include catalog rows never marked configured. Off by default because a provider can carry 160+ rows and an unconfigured one has not been set up to serve. On brand 7 today this moves the model count from 25 to 471.
`consumer` | string, 1–128 chars | none | For **lease** subscriptions only, the consumer id you would lease as (e.g. `opvs_runner:env_9f3c…`). Supplying it makes the entitlement answer *your* case instead of a generic one, so you learn about a `consumer_not_permitted` refusal here rather than at lease time.

Example

curl -H "Authorization: Bearer $CLIENT_ID:$API_KEY:$API_SECRET" \
  "https://spideriq.ai/api/gate/v1/subscriptions"

Response — 200. subscriptions holds one entry per credential your brand has vaulted, ordered by provider.

{
  "brand_id": 7,
  "subscriptions": [
    {
      "provider": "minimax",
      "integration_id": 644,
      "label": "Contributed by sheinmartin",
      "cost_type": "subscription",
      "usage": "completion",
      "harness": null,
      "is_active": true,
      "health_status": "healthy",
      "usable": true,
      "unusable_reason": null,
      "lease": null,
      "models": [
        {
          "id": "minimax-m2.5-qwen",
          "provider_model_id": "MiniMax-M2.5",
          "display_name": "MiniMax-M2.5",
          "context_window": 204800,
          "max_output": 196608,
          "supports_tools": true,
          "entitled": true,
          "not_entitled_reason": null,
          "probe_status": "ok"
        }
      ]
    },
    {
      "provider": "anthropic",
      "integration_id": 645,
      "cost_type": "subscription",
      "usage": "lease",
      "harness": "anthropic-claude-code",
      "is_active": true,
      "usable": true,
      "unusable_reason": null,
      "lease": { "total_slots": 1, "free_slots": 1 },
      "models": []
    }
  ]
}

Errors:

::table
Status | Code | When and what to do
`401` | `missing_api_key` | No `Authorization` header, or a malformed one. Send `Bearer <client_id>:<api_key>:<api_secret>`.
`403` | `brand_unbound` | The credential authenticated but is not bound to a brand, so there is no tenant to answer for. Use a brand-scoped client credential.
`503` | `db_unavailable` | The routing engine is still starting up. Retry after a short backoff; nothing about the request needs to change.
`500` | `internal_error` | An unexpected failure. The detail is deliberately generic; quote the `x-trace-id` response header when reporting it.

An empty subscriptions: [] is a 200, not a 404 — it means your brand has vaulted no LLM credentials.

Which id to pin

id is the name that works — pass it as model on /chat/completions. provider_model_id is what the provider calls the same thing, and it is informational. They are often different: MiniMax-M2.5 is minimax-m2.5-qwen to us.

Pinning the provider_model_id form was the reported defect this endpoint was built for, and it is now partly fixed: since brand-owned direct routing registers the provider-native id too, MiniMax-M2.5 also returns 200 today. Pin id anyway — it is the form that is guaranteed, on every subscription, and the one the catalog is keyed by.

Completion rows and lease rows

::table
`usage` | What it means | Which fields are populated
`completion` | You call `POST /chat/completions` and SpiderGate reaches the provider for you. | `models[]` is the menu. `harness` and `lease` are `null`.
`lease` | You borrow the credential and **your** runtime calls the provider. SpiderGate is never in the completion path. | `lease.free_slots` / `lease.total_slots` and `harness` are populated. `models[]` is deliberately **empty** — a leased credential is not a menu; your CLI chooses.

An empty models[] on a lease row is a correct answer, not a truncated one.

harness names the build runtime the credential is injected into. It is derived from our inject-recipe registry, not a stored column, and only two values are reachable from this endpoint today: anthropic-claude-code and gemini-cli. (A third recipe, openai-codex, exists in the registry but its provider is not inject-only, so it never appears here.)

Entitlement is not availability

This is the one thing to get right before you pin anything.

::table
Field | The claim it makes | What it cannot tell you
`entitled` | **We** permit this brand to complete on this model with this subscription, right now. Computed with the same predicate the resolver uses, so it can never promise something routing would refuse. | Whether the provider will actually answer.
`probe_status` | We have **seen** this model answer — the outcome of its last health probe. | Nothing, when it is `null`: that means never probed, not healthy.

entitled: true, probe_status: null is an unverified claim, and it is the common case rather than an edge case. Measured on brand 7 on 2026-08-31, across the 25 entitled models the endpoint returned:

  probe_status: "ok"          2
  probe_status: "error"       8
  probe_status: "delisted"    2
  probe_status: null         13

Two of twenty-five had been seen to answer. On the same call, four models reporting entitled: true were refused by their provider on a real completion — two 404, two 429 — and all four carried probe_status: null. The endpoint did not lie; permission was genuinely ours, and on the same credentials qwen3-coder-next and deepseek-chat both served 200.

The rule for a planner:

  • entitled: false — do not pin it. Read not_entitled_reason to know why.

  • entitled: true, probe_status: "ok" — the strongest signal available. Prefer these.

  • entitled: true, probe_status: null — permitted but unproven. Pin it only with a fallback, and treat a provider 404/429 as expected rather than exceptional.

  • entitled: true, probe_status: "error" or "delisted" — we have seen it fail. Prefer another model.

Refusal codes

not_entitled_reason on a model (null when entitled is true):

::table
Code | Meaning
`key_missing` | No credential row backed the check.
`key_inactive` | The subscription is deactivated — often auto-deactivated after repeated failures.
`key_not_owned` | The credential belongs to a different brand. Re-checked at read time even though it was proved at write time.
`key_inject_only` | The provider is lease-only by ToS; it is never a completion path, whoever owns the key.
`key_type_forbids_model` | A free-purpose key reaching a model that is not free **at that provider**. `(provider, model)` is the unit — the same model name is free at one provider and metered at another.

unusable_reason on a subscription. On a completion row it is no_configured_models, or the refusal of its first model. On a lease row it is one of:

::table
Code | Meaning
`key_inactive` | The subscription is deactivated.
`brand_mismatch` | The credential is not your brand's.
`activity_not_permitted` | The key is not authorized for `interactive_cli` activity.
`consumer_not_permitted` | Your `consumer` is not on the key's allowlist. The lease lane fails **closed** on an absent allowlist — unlike the pool path, a subscription must be opted **in** to a named runner, so a freshly connected credential is born un-leasable until someone adds the consumer.

Free slots are a floor, never a reservation

total_slots is the number of accounts the lease would consider — active credentials on an OAuth auth type — not a count of every vaulted row. free_slots is total_slots minus the accounts currently held.

It is a floor. Leases expire on a TTL and are released silently, so the true number can only rise between this read and your next request. Use it to size a parallel build; do not treat it as capacity you have been granted. Two callers reading free_slots: 3 at the same moment can both start three builds, and one of them will meet 409 no_free_account.

If Redis is unreachable the read degrades to free_slots: 0 with the real total_slots rather than failing — so a 0 here means "no free slot, or we could not tell".

Endpoint summary

Authenticated:

  • POST /chat/completions — chat completion, streaming and non-streaming

  • POST /media/generations — schema-aware image, video and speech generation

  • GET /media/models — media models and the parameter schema each one declares

  • POST /images/generations — image generation, OpenAI passthrough

  • POST /audio/speech — text-to-speech, returns audio bytes

  • POST /audio/transcriptions — transcription, multipart upload

  • POST /embeddings — vector embeddings

  • GET /subscriptions — your brand's own subscriptions, the models it may pin, and free lease slots

Public, no token:

  • GET /models — list models

  • GET /models/{model_id} — get one model

  • GET /aliases — list task aliases and their fallback chains

  • GET /providers — list providers

  • GET /health — liveness check

Observability

Every response carries enough to reconcile what your code saw with what the Traces view shows. All of it is on the normal bearer-authenticated surface; none of it needs a second call.

Response headers

  • x-trace-id — present on every response. The trace identifier for this request. Log it, then paste it into the Traces search box to find this exact request.

  • X-SpiderGate-Fallback-From — present on non-streaming responses only when the served model differs from the alias's first choice. Its value is the model the alias would have used, so its presence means a fallback occurred.

The response body's model field is always the model that actually served the request, which is not necessarily the model you sent. Compare the two to detect a silent fallback.

curl -sD - -X POST "https://spideriq.ai/api/gate/v1/chat/completions" \
  -H "Authorization: Bearer $CLIENT_ID:$API_KEY:$API_SECRET" \
  -H "Content-Type: application/json" \
  -d '{"model":"spideriq/fast","messages":[{"role":"user","content":"hello"}]}' \
  -o /dev/null | grep -i 'x-trace-id\|fallback-from'
# x-trace-id: 441c7118-77b3-4e33-a369-d9edcff2123d

POST /chat/completions?include_route_trace=true

Add ?include_route_trace=true to POST /chat/completions to inline the per-attempt dispatch detail in the response body (non-streaming). Use it when you need to know why a request took the path it did.

curl -s -X POST "https://spideriq.ai/api/gate/v1/chat/completions?include_route_trace=true" \
  -H "Authorization: Bearer $CLIENT_ID:$API_KEY:$API_SECRET" \
  -H "Content-Type: application/json" \
  -d '{"model":"spideriq/fast","messages":[{"role":"user","content":"hello"}]}'

The response gains a route_trace array, one entry per dispatch attempt:

{
  "id": "chatcmpl-5e780d89033f433ea999879e",
  "model": "nvidia_nim/meta/llama-3.1-8b-instruct",
  "route_trace": [
    { "attempt": 0, "path": "router", "model": "spideriq/fast", "outcome": "success", "latency_ms": 390 }
  ]
}

Each entry carries:

  • attempt (integer) — zero-based attempt index. More than one entry means the first choice did not serve.

  • path (string) — which dispatch path handled the attempt, for example router.

  • model (string) — the model or alias tried on this attempt.

  • outcome (string) — success, or the failure classification for that attempt.

  • latency_ms (integer) — wall-clock time for the attempt.

Errors: include_route_trace is advisory and never changes the status code: an unrecognised value is ignored and the request proceeds without a trace. Authentication, budget, model-permission and rate-limit failures return their usual 401 / 402 / 403 / 429 before any dispatch happens, so no route_trace is present on those responses. See Errors.

Reading traces back

There is no bearer-authenticated endpoint for querying stored traces. Trace history is a dashboard surface, described in Traces; the x-trace-id header above is how you correlate a request your code made with its row there.

Dashboard endpoints

The dashboard surfaces (agents, usage, traces, vault) are served by brand-scoped endpoints under /api/v1/brands/{brand_id}/gate/* and are authenticated by your dashboard session rather than a gateway bearer token. They power the dashboard and are not part of the OpenAI-compatible surface; use the dashboard UI to drive them.

Next steps

  1. Authenticate your calls — Authentication.

  2. Handle failures correctly — Errors.

  3. Stay within limits — Rate Limits & Budgets.