Build with CrypTok's in-house AI/SI engine. OpenAI-compatible response shape, one model (Lumen) running on the team's own GPU server (rgb).
Three steps to your first response:
curl https://cryptok.ai/api/v1/chat \
-H "Authorization: Bearer cak_<keyId>.<secret>" \
-H "Content-Type: application/json" \
-d '{
"messages": [
{"role": "user", "content": "What is $CRYPTOK?"}
],
"model": "cryptok-lumen"
}'Response is an OpenAI-style chat.completion object — drop it into existing tooling that expects that shape.
Every request requires Authorization: Bearer cak_<keyId>.<secret> where the key was generated for an account with API access (AI/SI Pro or a Killer Whale membership). The secret half is hashed at rest (SHA-256), only the prefix is recoverable — store the full key in your env vars at creation time.
cak_<keyId16>.<secret32>
Example:
cak_a17c07022795d3a3.177a41a921924696175292845bbbdf35
└─┬─┘└──────┬───────┘└──────────────┬─────────────────┘
prefix public id (16 hex) secret (32 hex)Send messages, get a response. Stateless — you manage conversation history client-side and pass the full messages array each request.
{
"messages": [
{"role": "system", "content": "(optional) Custom system prompt"},
{"role": "user", "content": "What is $CRYPTOK?"},
{"role": "assistant", "content": "(optional) Prior assistant turn"},
{"role": "user", "content": "Tell me more about its tokenomics."}
],
"model": "cryptok-lumen",
"stream": false
}| Field | Type | Required | Description |
|---|---|---|---|
messages | array | yes | Conversation turns. Must include at least one role: "user". Roles: user, assistant, system. |
model | string | no | One of the 7 model ids below. Defaults to cryptok-lumen. |
stream | boolean | no | If true, response is text/plain streaming of tokens as they generate. Default false = JSON. |
{
"id": "chatcmpl-s46v1rjzx6g",
"object": "chat.completion",
"created": 1780525913,
"model": "cryptok-lumen",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "$CRYPTOK is the native Solana token..."
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_chars": 17,
"completion_chars": 412,
"credits_charged": 3,
"source": "quota"
},
"knowledge": {
"hits": 5,
"top_score": 0.81
}
}The knowledge object reports how many sanitized snippets from CrypTok's internal knowledge base were retrieved and the top similarity score. usage.source is either "quota" (drawn from your daily allowance) or "credits" (drawn from your credit balance).
There is one model: cryptok-lumen (Lumen), the same engine every CrypTok AI/SI user chats with. The model field is optional; older ids (cryptok-lumen-pro, -nova, -forge, -spark, -coder-mini, -comet, -pico) are still accepted and run on Lumen.
| Model id | Context | Best for |
|---|---|---|
cryptok-lumen | 32K tokens | Everything — conversation, research, code, crypto and on-chain questions. |
Pass "stream": true in the request to receive tokens as a text/plain stream — same format as the cryptok.ai web UI uses. Time to first byte is typically ~200ms even on Lumen.
const res = await fetch('https://cryptok.ai/api/v1/chat', {
method: 'POST',
headers: {
'Authorization': 'Bearer cak_<keyId>.<secret>',
'Content-Type': 'application/json',
},
body: JSON.stringify({
messages: [{role: 'user', content: 'Explain Solana validators briefly.'}],
model: 'cryptok-lumen',
stream: true,
}),
});
const reader = res.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { value, done } = await reader.read();
if (done) break;
process.stdout.write(decoder.decode(value, {stream: true}));
}Every successful response includes headers so you can show live usage in your app without a separate quota call:
| Header | Type | Example |
|---|---|---|
X-CrypTok-AI-Used | int | 1247 messages used today |
X-CrypTok-AI-Cap | int | 5000 daily cap |
X-CrypTok-AI-Credits | float | 247.5 credits in balance |
X-CrypTok-AI-Cost | float | 3 credits this call cost |
X-CrypTok-AI-Source | enum | quota or credits |
X-Request-Id | string | chatcmpl-s46v1rjzx6g |
Errors are returned as JSON with an error object containing a code and message:
{
"error": {
"code": "rate_limited",
"message": "Daily cap reached",
"used": 5000,
"cap": 5000,
"credit_balance": 0
}
}| Code | HTTP | When | How to fix |
|---|---|---|---|
invalid_api_key | 401 | Authorization header missing or key is malformed/revoked. | Send Authorization: Bearer cak_<keyId>.<secret>. Verify the key on /upgrade. |
tier_required | 403 | Your account doesn't include API access. | API keys come with AI/SI Pro or a Killer Whale membership (cryptok.me/packages). |
bad_request | 400 | Invalid JSON in request body. | Ensure Content-Type: application/json and the body is valid JSON. |
no_messages | 400 | messages[] is empty or has no user-role message. | Include at least one {"role": "user", "content": "..."} entry. |
rate_limited | 429 | Daily message limit reached. | Wait for the rolling 24h refill, or move to AI/SI Pro (250 messages a day). The error body includes used/cap. |
engine_error | 502 | Inference backend (rgb) returned an error or timed out. | Retry with backoff. Persistent failure → contact support. |
One model, one plan. Every call counts as one message against the key owner's daily limit — the same limits as the cryptok.ai chat.
| Account | Messages / day | Images / day | API keys |
|---|---|---|---|
| Free CrypTok account | 25 | 3 | No |
| AI/SI Pro ($29/mo · $78/3 mo · $290/yr) | 250 | 30 | ✓ Yes |
| Degen membership (includes AI/SI Pro limits) | 250 | 30 | No |
| Killer Whale membership (includes AI/SI Pro) | 250 | 30 | ✓ Yes |
Limits refill on a rolling 24-hour window, not calendar-day midnight. When they run out you get a 429 rate_limited error until they refill.
async function chat(message: string): Promise<string> {
const res = await fetch('https://cryptok.ai/api/v1/chat', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.CRYPTOK_AI_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
messages: [{role: 'user', content: message}],
model: 'cryptok-lumen',
}),
});
if (!res.ok) {
const err = await res.json();
throw new Error(`${err.error.code}: ${err.error.message}`);
}
const data = await res.json();
return data.choices[0].message.content;
}
console.log(await chat('Whats the price of SOL?'));import os
import requests
def chat(message: str) -> str:
res = requests.post(
'https://cryptok.ai/api/v1/chat',
headers={
'Authorization': f'Bearer {os.environ["CRYPTOK_AI_KEY"]}',
'Content-Type': 'application/json',
},
json={
'messages': [{'role': 'user', 'content': message}],
'model': 'cryptok-lumen',
},
timeout=120,
)
res.raise_for_status()
data = res.json()
return data['choices'][0]['message']['content']
print(chat('Whats the price of SOL?'))The response shape is compatible with the OpenAI chat.completion object, so you can use the official OpenAI SDK by changing the baseURL:
import OpenAI from 'openai';
const client = new OpenAI({
apiKey: process.env.CRYPTOK_AI_KEY,
baseURL: 'https://cryptok.ai/api/v1',
});
const completion = await client.chat.completions.create({
model: 'cryptok-lumen',
messages: [{role: 'user', content: 'Hello!'}],
});
console.log(completion.choices[0].message.content);Note: OpenAI SDK quirks like tools array, function calls, response_format: json, and seed may behave differently or not be supported. Stick with the simple messages → choices path for guaranteed compatibility.