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.
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:
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.
| model | Model | Input / 1M | Output / 1M | Notes |
|---|---|---|---|---|
| free | Llama 3.3 70B (free) Meta | Free | Free | 20 requests a day, no key needed. May log prompts. |
| gpt-4o-mini | GPT-4o mini OpenAI | 225 | 900 | Fast and very cheap. Great for everyday questions and quick drafts. Also accepts fast |
| gpt-4.1 | GPT-4.1 OpenAI | 3,000 | 12,000 | Strong all-rounder for writing, analysis and long instructions. Also accepts reason |
| gpt-5 | GPT-5 OpenAI | 1,875 | 15,000 | OpenAI flagship that thinks before answering. Best for hard problems.reasoning |
| claude-sonnet | Claude Sonnet Anthropic | 3,000 | 15,000 | Anthropic's latest Sonnet. Excellent at writing, coding and careful reasoning. |
| gemini-flash | Gemini Flash Google | 1,125 | 5,625 | Google's latest Flash model. Quick, cheap and good with long inputs. |
| gemini-pro | Gemini Pro Google | 3,000 | 15,000 | Google's latest Pro model for deep analysis and research. |
| deepseek-v3 | DeepSeek V3 DeepSeek | 387 | 1,544 | Open-weight model that is strong at code and maths, at a low price. Also accepts coder |
| llama-3.3-70b | Llama 3.3 70B Meta | 150 | 480 | Open model on paid, non-logging providers. Cheap and private. |
Returns the live list in the OpenAI format, with SHADE pricing. No key needed.
{
"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
Generates a reply to a conversation. Request and response bodies follow the OpenAI chat completions format.
Request body
| Field | Type | Description |
|---|---|---|
| modelrequired | string | A model id from the table above. |
| messagesrequired | array | The 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. |
| stream | boolean | Send the reply as Server-Sent Events while it's generated. Default false. See Streaming. |
| max_tokens | integer | Maximum tokens to generate, up to 4,096. Default 2,048. max_completion_tokens is accepted too. Reasoning models always get the full 4,096. |
| temperature | number | 0 to 2. Higher is more random. |
| top_p | number | 0 to 1. Nucleus sampling. |
| stop | string | array | Up to 4 sequences where generation stops. |
| presence_penalty | number | -2 to 2. |
| frequency_penalty | number | -2 to 2. |
| seed | integer | Best-effort deterministic sampling, where the model supports it. |
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.
{
"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
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
{ "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
Body: { "package_id": "…" }. Available packages:
| package_id | Price | Credits |
|---|---|---|
| starter | $5 | 5,000 |
| builder | $20 | 21,000 (+5% bonus) |
| heavy | $50 | 55,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
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:
{
"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
}
}| Status | code | What to do |
|---|---|---|
| 400 | invalid_request | Fix the request body. The message says which field is wrong. |
| 400 | unknown_model | Use an id from GET /v1/models. |
| 401 | invalid_key | Send a valid key in the Authorization header. |
| 402 | insufficient_credits | Top up the key, or lower max_tokens. |
| 413 | prompt_too_long | Shorten the conversation. |
| 429 | rate_limited | Wait and retry with backoff. |
| 429 | free_limit_reached | Use a key, or try again tomorrow. |
| 502 | provider_4xx / provider_5xx | The model provider failed. Retry, or pick another model. Not charged. |
| 503 | provider_429 | The model is busy upstream. Retry shortly. Not charged. |
| 504 | provider_timeout | The model took too long. Retry with a shorter request. Not charged. |
| — | provider_stream_error | Sent inside a stream if the model stops mid-answer. You pay only for what was produced. |
Limits
| Limit | Value |
|---|---|
| Requests per key | 60 per minute |
| Requests per network | 120 per minute |
| Free model | 5 per minute, 20 per day per visitor |
| Output per request | 4,096 tokens |
| Prompt size | 400,000 characters, 200 messages |
| Request duration | 60 seconds |
| New keys | 10 per network per day |
| Top-ups | 20 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.