Document Extraction and Classification
Run document extraction and classification on Tycho at $0.50 / $2.25 per 1M tokens, with JSON schema output. Change the base URL, key and model.
What drives the cost
Extraction and classification are high-volume, low-output work. The model reads a whole document and writes back a few hundred tokens of fields, or a single label. That makes input price the main lever, and it is why the same pipeline costs very different amounts depending on which model reads the pages.
The cost of one call is the input tokens multiplied by the input price, plus the output tokens multiplied by the output price, divided by one million. Tycho costs $0.50 per million input tokens and $2.25 per million output tokens.
Two worked examples to replace with your own numbers
Token counts per page vary with layout and density. Anthropic's PDF documentation puts a page of text at 1,500 to 3,000 tokens, and the examples below use the low end.
The first is pulling fields from a two-page invoice that has already been converted to text.
| Assumption | Value |
|---|---|
| Instructions and JSON schema, sent on every call | 1,200 tokens |
| Invoice text, two pages | 3,000 tokens |
| Input tokens per invoice | 4,200 |
| Output tokens per invoice, about 20 fields and the line items | 400 |
| Cost per invoice on Tycho | $0.0030 |
| Cost per 100,000 invoices | About $300 before prompt caching |
An accounts payable team that processes 40,000 invoices a month pays about $120 a month on Tycho before prompt caching. The same invoice costs about $0.0091 on Clarke and $0.0150 on Hal.
The second is sorting inbound documents into types, such as invoice, purchase order, contract, receipt or other, so each one reaches the right queue. The model needs the first page, and the answer is one label.
| Assumption | Value |
|---|---|
| Instructions and the list of labels, sent on every call | 800 tokens |
| First page of the document | 1,500 tokens |
| Input tokens per document | 2,300 |
| Output tokens per document, the label | 50 |
| Cost per document on Tycho | $0.0013 |
| Cost per million documents | About $1,262 before prompt caching |
In the extraction example input is 70 percent of the cost, and the instructions and schema are 29 percent of the input. They are identical on every call, so prompt caching applies to them, and Radium's model pages put the saving on repeated prompts at up to 90 percent. In the classification example the label is 9 percent of the cost, so sending only the first page matters far more than shortening the answer.
Count the tokens in a sample of your own documents before you plan around any of these numbers.
Which model to use
Tycho is the default for document extraction and classification. Radium built it for classification, extraction, summarization and routing at high volume, and positions it as comparable to Haiku 5.5 and GPT-6 Luna. It reads text, follows a JSON schema and costs the least per token of the three models, which matters when every document in a backlog goes through it.
Clarke takes the documents Tycho cannot or should not handle. Tycho does not accept images, so scanned pages and photos either go through OCR first or go to Clarke, which accepts image input. Clarke also has a context window of 1,000,000 tokens against Tycho's 125,000, so a long contract or a full deal room fits in one call. It is the better choice for fields that need judgment, such as a renewal term buried in a clause or an amount that depends on a referenced schedule.
Hal is for the exceptions queue, where a document needs reasoning across sources, such as reconciling an invoice against a purchase order and a delivery note. Most pipelines never need it. Routing by validation keeps the bill close to Tycho's price: if 5 percent of invoices fail the checks and are sent again to Clarke, the average cost per invoice is about $0.0035 in the example above.
| Step | Model | Model ID |
|---|---|---|
| Classification, routing and field extraction from text | Tycho | tycho-1.0 |
| Scanned pages and photos, long documents, fields that need judgment, documents that fail validation | Clarke | clarke-1.0 |
| Reconciling one document against others, exceptions that need reasoning | Hal | hal-1.0 |
Let the validation checks decide what moves up a tier.
What this workload needs from the API
An extraction pipeline depends on a short list of API capabilities. Confirm each one against your own client and a sample of your own documents before production traffic moves.
| Capability | Why extraction and classification need it | On Radium |
|---|---|---|
| Structured output | Returning fields that your database or ERP can load without parsing free text | JSON mode and JSON schema on all three models, through response_format |
| Image input | Reading scanned pages, photos and stamps | Hal and Clarke. Tycho reads text only |
| Context window | Holding a long contract or several related documents in one call | Tycho 125,000 tokens, Hal 250,000, Clarke 1,000,000 |
| Concurrency | Working through a backlog of thousands of documents | No published rate limits. Send requests in parallel and retry 429 and 503 responses with backoff |
| Repeatable runs | Getting the same fields from the same document twice | temperature 0 with a fixed seed |
| Token counting | Estimating cost before a large run | POST /v1/messages/count_tokens on the Anthropic-compatible endpoint |
Four things do not carry over unchanged. Radium does not offer a batch endpoint, so a pipeline built on a provider's batch API sends ordinary requests in parallel instead. The docs do not describe PDF or file upload, so send the extracted text, or page images to Clarke. Radium's structured outputs guide warns that schemas with recursive references or union types may not be fully supported, so keep the schema flat and test it on real documents. Radium does not offer embeddings, so a pipeline that also indexes documents for search keeps its current embedding provider.
Run your schema on 50 real documents before the cutover.
How to switch
Radium serves OpenAI-compatible and Anthropic-compatible APIs, so an existing extraction service keeps its SDK, its prompts, its schemas and its retry logic. Three values change, which are the base URL, the API key and the model name.
Pick the SDK you already use, then your language. The Anthropic SDK adds the /v1 path itself, so its base URL does not include it. Replace YOUR_API_KEY with your key.
from openai import OpenAI
client = OpenAI(
api_key="YOUR_API_KEY",
base_url="https://api.radium.cloud/v1",
)
response = client.chat.completions.create(
model="tycho-1.0",
messages=[
{"role": "user", "content": "Hello, Radium!"}
],
)
print(response.choices[0].message.content)import OpenAI from "openai";
const client = new OpenAI({
apiKey: "YOUR_API_KEY",
baseURL: "https://api.radium.cloud/v1",
});
const response: OpenAI.Chat.ChatCompletion =
await client.chat.completions.create({
model: "tycho-1.0",
messages: [{ role: "user", content: "Hello, Radium!" }],
});
console.log(response.choices[0].message.content);curl https://api.radium.cloud/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"model": "tycho-1.0",
"messages": [{"role": "user", "content": "Hello, Radium!"}]
}'package main
import (
"context"
"fmt"
openai "github.com/sashabaranov/go-openai"
)
func main() {
cfg := openai.DefaultConfig("YOUR_API_KEY")
cfg.BaseURL = "https://api.radium.cloud/v1"
client := openai.NewClientWithConfig(cfg)
resp, err := client.CreateChatCompletion(
context.Background(),
openai.ChatCompletionRequest{
Model: "tycho-1.0",
Messages: []openai.ChatCompletionMessage{
{Role: openai.ChatMessageRoleUser, Content: "Hello, Radium!"},
},
},
)
if err != nil {
panic(err)
}
fmt.Println(resp.Choices[0].Message.Content)
}import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.models.chat.completions.*;
OpenAIClient client = OpenAIOkHttpClient.builder()
.apiKey("YOUR_API_KEY")
.baseUrl("https://api.radium.cloud/v1")
.build();
ChatCompletion completion = client.chat().completions().create(
ChatCompletionCreateParams.builder()
.model("tycho-1.0")
.addUserMessage("Hello, Radium!")
.build());
System.out.println(
completion.choices().get(0).message().content().orElseThrow());using OpenAI;
using OpenAI.Chat;
var client = new OpenAIClient(
new ApiKeyCredential("YOUR_API_KEY"),
new OpenAIClientOptions
{
Endpoint = new Uri("https://api.radium.cloud/v1"),
});
var chatClient = client.GetChatClient("tycho-1.0");
var response = await chatClient.CompleteChatAsync(
new UserChatMessage("Hello, Radium!"));
Console.WriteLine(response.Value.Content[0].Text);import anthropic
client = anthropic.Anthropic(
api_key="YOUR_API_KEY",
base_url="https://api.radium.cloud",
)
message = client.messages.create(
model="tycho-1.0",
max_tokens=1024,
messages=[
{"role": "user", "content": "Hello, Radium!"}
],
)
print(message.content[0].text)import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic({
apiKey: "YOUR_API_KEY",
baseURL: "https://api.radium.cloud",
});
const message: Anthropic.Message = await client.messages.create({
model: "tycho-1.0",
max_tokens: 1024,
messages: [{ role: "user", content: "Hello, Radium!" }],
});
console.log(message.content[0].text);curl https://api.radium.cloud/v1/messages \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-d '{
"model": "tycho-1.0",
"max_tokens": 1024,
"messages": [{"role": "user", "content": "Hello, Radium!"}]
}'Extract invoice fields as JSON
This example reads a folder of invoices that are already text, asks Tycho for fields that match a JSON schema at temperature 0, and runs 16 documents at a time. Two arithmetic checks and the list of missing fields decide which invoices pass and which go to Clarke.
import json
from concurrent.futures import ThreadPoolExecutor
from pathlib import Path
from openai import OpenAI
client = OpenAI(
api_key="YOUR_API_KEY",
base_url="https://api.radium.cloud/v1",
max_retries=5, # retries 429 and 5xx responses with backoff
)
line_item = {
"type": "object",
"properties": {
"description": {"type": "string"},
"quantity": {"type": "number"},
"unit_price": {"type": "number"},
"amount": {"type": "number"},
},
"required": ["description", "quantity", "unit_price", "amount"],
}
schema = {
"name": "invoice",
"schema": {
"type": "object",
"properties": {
"invoice_number": {"type": "string"},
"invoice_date": {"type": "string"},
"vendor_name": {"type": "string"},
"currency": {"type": "string"},
"subtotal": {"type": "number"},
"tax": {"type": "number"},
"total": {"type": "number"},
"line_items": {"type": "array", "items": line_item},
"missing_fields": {"type": "array", "items": {"type": "string"}},
},
"required": [
"invoice_number", "invoice_date", "vendor_name", "currency",
"subtotal", "tax", "total", "line_items", "missing_fields",
],
},
}
INSTRUCTIONS = (
"Extract the invoice fields from the document text. Copy values exactly "
"as printed and write dates as YYYY-MM-DD. If a field is not on the "
"document, use an empty string or 0 and add its name to missing_fields."
)
def extract(path):
response = client.chat.completions.create(
model="tycho-1.0",
temperature=0,
seed=7,
messages=[
{"role": "system", "content": INSTRUCTIONS},
{"role": "user", "content": path.read_text()},
],
response_format={"type": "json_schema", "json_schema": schema},
)
data = json.loads(response.choices[0].message.content)
lines = round(sum(i["amount"] for i in data["line_items"]), 2)
checks_pass = (
abs(lines - data["subtotal"]) < 0.01
and abs(data["subtotal"] + data["tax"] - data["total"]) < 0.01
and not data["missing_fields"]
)
return path, data, checks_pass
files = sorted(Path("invoices").glob("*.txt"))
with ThreadPoolExecutor(max_workers=16) as pool:
for path, data, ok in pool.map(extract, files):
print(path.name, data["total"], "ok" if ok else "send to clarke-1.0")Steps
- Create an account at deploy.radium.cloud and generate a key at deploy.radium.cloud/keys. New accounts get $25 in credit with no card required.
- Store the key as a secret in the service that runs the pipeline, and keep it out of the repository and out of browser code.
- Change the base URL and model name, then run Radium in shadow mode on a copy of the day's documents while your current provider keeps writing to production.
- Compare the two outputs field by field, then move one document type at a time.
Switching back means pointing the base URL at your previous provider. Guides for the OpenAI and Anthropic SDKs, LangChain, LlamaIndex and PydanticAI are in the Radium docs.
How to test it on documents you have already keyed
Extraction is easier to test than most workloads, because the right answer usually already exists. Every invoice your team has keyed into the ERP is a labelled example. Take 300 documents with known values, spread across your vendors and layouts, and run each one through your current model and through Tycho with the same prompt and schema.
- Score each field against the known value. Normalize dates, amounts and whitespace first, then count exact matches field by field. A single accuracy number hides the field that fails, and research on invoice extraction evaluates field-level precision and exact match for that reason.
- Count invented values separately. A field that is not on the document should come back empty and listed as missing. A value that appears with no source in the document is the expensive error, so track it on its own.
- Run the arithmetic checks. Line items should add up to the subtotal, and the subtotal plus tax should equal the total. The share of documents that fail these checks is the share that would go to Clarke, which sets your real cost per document.
- Test classification on a confusion matrix. Look at which labels get mixed up, such as receipts filed as invoices, and fix the label definitions before you blame the model.
Use temperature 0 and a fixed seed on both sides, and decide the pass mark before you look at the results.
When Radium is the wrong choice
Radium fits when document processing is a meaningful share of your AI spend and the documents reach the model as text. There are cases where switching is the wrong move.
- Most of your documents are scans or photos and you want one model call per page. Tycho reads text only, so those pages need OCR first or need Clarke at Clarke's price.
- Your pipeline runs on a batch API and can wait a day. OpenAI's Batch API and Anthropic's Message Batches API charge half the standard price. Radium has no batch endpoint, so compare against what you pay on batch.
- Your pipeline depends on a provider's file upload or built-in PDF parsing. Radium takes text and, on Hal and Clarke, images, so the parsing step stays in your code.
- Your volume is small. If the bill is a few hundred dollars a month, the evaluation will cost more than the switch saves.
- Your contracts require a certification Radium has not published. Radium lists GDPR compliance and handles SOC 2 Type II, ISO 27001 and HIPAA questions through its security package, so request it before you send regulated documents.
If none of these apply, run the comparison on 300 documents you have already keyed.
Frequently asked questions
Can Radium extract structured data from documents as JSON?
Yes. All three Radium models support JSON mode and JSON schema through the response_format parameter, on both the OpenAI-compatible and the Anthropic-compatible endpoints. Send the document text and a schema, and the response comes back as JSON that matches it. Keep the schema flat, because recursive references and union types may not be fully supported.
Which Radium model is best for document extraction and classification?
Use Tycho (tycho-1.0) for classification, routing and field extraction from text. Use Clarke (clarke-1.0) for scanned pages and photos, long documents, fields that need judgment and documents that fail validation. Use Hal (hal-1.0) for exceptions that need reasoning across several documents.
How much does LLM document extraction cost on Radium?
Tycho costs $0.50 per million input tokens and $2.25 per million output tokens. Extracting about 20 fields from a two-page invoice, at about 4,200 input tokens and 400 output tokens, comes to roughly $0.0030 per invoice, or about $120 for 40,000 invoices a month before prompt caching.
Can Tycho read scanned PDFs or images?
No. Tycho accepts text only. Run OCR first and send the text to Tycho, or send the page images to Clarke or Hal, which accept image input. Radium does not document PDF or file upload, so your code extracts the text or renders the pages.
Does Radium have a batch API?
No. Radium does not offer a batch endpoint or a batch discount. It does not publish rate limits either, so large backlogs run as ordinary requests sent in parallel, with retries and backoff on 429 and 503 responses.
Does Radium train models on the documents we send?
No. Radium does not train on customer data, and prompts and completions are used only to service the request. Radium lists GDPR compliance, and SOC 2 Type II, ISO 27001 and HIPAA questions go through its security package.