The creator of an AI coin can get their own API key for that coin's AI and use it from any app, bot or script that speaks the OpenAI chat API. Requests are paid from the coin's AI vault in USDC, under the coin's rules, with the same receipts as every other request.
Keys work for every AI coin, whichever mode (Dark Room or Build) it launched with. Each creator has one active key per coin.
Get a key#
Open the API key tab
On your coin page, open the AI settings panel (visible only to the creator wallet) and choose the API key tab.Optional: label and spend limit
A label of up to 60 characters, and a spend limit in USD (up to $10,000). With no limit only the coin's rules apply.Sign
Your wallet signs a message (free, moves no funds). Each signature can change your keys only once.Copy it now
The key starts withpp_live_and is shown once. Only a hash of it is stored, so it cannot be shown again. Generating a new key stops the old one immediately; “Revoke key” stops it without a new one.
What a key can spend#
| Limit | Value |
|---|---|
| Tier | Premium: every model the coin’s rules allow, at any tier |
| Requests per day | No daily request quota |
| Per request | The coin’s max per request (default $0.25), what is left of the key’s spend limit and of today’s budget, and the platform hard cap |
| Per day | The coin’s daily budget (default $20), shared with every other caller |
| Key spend limit | Optional, set when you create the key |
| Rate | 60 requests per minute per key (the platform can change it) |
While a request runs, its cap is reserved against the key's limit, so a burst of parallel requests cannot all pass the same check (per server process). What each request paid is added to the key's spend when it ends, including a stream you stopped halfway.
Endpoints#
The base URL is https://pairity.wtf/api/v1. Send the key as Authorization: Bearer pp_live_….
| Endpoint | What it does |
|---|---|
POST /api/v1/chat/completions | OpenAI-compatible chat, with or without streaming. |
GET /api/v1/models | The models this key may use, with tier, context length, price per 1M tokens, tool support and which one is the default. |
GET /api/v1/key | The key’s limit, what it spent, what is left, and the AI vault’s USDC and address. |
A request
curl https://pairity.wtf/api/v1/chat/completions \
-H "Authorization: Bearer pp_live_…" \
-H "Content-Type: application/json" \
-d '{
"model": "deepseek/deepseek-chat",
"max_tokens": 300,
"messages": [{ "role": "user", "content": "Write a gm for our holders" }]
}'{
"id": "chatcmpl-pp1234",
"object": "chat.completion",
"created": 1760875200,
"model": "deepseek/deepseek-chat",
"choices": [
{
"index": 0,
"message": { "role": "assistant", "content": "gm, tentacle fam…" },
"finish_reason": "stop"
}
],
"usage": { "prompt_tokens": 14, "completion_tokens": 42, "total_tokens": 56 },
"pairpad": {
"request_id": 1234,
"cost_usd": 0.001,
"payment_sig": "5Kq…",
"receipt_url": "https://pairity.wtf/verify?id=1234"
}
}The pairpad object is the only addition to the OpenAI format:
request_id: the request's number, used for its receipt.cost_usd: what the AI vault paid for this request, in USD (USDC).payment_sig: the Solana transaction of the USDC payment, when the provider returned one (otherwise null).receipt_url: the public page that checks this request against the Merkle root on Solana. See Verify AI receipts.
Choosing a model
modelmust be one ofGET /api/v1/models; any other id is refused with 400model_not_found.- Leave it out to use the default: the coin's default model for the premium tier if the creator set one, else the coin's own paired model, else the cheapest allowed premium model.
Streaming
Add "stream": true to get Server-Sent Events in the OpenAI chunk format. The last chunk carries finish_reason, usage and the same pairpad object, then data: [DONE]. Refusals that happen before the first token (no funds, over the cap, bad model) come back as a normal HTTP error; an error after streaming started arrives as a chunk with an error object.
With an OpenAI SDK
import OpenAI from 'openai'
const ai = new OpenAI({ apiKey: process.env.PAIRPAD_KEY, baseURL: 'https://pairity.wtf/api/v1' })
const r = await ai.chat.completions.create({
messages: [{ role: 'user', content: 'Three facts about our coin, please' }],
max_tokens: 300,
})
console.log(r.choices[0].message.content, (r as any).pairpad.receipt_url)Check what is left
curl -s https://pairity.wtf/api/v1/key -H "Authorization: Bearer pp_live_…"{
"data": {
"coin": "<MINT>",
"label": "My bot",
"limit": 50,
"usage": 1.234,
"limit_remaining": 21.5,
"vault_usdc": 21.5,
"vault_address": "<the coin's AI vault>"
}
}limit_remaining is what the key may still spend, never more than the vault holds (here the vault has less than the key's $48.77 left). limit is null for a key without a spend limit. The vault figures can be up to about a minute old.
Format limits#
- Text only: no images, audio or files.
developermessages are treated as system messages. - Up to 500 messages and a 400,000-character body.
max_tokens(ormax_completion_tokens) defaults to 1,024 and is capped at 4,096. A lower value is cheaper.temperaturefrom 0 to 2.- Up to 64 function tools are passed to the model; the model must support tools. Your app runs the tools.
Errors#
Errors use the OpenAI shape: {"error":{"message":"…","type":"…","code":"…"}}.
| Status | Type / code | Why |
|---|---|---|
| 401 | authentication_error | Missing, invalid or revoked key. |
| 429 | rate_limit_error | More than 60 requests per minute on this key. |
| 402 | insufficient_quota | The key reached its spend limit, the vault is out of USDC (no_funds), or the request would cost more than its cap (over_cap). |
| 429 | insufficient_quota | The coin used its daily budget. Resets at 00:00 UTC. |
| 400 | invalid_request_error | Bad body, a model that is not allowed (model_not_found), or a model that cannot use tools. |
| 403 | permission_error | The key no longer belongs to the coin’s creator. |
| 503 | service_unavailable or invalid_request_error | AI turned off, the coin’s AI paused, or no model available right now. |
| 502 / 503 | api_error | The AI provider failed or was busy, or its payment terms were refused. |
Keep the key secret