Sign-up and API keys are open. Paid top-ups open soon.What changed
Docs menu

Reference

Authentication and API keys

Send your API key as a bearer token, check which key you use, and validate the key format and checksum offline.

View as Markdown

Every public route takes an API key in the authorization header. Keys come from the console at https://thinqit.ai/console/: sign in with your email address and create a test or live key. Test keys are free. This page covers the header, the two kinds of key, scopes, how to check which key you are using, and the key format with its built-in checksum.

Send the key

curl https://api.thinqit.ai/v1/models \
  -H "authorization: Bearer $DEX_API_KEY"

The SDKs and the CLI read the key from the DEX_API_KEY environment variable, or take it as an argument. Never put a key in source code, a URL or a log line.

A missing key gives 401 missing_api_key. A key that does not exist gives 401 invalid_api_key. A revoked or expired key gives 401 revoked_api_key or 401 expired_api_key. See Errors.

Live keys and test keys

Kind Starts with Billed Serving path Limits
Live dex_live_ Yes, from your prepaid balance GPU, with the fallback when allowed 300 requests and 300,000 tokens per minute by default
Test dex_test_ No GPU only 30 requests and 40,000 tokens per minute, 250,000 tokens a day per account

See Rate limits and Tokens and billing.

Scopes

A key carries one or more scopes. A new key gets all three unless you narrow it in the console.

Scope Allows
decide POST /v1/decide
usage:read GET /v1/usage
balance:read GET /v1/balance

GET /v1/models and GET /v1/key work with any valid key, whatever its scopes. A key without the scope a route needs gets 403 insufficient_scope.

Check which key you are using

GET /v1/key returns the key that made the call: its id, display prefix, mode, scopes, account and creation time. It never returns the secret. Use it to confirm a deployment has the right key.

curl https://api.thinqit.ai/v1/key \
  -H "authorization: Bearer $DEX_API_KEY"
from thinqit_dex import Client

key = Client().get_key()
print(key.prefix, key.mode, key.scopes, key.account_id)
import { Client } from "@thinqit/dex";

const key = await new Client().getKey();
console.log(key.prefix, key.mode, key.scopes, key.account_id);
dex whoami
{
  "object": "key",
  "id": "key_01M54VQCG06CQ643DZVMXXQKFB",
  "prefix": "dex_live_Q7mK",
  "mode": "live",
  "scopes": ["decide", "usage:read", "balance:read"],
  "account_id": "acc_01M4ZPXYG0F5KZNWJ47TAN9ZT2",
  "created": 1792225800
}

The values above are illustrative. See GET /v1/key in the API reference.

Key format and checksum

A key is a mode prefix followed by 40 base62 characters: 34 random characters (about 202 bits) and a 6-character checksum. The checksum lets secret scanners and your own tools reject a mistyped key without calling the API.

  • Alphabet. 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz, with digit values 0 to 61 in that order.
  • Shape. The key matches ^dex_(live|test)_([0-9A-Za-z]{34})([0-9A-Za-z]{6})$.
  • Checksum input. The UTF-8 bytes of the mode prefix plus the 34 random characters (43 bytes).
  • Checksum. CRC-32 as in IEEE 802.3 and zlib, as an unsigned 32-bit number, written in base62 with the most significant digit first and left-padded with 0 to exactly 6 characters.
  • Test vectors. dex_live_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0JnKxX and dex_test_0123456789abcdefghijABCDEFGHIJxyzX1wdXRa are well formed. Changing any one character of either breaks the checksum.

A matching checksum proves only that the key was not mistyped. It does not prove that the key exists or is active. Only the API can tell you that, for example with GET /v1/key.

import re
import zlib

ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
KEY = re.compile(r"^(dex_(?:live|test)_)([0-9A-Za-z]{34})([0-9A-Za-z]{6})$")


def key_is_well_formed(key: str) -> bool:
    m = KEY.fullmatch(key)
    if not m:
        return False
    crc = zlib.crc32((m.group(1) + m.group(2)).encode("utf-8"))
    digits = ""
    for _ in range(6):
        crc, r = divmod(crc, 62)
        digits = ALPHABET[r] + digits
    return digits == m.group(3)
const ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
const KEY = /^(dex_(?:live|test)_)([0-9A-Za-z]{34})([0-9A-Za-z]{6})$/;

function crc32(bytes: Uint8Array): number {
  let crc = 0xffffffff;
  for (const b of bytes) {
    crc ^= b;
    for (let k = 0; k < 8; k++) crc = crc & 1 ? (crc >>> 1) ^ 0xedb88320 : crc >>> 1;
  }
  return (crc ^ 0xffffffff) >>> 0;
}

export function keyIsWellFormed(key: string): boolean {
  const m = KEY.exec(key);
  if (!m) return false;
  let n = crc32(new TextEncoder().encode(m[1] + m[2]));
  let digits = "";
  for (let i = 0; i < 6; i++) {
    digits = ALPHABET[n % 62] + digits;
    n = Math.floor(n / 62);
  }
  return digits === m[3];
}

The CLI runs this check before it stores or sends a key: dex login refuses a mistyped key with the error code invalid_key_checksum.

Keeping keys safe

  • Shown once. The console shows a key's secret only when you create it. Dex stores only a SHA-256 hash of the key and a display prefix, such as dex_live_Q7mK.
  • Revocation is fast. A key you revoke in the console stops working on every gateway replica within about a second.
  • Expiry and limits. A key can have an expiry date and its own limits, at or below the account caps.
  • Leaked keys. We are registering the key format with GitHub secret scanning. Once that is in place, a key found in a public repository is revoked automatically and the account owner gets an email.
  • Test keys for development. Use a test key on development machines and in CI. It is never billed.