Docs
Sign inStart free
Documentation/Authentication & API keys

Authentication & API keys

Every /v1/* call carries Authorization: Bearer $STELQ_KEY. Keys are stelq_live_ / stelq_test_, the secret is shown exactly once, and you pause, revoke, or expire them in the console. This is the cross-service article the per-service Auth section only summarizes.

4 min readHumans & agentsopen as text

01Bearer token on every request

Every request needs a bearer token: Authorization: Bearer $STELQ_KEY. The gateway requires the literal Bearer prefix — any other scheme, or a missing header, is rejected at the edge with 401 before your request reaches a service. There is no cookie, query-param, or basic-auth fallback; the header is the only credential.

search · cURL
curl https://api.stelq.com/v1/search/query \
  -H "Authorization: Bearer $STELQ_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "query": "best ai search infra 2026", "limit": 10 }'

Read the key from the STELQ_KEY environment variable — never hardcode it and never paste a live secret into a prompt, a chat log, or source control. Every copyable payload in the console (cURL, MCP config, agent prompt) references $STELQ_KEY for exactly this reason: you set it once in your agent's environment, and the credential never travels through a model's context window. In the in-console Playground the key is injected for you, so you can call a live endpoint without ever handling the secret yourself.

Same token, every service. The header above is identical for Search, Content, Research, and Answers — auth is platform-wide, not per-endpoint. Wire it once and every /v1/* call is authenticated.

02Key format and the shown-once secret

STELQ keys are prefixed stelq_live_ or stelq_test_, followed by 32 bytes of base64url randomness — not sk_live_/sk_test_. The prefix makes a leaked key instantly recognizable in logs and scanners.

ElementValue
Live prefixstelq_live_
Test prefixstelq_test_
Random partbase64url(32 bytes)
HeaderAuthorization: Bearer <key>

The plaintext secret is returned exactly once — in the response to the create call, and in the reveal dialog right after. The console never sees it again: only a SHA-256 hash (for lookup at the gateway) and the last 4 characters (for a readable preview in your key list) are stored. A key minted automatically at signup is delivered the same way — once, via a ?newKey= parameter the console reads to show the reveal modal, then immediately strips from the URL and browser history so the secret can't linger in a shared link or back-button.

A lost key cannot be recovered — there is no plaintext to recover. If you close the reveal dialog without copying it, or you misplace it later, create a new key and delete the old one. Rotate, don't retrieve.

03Key states: pause, revoke, expire

A key is active, paused, or revoked, and may also carry an optional expiry date. The gateway resolves the state on every request, so a state change takes effect for the very next call — there is nothing to redeploy.

StateWhat it meansGateway response
activeNormal — the key authenticates and bills your wallet.Request proceeds
pausedTemporarily disabled; flip it back to active anytime.403 — "This API key is paused."
revokedPermanently killed (soft delete — marked revoked and kept in the kill-log; its captured payloads are purged).401 — treated as an invalid key
expiredPast its expiry timestamp.401 — "This API key has expired."

Pause is the reversible control — flip a key off to stop spend during an incident, flip it back on when you're ready, with no new secret to distribute. Revoke is the irreversible one: it permanently marks the key revoked and, in the same operation, purges any raw request/response payloads that key captured (payload logging is off by default and opt-in per key). Revoked keys stay visible in a collapsed kill-log beneath your active keys so you can confirm what you've already shut down.

Expiry is enforced at the gateway: once the current time passes a key's expiry timestamp, every call returns 401. The platform understands fixed expiry windows — 7, 30, or 90 days, 1 year, or never — though new keys created from the current console form default to no expiry (you set a name and an optional monthly spend cap there). Don't rely on expiry as your only off-switch: pause or revoke when you need an immediate, deliberate stop.

One more invariant from the control plane: if your whole account is suspended, all of its keys stop authenticating immediately and uniformly return 401 — the gateway never leaks the underlying reason.

04Two honest caveats

Two things about keys are easy to assume and currently wrong. We'd rather you know them than discover them.

Keys are unscoped. The current backend issues unscoped keys — every key can call every service. There is no per-service or per-endpoint permission boundary and no authorization check beyond "is this key valid and active." When the console or this prose says you can "scope" a key, that means its spend configuration — an optional spend cap (and, where supported, a reset period and expiry) — not a permission boundary. Do not architect around per-service key scopes; they don't exist yet.

Test keys are not a sandbox. The stelq_test_ prefix is a latent field with no behavioral difference: a test key bills the same live wallet, at the same per-call price, against the same services as a live key. There is no free test mode, no sandboxed data, and no live/test toggle in the create form — new keys default to live. Treat a stelq_test_ key exactly like a live credential.

Both caveats are statements about today's backend, not aspirations. If per-service scopes or a true test mode ship later, this article changes with them — until then, plan for one tier of fully-capable, live-billing keys.

05Related