Document Extraction and Classification

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.

tycho-1.0
October 14, 2026
At a glance
Recommended model
Tycho
Model ID
tycho-1.0
Price per 1M tokens
$0.50 input / $2.25 output
Compatibility
OpenAI and Anthropic compatible APIs. Change the base URL, key and model.
Last updated
October 14, 2026

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.

AssumptionValue
Instructions and JSON schema, sent on every call1,200 tokens
Invoice text, two pages3,000 tokens
Input tokens per invoice4,200
Output tokens per invoice, about 20 fields and the line items400
Cost per invoice on Tycho$0.0030
Cost per 100,000 invoicesAbout $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.

AssumptionValue
Instructions and the list of labels, sent on every call800 tokens
First page of the document1,500 tokens
Input tokens per document2,300
Output tokens per document, the label50
Cost per document on Tycho$0.0013
Cost per million documentsAbout $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.

StepModelModel ID
Classification, routing and field extraction from textTychotycho-1.0
Scanned pages and photos, long documents, fields that need judgment, documents that fail validationClarkeclarke-1.0
Reconciling one document against others, exceptions that need reasoningHalhal-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.

CapabilityWhy extraction and classification need itOn Radium
Structured outputReturning fields that your database or ERP can load without parsing free textJSON mode and JSON schema on all three models, through response_format
Image inputReading scanned pages, photos and stampsHal and Clarke. Tycho reads text only
Context windowHolding a long contract or several related documents in one callTycho 125,000 tokens, Hal 250,000, Clarke 1,000,000
ConcurrencyWorking through a backlog of thousands of documentsNo published rate limits. Send requests in parallel and retry 429 and 503 responses with backoff
Repeatable runsGetting the same fields from the same document twicetemperature 0 with a fixed seed
Token countingEstimating cost before a large runPOST /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.

OpenAI SDK, Python
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)
OpenAI SDK, TypeScript
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);
OpenAI SDK, curl
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!"}]
  }'
OpenAI SDK, Go
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)
}
OpenAI SDK, Java
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());
OpenAI SDK, C#
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);
Anthropic SDK, Python
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)
Anthropic SDK, TypeScript
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);
Anthropic SDK, curl
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.

Python, invoice extraction
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

  1. 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.
  2. 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.
  3. 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.
  4. 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.

  1. 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.
  2. 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.
  3. 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.
  4. 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.