Documentation
Base URL https://api.punkytigerlabs.com · Authentication Authorization: Bearer s1k_… · JSON over HTTPS. The API follows the standard System One request and response format.
On this page: First decision · Models · Questions · Answers · Errors · Headers and limits · Billing · Keys
Your first decision
Send a state (the text, document or conversation to judge) and named questions. Every answer comes back under the name you gave it.
curl https://api.punkytigerlabs.com/v1/systemone \
-H "Authorization: Bearer $S1_KEY" -H "Content-Type: application/json" \
-d '{"model": "superion-s1-latest",
"state": "Message: The laptop arrived broken. I want my money back now, this is unacceptable.",
"questions": {
"refund": {"type": "noul", "instructions": "Does the customer ask for a refund or a replacement?", "criteria": {"true": "yes", "false": "no"}},
"sentiment": {"type": "choice", "instructions": "Overall sentiment of the message.", "criteria": {"pos": "positive", "neg": "negative", "neu": "neutral"}},
"urgency": {"type": "score", "instructions": "How urgent is a reply?", "criteria": ["not urgent", "somewhat urgent", "very urgent"]}}}'
{"model": "superion-s1-v6-int4e-vllm-kv1@sha256:e6dc49dc",
"answers": {
"refund": {"type": "noul", "noul": 0.9998},
"sentiment": {"type": "choice", "choice": "neg", "confidence": 0.9661, "probabilities": {"pos": 0.0003, "neg": 0.9941, "neu": 0.0056}},
"urgency": {"type": "score", "score": 1.9393, "confidence": 0.7917, "probabilities": {"0": 0.0, "1": 0.0607, "2": 0.9393}, "legend": {"0": "not urgent", "1": "somewhat urgent", "2": "very urgent"}}},
"usage": {"input_tokens": 135, "output_tokens": 0},
"meta": {"request_id": "63a6718e-ab64-404b-92f3-fdba826d0dfe", "mode": "live", "latency_ms": 103}}
Real response to this exact request (12 Nov 2026, model superion-s1-v6-int4e-vllm-kv1@sha256:e6dc49dc), rounded to four decimals for display; the API returns full precision. latency_ms is server-side processing time.
Python
import os, requests
r = requests.post("https://api.punkytigerlabs.com/v1/systemone", timeout=120,
headers={"Authorization": "Bearer " + os.environ["S1_KEY"]},
json={"model": "superion-s1-latest",
"state": "Message: I was charged twice this month. Can you refund the extra payment?",
"questions": {"team": {"type": "choice", "instructions": "Which team should handle this?",
"criteria": {"billing": "Payments, invoices and refunds",
"support": "Technical problems and bugs"}}}})
r.raise_for_status()
print(r.json()["answers"]["team"]["choice"]) # "billing"
JavaScript
const r = await fetch("https://api.punkytigerlabs.com/v1/systemone", {
method: "POST",
headers: { "Authorization": `Bearer ${process.env.S1_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({ model: "superion-s1-latest", state: "…", questions: { /* … */ } })
});
if (!r.ok) throw new Error(await r.text());
const { answers } = await r.json();
Use a client timeout of at least 120 s. Most requests finish well under a second; under heavy load a request waits in the fair queue instead of failing.
Models
| model | What it is |
|---|---|
superion-s1-latest | Always the current recommended release (today v6). Also used when model is omitted. The response names the exact release that answered. |
superion-s1-v6-int4e-vllm-kv1@sha256:e6dc49dc | Superion S1 v6, pinned. Recommended. |
superion-s1-v5-int4e-vllm-kv1@sha256:47f7571c | Superion S1 v5, pinned. Available. |
Pinned IDs are immutable: the hash identifies the exact package served, and results are deterministic within a pinned release: the same request returns the same probabilities. New releases get new IDs; retirement comes with at least 90 days notice. GET /v1/models lists what is served. Superion S1 is built on Gemma 4 26B-A4B, served with mixture-of-experts weights in INT4.
Questions
| type | criteria | answer fields |
|---|---|---|
noul | object with true and false descriptions | noul: probability that the answer is yes |
choice | object: option id → description (2–255 options) | choice, confidence, probabilities |
score | array of 2–16 ordered levels, lowest first | score, confidence, probabilities, legend |
Every question needs type, instructions and criteria. Describe each option or level the way you would for a careful human reviewer; clear descriptions give better answers. You can ask any number of questions about the same state in one request, within the token limits below. Before automating on a threshold, check the probabilities on a sample of your own data.
Answers
| field | meaning |
|---|---|
model | The pinned release that answered. |
answers.<name>.noul | Probability of yes, 0–1. |
answers.<name>.choice | The most likely option id. |
answers.<name>.score | Expected level on the scale, from 0 to N−1 (probability-weighted). |
answers.<name>.probabilities | Calibrated probability per option id (choice) or per level index (score). They sum to 1. |
answers.<name>.confidence | How concentrated the probabilities are: 1 − H(p) / ln(n) for n options. 1 when one option holds all the probability, 0 when all are equally likely. It is not the probability of the chosen option. |
answers.<name>.legend | Score only: level index → your level description. |
usage.input_tokens | Billed input tokens (what counts). output_tokens is always 0. |
meta.request_id | Unique id of the request; same as the X-Request-ID header. Quote it to support. |
meta.latency_ms | Server-side time: admission, queue and inference. It excludes your network round trip. |
meta.cost_usd | Prepaid keys: the exact charge for this request, in USD. |
Errors
Errors return JSON with an error message and, where useful, a machine-readable code. Check the HTTP status before reading answers. Requests that fail are never charged and do not count toward quotas.
{"error": "insufficient balance: add credits at https://punkytigerlabs.com/s1/ (prepaid, 0.042 USD per million input tokens)",
"code": "insufficient_credits", "contact_url": "https://punkytigerlabs.com/s1/account", "balance_usd": 0.0}
| Status | Meaning | What to do |
|---|---|---|
| 400 | Invalid JSON, unknown model or malformed question | Fix the request; the message says what |
| 401 | Missing, wrong, rotated or revoked key | Check the key |
| 402 | Prepaid balance is used up (code: "insufficient_credits", with contact_url) | Add credits |
| 413 | Body over 4 MB | Split the request |
| 422 | A question exceeds 32,768 tokens, the request exceeds 65,536 billable tokens, more than 255 options or more than 16 levels | Shorten the state or split the questions |
| 429 | Daily quota used up, prepaid per-minute limit, or queue full (code: "rate_limited") | Wait for Retry-After |
| 503 | Service temporarily unavailable | Retry with backoff, respecting Retry-After; quote the request id if it persists |
Headers and limits
| Header | When |
|---|---|
X-Request-ID | Every response, including errors. |
Retry-After | 429 and 503, in seconds. |
X-RateLimit-Limit-Tokens-Day, -Remaining-Tokens-Day, -Reset-Day | Free keys (daily quota; reset at 00:00 UTC). |
X-RateLimit-Limit, -Remaining, -Reset and the same with -Tokens; X-S1-Balance-USD | Prepaid keys (per-minute limits and balance). |
| Plan | Per minute | Per day (UTC) | In flight |
|---|---|---|---|
| Free | no limit (fair queue) | 20 M input tokens | 8 |
| Prepaid | 240 requests · 80,000 input tokens | no cap (balance) | 32 |
Size limits: each question (state + that question) up to 32,768 tokens; each request up to 65,536 billable input tokens; body up to 4 MB. Longer inputs are refused with 422, never truncated. When the service is busy, requests wait in a fair queue (up to 60 s; paid keys first) instead of failing.
Billing: what counts as input tokens
usage.input_tokens is what we bill: your state once per request, plus each question's instructions, criteria and option labels, in the Gemma 4 tokenizer. Asking more questions about the same state only adds the questions' own tokens. Output is always free. Prepaid: 0.042 USD per million input tokens, deducted per successful request with exact integer accounting; meta.cost_usd shows the charge. The request that crosses zero is completed; the next one returns 402.
Your key and usage
GET /v1/usage | Plan, today's counters, limits, balance, daily history (90 days), purchases |
POST /v1/keys/rotate | Returns a new key; the old one stops working at once |
POST /v1/keys/revoke | Disables the key |
GET /v1/models · GET /health | No key needed |
Keep keys on your server: /v1/systemone, /v1/usage and /v1/keys/* send no CORS headers, so browsers cannot call them. We store only a hash of each key.
Data
We do not store your requests or answers. See privacy and the API terms. For custom and industry models or dedicated capacity, contact us.