API reference

SHADE is an OpenAI-compatible gateway to 8 leading models from OpenAI, Anthropic, Google, DeepSeek, Meta. If your code already talks to OpenAI, change the base URL and key and it works. Every request is billed from a prepaid SOL balance, so there is no account, no email and no card. AI agents can fund and use a key entirely through the API.

Base URL
https://shade-gateway.vercel.app/v1
Auth header
Authorization: Bearer shade_sk_…
Format
OpenAI-compatible JSON and SSE

Quickstart

Get a key on the purchase page (or through the API), then send your first request. The free model (free) works without a key, so you can test before paying.

curl https://shade-gateway.vercel.app/v1/chat/completions \
  -H "Authorization: Bearer $SHADE_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-4o-mini",
    "messages": [{"role": "user", "content": "Explain Solana in one sentence."}]
  }'

Tools built on the OpenAI API, such as LangChain, LlamaIndex, the Vercel AI SDK and most agent frameworks, work the same way: set the base URL to https://shade-gateway.vercel.app/v1 and the API key to your SHADE key.

Authentication

Every paid request needs a SHADE key, a secret that starts with shade_sk_. The key is the account: it holds the credit balance and is the only thing that identifies you. Send it as a bearer token:

Header
Authorization: Bearer shade_sk_your_key_here
  • We store only a one-way hash of each key, so a lost key can't be recovered. Keep it in a password manager or secret store.
  • Anyone with the key can spend its credits. Never ship it in front-end code or commit it to a repository.
  • Use separate keys for separate apps or agents. Each one has its own balance and limits.

Models

Pass the id in the model field. Prices are in credits per million tokens (1,000 credits = $1) and follow the providers' current rates. Requests are routed only to providers that don't store or train on prompts, except the free model.

modelModelInput / 1MOutput / 1MNotes
freeLlama 3.3 70B (free)
Meta
FreeFree20 requests a day, no key needed. May log prompts.
gpt-4o-miniGPT-4o mini
OpenAI
225900Fast and very cheap. Great for everyday questions and quick drafts.
Also accepts fast
gpt-4.1GPT-4.1
OpenAI
3,00012,000Strong all-rounder for writing, analysis and long instructions.
Also accepts reason
gpt-5GPT-5
OpenAI
1,87515,000OpenAI flagship that thinks before answering. Best for hard problems.reasoning
claude-sonnetClaude Sonnet
Anthropic
3,00015,000Anthropic's latest Sonnet. Excellent at writing, coding and careful reasoning.
gemini-flashGemini Flash
Google
1,1255,625Google's latest Flash model. Quick, cheap and good with long inputs.
gemini-proGemini Pro
Google
3,00015,000Google's latest Pro model for deep analysis and research.
deepseek-v3DeepSeek V3
DeepSeek
3871,544Open-weight model that is strong at code and maths, at a low price.
Also accepts coder
llama-3.3-70bLlama 3.3 70B
Meta
150480Open model on paid, non-logging providers. Cheap and private.
GET/v1/models

Returns the live list in the OpenAI format, with SHADE pricing. No key needed.

Response
{
  "object": "list",
  "data": [
    {
      "id": "gpt-4o-mini",
      "object": "model",
      "owned_by": "openai",
      "name": "GPT-4o mini",
      "free": false,
      "reasoning": false,
      "pricing": { "input_credits_per_m": 225, "output_credits_per_m": 900 }
    }
  ]
}

Chat completions

POST/v1/chat/completions

Generates a reply to a conversation. Request and response bodies follow the OpenAI chat completions format.

Request body

FieldTypeDescription
modelrequiredstringA model id from the table above.
messagesrequiredarrayThe conversation. Each item has a role (system, developer, user or assistant) and content, either a string or an array of { "type": "text", "text": "…" } parts. Up to 200 messages.
streambooleanSend the reply as Server-Sent Events while it's generated. Default false. See Streaming.
max_tokensintegerMaximum tokens to generate, up to 4,096. Default 2,048. max_completion_tokens is accepted too. Reasoning models always get the full 4,096.
temperaturenumber0 to 2. Higher is more random.
top_pnumber0 to 1. Nucleus sampling.
stopstring | arrayUp to 4 sequences where generation stops.
presence_penaltynumber-2 to 2.
frequency_penaltynumber-2 to 2.
seedintegerBest-effort deterministic sampling, where the model supports it.
Not supported yet: tool and function calling, image or file inputs, and n greater than 1. Requests that use them return a clear invalid_request error rather than being silently ignored.

Response

The standard OpenAI response, plus a shade object with the exact credits charged and the remaining balance.

