A free text classification API that returns the label, not a paragraph about the label
Free to call with a key and no card. Send the text and the labels you want it sorted into; get one of those labels back in a typed field with a number beside it. There is no prompt to tune, no JSON to parse, no training data, and no way for the response to come back as prose, because the response has no field that holds prose. It is OpenAI's Decisions API, request and response, behind a free key.
Your labels go in the request
No fine-tuning step, no labelled corpus, no model per task. The label set is an argument, so a new category is a new line in a JSON object rather than a retraining run. This is a support-ticket router, in full:
POST https://api.blockrun.ai/v1/decisions
Authorization: Bearer <your key>
{
"model": "gpt-6-luna",
"input": "My card was charged twice for
the same order and I need one
of them back.",
"questions": [{
"type": "choice",
"name": "dept",
"instructions": "Which team should
handle this ticket?",
"choices": [
{ "value": "billing", "description":
"a charge, refund, invoice or
payment problem" },
{ "value": "shipping", "description":
"a delivery, tracking or address
problem" },
{ "value": "technical", "description":
"the product or app is broken
or erroring" },
{ "value": "account", "description":
"login, password, or account
settings" },
{ "value": "sales", "description":
"a pre-purchase question about
buying" }
]
}]
}{
"model": "gpt-6-luna",
"answers": [{
"type": "choice",
"name": "dept",
"choice": "billing",
"probabilities": [
{ "value": "billing", "probability": 1.0 },
{ "value": "shipping", "probability": 0.0 },
{ "value": "technical", "probability": 0.0 },
{ "value": "account", "probability": 0.0 },
{ "value": "sales", "probability": 0.0 }
],
"confidence": 1.0
}],
"usage": {
"input_tokens": 181,
"output_tokens": 0
}
}output_tokens: 0 is not a bug. Nothing was generated. The model scored your labels against your text and returned the winner.
Read answers[0].choice and you are done. The failure mode you normally budget for here — the model answered in a sentence, or wrapped the JSON in backticks, or invented a sixth department — cannot happen, because the reply is assembled from the keys you sent.
When nothing fits, it still answers — confidently
A choice question always returns one of the values you offered. Feed the support router a sentence with nothing to do with support and it picks one, on 2026-10-07, three times out of three:
“The mitochondria is the powerhouse of the cell.” — billing, shipping, technical
"choice": "technical"
"probabilities": [
{ "value": "billing", "probability": 0.05 },
{ "value": "shipping", "probability": 0.01 },
{ "value": "technical", "probability": 0.94 }
]
"confidence": 0.91It is not unsure, it is wrong — and the number gives no warning. No threshold you could set would let real tickets through and stop this one.
Same sentence, one more value: “none — not a support request, or fits none of the teams above”
"choice": "none"
"probabilities": [
{ "value": "billing", "probability": 0.0 },
{ "value": "shipping", "probability": 0.0 },
{ "value": "technical", "probability": 0.01 },
{ "value": "none", "probability": 0.99 }
]
"confidence": 0.990.94 on a wrong answer. 1.0 on the right one above. If you had only the number you could not tell which was which.
The number is the model's stated confidence among the values you offered. It is not the probability that the label is right, we have not measured how well it tracks real outcomes, and no threshold turns it into that. What works is giving every choice a value for “none of these”, or asking a predicate first — “is this a support request?” came back 0.0 for this sentence.
The sarcasm case the old model got wrong
Until 2026-10-07 this page ran on OpenJev, an open-source model that read words rather than meaning: “Oh fantastic, another outage. Really loving this service today.” came back positive. The same sentence on the current backend, positive / neutral / negative:
"choice": "negative"
"probabilities": [
{ "value": "positive", "probability": 0.0 },
{ "value": "neutral", "probability": 0.0 },
{ "value": "negative", "probability": 1.0 }
]Right, three times out of three. One sentence is not an accuracy figure — run your own hard cases before you rely on it — but it is the failure this page used to be built on, and it no longer reproduces.
Several classifications in one request
questions is a list, so one call can carry several over the same text — and they do not all have to be label sets. One real customer message, four different answers, one round trip, 0.28s to 0.38s:
“Honestly the checkout flow is a disaster. Third time this week it timed out and I gave up. I have been a customer for six years and I am about done.”
| Question | Type | Answer | Number |
|---|---|---|---|
| sentiment | choice | negative | 1.0 |
| at risk of leaving? | predicate | yes | 0.95 |
| how urgent a reply | score | between “today” and “now” | 2.23 |
| topic | choice | checkout | 1.0 |
predicate is a yes/no and comes back as one probability. score takes an ordered list of levels — no rush, this week, today, now — and returns a weighted position between them, so 2.23 means “past reply-today, not yet reply-now” — a real answer to a question that a label set would have had to round off.
Use this, or use a chat model
- The answer is one of a set you can write down
- You want the same answer for the same input, every time
- You are doing it per message, at volume, and cost matters
- Routing, triage, tagging, moderation pre-filters, intent detection
- You have no labelled data and no appetite to make some
- The answer needs a reason, a summary, or a reply to the customer
- The categories depend on knowledge outside the text you send
- You need tool calls or a multi-step chain
- The label set is not knowable until you have read the text
These compose. Classify everything cheaply, then spend a chat model only on the slice where the top two labels were close — see what response_format actually does for why the second half of that pipeline needs a parse guard and this half does not.
What to expect in production
| Latency | Determinism | Input | Price |
|---|---|---|---|
| 0.28s to 1.34s across 15 calls; no cold start | Same input, same numbers, in every run we sent | Text, or messages with inline images | Free with a registered key |
The old backend's 32 seconds after a quiet spell is gone: the model is no longer a container of ours that sleeps. Probabilities come back rounded to two places, so expect 1.0 and 0.0 often — a clear case reads as certain.
Try it on your own labels
A key from the dashboard is free and needs no card, and this endpoint costs nothing to call on that key. Paste your own categories into the request above and see what it does with ten of your real messages before you believe any of this.
Text classification API questions: labels, accuracy, cost, limits
- Is there a genuinely free text classification API?
- This one. A registered key costs nothing and needs no card, and classification calls on that key are not charged. There is an hourly rate limit per key and no bill behind it.
- How does classification work without any training data?
- You describe each label in a sentence and send that alongside the text. The model returns the label that fits best, with a probability for each one. Nothing is fine-tuned and nothing is retained, so adding a category is another line in the request rather than a retraining run.
- How accurate is a zero-shot classifier like this?
- Good on plain routing and on the sarcasm case the previous backend got wrong, and wrong in one way worth knowing before you ship: text that fits none of your labels still comes back as one of them, confidently. The page shows that failure with the captured reply and the fix. Measure it on your own messages.
- What does the confidence number actually mean?
- The model's stated confidence among the labels you offered. It is not the probability the label is right, and we have not measured how well it tracks real outcomes. The page shows a confident wrong answer next to a right one with nearly the same number, which is the clearest way to see that a threshold alone will not separate them.
- How should I set a threshold on the output?
- From your own labelled examples, and do not rely on it alone. The failure that matters — text that fits none of the labels — arrives confident, so catch it in the question instead: give the choice a value for none of these, or ask a yes/no first.
- What happens if none of my labels apply to the text?
- It picks one anyway, often with a high number. Add a catch-all value such as none, described as fitting none of the others, and the same text goes there instead — on the page it moved from a confident wrong label to none.
- Can I classify several dimensions in a single call?
- Yes, several questions over the same text in one request, and they need not all be label sets — a yes/no and a graded score can ride along. Each answer comes back typed, and the call on the page returned in under half a second.
- Do I get the same label for the same text every time?
- In every run we sent, yes: identical input returned identical numbers. A rule you tune against yesterday's traffic still means what you thought. A chat model running at a temperature does not give you that.
- Text classification API vs prompting an LLM instead?
- Use a chat model when the answer needs a reason, a summary, or knowledge beyond the text in front of it. Use this when the answer is one of a set you can write down: it is free on a key, quick, and cannot reply in a sentence, because the response has no field that holds one.