Dracon AI API — docs
For Chrome extension authors. One API. One key. Pay only for what you use.
Get a key
Sign in at /auth/login with a magic link, then create an API key
at /dashboard/api-keys. The key is prefixed with drn_.
Make your first call in 60 seconds
curl -X POST https://dracon.uk/api/v1/ai/chat/completions \
-H "x-api-key: drn_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"messages":[{"role":"user","content":"Hello"}]}' Endpoint reference
| Method | Path | What it does |
|---|---|---|
| POST | /api/v1/ai/chat/completions | Send messages; receive a streamed or non-streamed completion. |
| POST | /api/v1/auth/email-link | Request a magic-link sign-in email. |
| GET | /api/v1/auth/api-keys | List your API keys. |
| POST | /api/v1/auth/api-keys | Create a new API key (scope=ai). |
| DELETE | /api/v1/auth/api-keys/{id} | Revoke a key. |
| GET | /api/v1/me | Current profile (requires session cookie or x-api-key). |
| POST | /api/v1/billing/checkout | Start a Paddle checkout for a plan. |
| POST | /api/v1/webhooks/paddle | Paddle webhook receiver (server-side). |
Auth
Every request must include the x-api-key header. Example:
x-api-key: drn_YOUR_KEY For dashboard sessions (browser cookie auth), the same x-api-key header is also
accepted. There is no OAuth flow for the AI API — the key is the credential.
Request shape (chat/completions)
{
"messages": [
{ "role": "system", "content": "You are a helpful assistant." },
{ "role": "user", "content": "Summarize the page." }
],
"lane": "free", // optional: free | omega (default: free)
"stream": false, // optional: true for SSE streaming
"model": "auto" // optional: provider-routed or explicit model slug
} Response shape (non-streamed)
{
"id": "chatcmpl-...",
"object": "chat.completion",
"created": 1718000000,
"model": "auto",
"choices": [
{
"index": 0,
"message": { "role": "assistant", "content": "..." },
"finish_reason": "stop"
}
],
"usage": { "prompt_tokens": 12, "completion_tokens": 87, "total_tokens": 99 }
} Lanes
- free — 5 free lanes, rate-limited per provider, no Paddle billing. Routes across 5 rotating providers from the 38 in the catalog.
- omega — Dracon Omega subscribers. Higher rate limits, dedicated model selection, metered billing via Paddle. Currently gated by
BILLING_OMEGA_ENABLED=falsepre-go-live.
Error catalog
| Code | Name | Meaning |
|---|---|---|
| 400 | bad_request | Malformed JSON, missing fields, or messages array empty. |
| 401 | unauthorized | Missing or invalid x-api-key. Check that the key was created and is in chrome.storage.sync. |
| 403 | forbidden | API key valid but the requested lane is not enabled for this key. |
| 429 | rate_limited | Free-tier rate limit hit. Wait and retry, or upgrade to Dracon Omega. |
| 500 | internal_error | Upstream provider error or Dracon internal error. Retry with backoff. |
| 502 | bad_gateway | All upstream providers in the lane failed. Retry. |
| 503 | unavailable | Lane is disabled (BILLING_OMEGA_ENABLED=false) or all providers in maintenance. |
Rate limits
Free lane: per-provider limits inherited from the upstream free tier (typically 30 RPM and 1K–14.4K req/day).
Omega lane: configurable in BILLING_OMEGA_RATE_LIMIT_RPM; default 600 RPM.
429 responses include a retry_after_ms field. Exponential backoff in the extension's background.js is recommended.
Chrome extension example (copy-paste)
A minimal Manifest V3 extension that prompts the user, sends it to the Dracon AI API, and
shows the reply. Drop the four files into a folder, load it as an unpacked extension in chrome://extensions, and set the API key in chrome.storage.sync via the
extension's options page (or the snippet at the bottom).
manifest.json
{
"manifest_version": 3,
"name": "My Dracon Extension",
"version": "1.0.0",
"permissions": ["storage", "scripting"],
"host_permissions": ["https://dracon.uk/*"],
"background": { "service_worker": "background.js" },
"action": { "default_popup": "popup.html" }
} background.js
// Read the API key from chrome.storage.sync (set on first run
// via options.js or by pasting it in the extension's options page).
const API_KEY = await chrome.storage.sync.get('dracon_api_key');
const API_URL = 'https://dracon.uk/api/v1/ai/chat/completions';
chrome.runtime.onMessage.addListener((msg, _sender, sendResponse) => {
if (msg.type !== 'dracon_chat') return false;
fetch(API_URL, {
method: 'POST',
headers: {
'x-api-key': API_KEY.dracon_api_key,
'Content-Type': 'application/json'
},
body: JSON.stringify({
messages: [{ role: 'user', content: msg.prompt }],
lane: msg.lane || 'free'
})
})
.then(r => r.json())
.then(data => sendResponse({ ok: true, data }))
.catch(err => sendResponse({ ok: false, error: String(err) }));
return true; // keep the message channel open for the async response
}); popup.html
<!doctype html>
<html>
<body style="min-width:300px">
<h3>Dracon AI</h3>
<textarea id="prompt" rows="4" style="width:100%"></textarea>
<button id="ask">Ask</button>
<pre id="out"></pre>
<script src="popup.js"></script>
</body>
</html> popup.js
document.getElementById('ask').addEventListener('click', async () => {
const prompt = document.getElementById('prompt').value;
const out = document.getElementById('out');
out.textContent = '...';
const res = await chrome.runtime.sendMessage({
type: 'dracon_chat',
prompt,
lane: 'free'
});
out.textContent = res.ok
? (res.data.choices?.[0]?.message?.content || JSON.stringify(res.data))
: ('Error: ' + res.error);
}); Set the API key once (one-time, from the devtools console of the extension's options page)
chrome.storage.sync.set({ dracon_api_key: 'drn_YOUR_KEY' }); Products in the catalog
- Azumi — Ship Rust web products that can't break silently in production.
- Tiles — Stop maintaining shell scripts that only you understand.
- SamAI — Automate browser tasks without writing brittle scripts.
- Filler — Never fill the same form twice.
AI providers (38)
The free lane rotates across 5 of these at a time. The full list is in tools/specta-data/ai-providers-catalog.json.
- Groq free tier 30 RPM, 14.4K req/day
- DeepInfra free tier 200 concurrent requests
- Hyperbolic free tier $1 free credits
- NVIDIA NIM free tier ~40 RPM
- Cloudflare Workers AI free tier 10K neurons/day, 300 RPM
- Google AI Studio free tier 15–60 RPM, 250–1.5K req/day
- SambaNova free tier Generous dev tier
- SiliconFlow free tier 100 req/day + $1 free credits
- Scaleway free tier 1M free tokens (permanent)
- Alibaba DashScope free tier 1M free tokens/model (90 days)
- OVHcloud AI Endpoints free tier 2 req/min/IP free, 400 RPM with key
- Replicate free tier 6 req/min free, 3K RPM with payment
- Hugging Face free tier ~$0.10/month free credits
- Perplexity free tier ~50 RPM (tiered by spend)
- Mistral La Plateforme free tier 1 req/s, 1B tokens/month
- Codestral free tier 30 RPM, 2K req/day
- Cerebras free tier 1M tokens/day
- Kilo Code free tier 200 req/hr for anonymous users
- RunPod free tier Varies by plan
- Nous Portal free tier Ultra: 1,600 RPM
- Z.AI GLM free tier Varies by plan
- BytePlus ModelArk free tier 500K free tokens per model
- MiniMax free tier Varies by plan
- Venice free tier Varies by plan
- LLM Gateway free tier Depends on upstream provider
- Synthetic free tier Varies by plan
- Mimo free tier
- Anthropic Varies by usage tier
- Fireworks AI free tier High rate limits with postpaid billing
- GitHub Models free tier Free tier limits, pay per token unit beyond
- Ollama free tier Local: unlimited. Cloud: 1 concurrent model (Free)
- Baidu Qianfan Varies by tier
- Kimi free tier Varies by usage tier
- StepFun free tier Varies by plan
- Chutes Varies by model
- CrofAI free tier 500 requests/day (Hobby)
- OpenCode $12/5hr, $30/week, $60/month
- AI Router free tier Free: 1K req/mo, Starter: 20K req/mo, Pro: 100K req/mo