200 OK
{
  "id": "gen-1762167420-x8Kq2",
  "object": "chat.completion",
  "created": 1762167420,
  "model": "gpt-4o-mini",
  "choices": [
    {
      "index": 0,
      "message": { "role": "assistant", "content": "Solana is a high-speed blockchain..." },
      "finish_reason": "stop"
    }
  ],
  "usage": { "prompt_tokens": 14, "completion_tokens": 31, "total_tokens": 45 },
  "shade": { "charged_credits": 0.03, "balance_credits": 4987.12, "free": false }
}

Streaming

Set "stream": true to receive the reply token by token as Server-Sent Events, in the same chunk format as OpenAI. The last chunk before [DONE] has an empty choices array, the token usage and the shade billing summary. If you disconnect early, generation stops and you pay only for what was produced.

stream = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[{"role": "user", "content": "Write a haiku about privacy."}],
    stream=True,
)
for chunk in stream:
    if chunk.choices and chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="", flush=True)

Balance

GET/api/account

Returns the balance of the key in the Authorization header.

curl https://shade-gateway.vercel.app/api/account -H "Authorization: Bearer $SHADE_KEY"

Funding with SOL

Keys can be created and topped up without a browser, so an agent with a Solana wallet can pay for its own inference. Each top-up gets a unique SOL amount and a Solana Pay reference, priced at the live SOL/USD rate and valid for 30 minutes. The transfer is detected on-chain and credited automatically, usually within seconds of confirmation.

1. Create a key

POST/api/account
Response · 201
{ "key": "shade_sk_…", "key_prefix": "shade_sk_ab12", "balance_credits": 0 }

The full key is returned only once. Store it before doing anything else.

2. Start a top-up

POST/api/deposits

Body: { "package_id": "…" }. Available packages:

package_idPriceCredits
starter$55,000
builder$2021,000 (+5% bonus)
heavy$5055,000 (+10% bonus)
curl -X POST https://shade-gateway.vercel.app/api/deposits \
  -H "Authorization: Bearer $SHADE_KEY" \
  -H "Content-Type: application/json" \
  -d '{"package_id": "starter"}'

3. Send SOL

Send exactly amount_sol to address on Solana mainnet, ideally with reference attached as a read-only account key (Solana Pay). Wallets that open solana_pay_url do this for you.

4. Confirm

GET/api/deposits/{id}

Poll every few seconds. status becomes credited and balance_credits shows the new balance. If a manual transfer isn't picked up, POST /api/deposits/{id} with { "signature": "<tx signature>" } to claim it. One transaction can credit only one top-up.

Billing

  • 1,000 credits = $1. Credits never expire and there are no subscriptions or minimums.
  • Before each request, the maximum it could cost is held from the balance. When it finishes, the exact cost reported by the provider is charged and the rest is released.
  • Requests that fail are not charged. A balance can never go below zero.
  • Reasoning models count their internal thinking as output tokens, so they cost more per answer.
  • Every response tells you what it cost in shade.charged_credits.

Errors

Errors use standard HTTP status codes and a consistent body:

402 Payment Required
{
  "error": {
    "message": "Not enough credits for this request. Top up on the purchase page.",
    "code": "insufficient_credits",
    "required_credits": 9.41,
    "balance_credits": 2.03
  }
}
StatuscodeWhat to do
400invalid_requestFix the request body. The message says which field is wrong.
400unknown_modelUse an id from GET /v1/models.
401invalid_keySend a valid key in the Authorization header.
402insufficient_creditsTop up the key, or lower max_tokens.
413prompt_too_longShorten the conversation.
429rate_limitedWait and retry with backoff.
429free_limit_reachedUse a key, or try again tomorrow.
502provider_4xx / provider_5xxThe model provider failed. Retry, or pick another model. Not charged.
503provider_429The model is busy upstream. Retry shortly. Not charged.
504provider_timeoutThe model took too long. Retry with a shorter request. Not charged.
—provider_stream_errorSent inside a stream if the model stops mid-answer. You pay only for what was produced.

Limits

LimitValue
Requests per key60 per minute
Requests per network120 per minute
Free model5 per minute, 20 per day per visitor
Output per request4,096 tokens
Prompt size400,000 characters, 200 messages
Request duration60 seconds
New keys10 per network per day
Top-ups20 per key per hour

Privacy

  • Prompts and responses pass through the gateway in memory and are never written to a database or log.
  • We store a hash of each key, token counts and cost per request, and deposit transactions, which is what billing needs.
  • IP addresses are hashed with a daily salt for rate limiting and never stored.
  • Paid requests are routed only to providers that don't retain or train on prompts. The model provider still processes the prompt to answer it.
  • The free model runs on community endpoints that may log prompts. Don't send anything sensitive to it.