unbiased ai

Turn messy text into typed records

Support emails arrive as run-on sentences. Your code wants a customer, an order number and a category. Write the record once as a Pydantic class, send each message to Pareto and get back an object that already passed validation. Pareto is Unbiased’s model: it selects frontier and open models for each request and blends their outputs into one answer.

Tested live with Pydantic AI 2.58.0 and openai 3.35.150M tokens a week

A replay of the recipe below running on Pareto, recreated from a real run. Not a screenshot.

Tools that do this

Each of these talks to Pareto with a base URL, an API key and the model string pareto. Pick the one you already use.

The recipe: one class, one loop

Both versions below send the same three messages and print the same Ticket records. Pick Pydantic AI if you want validation retries and agents later; pick the OpenAI SDK if you want the fewest moving parts. Either way the schema travels as a tool call, which is the structured-output path Pareto supports.

  1. Get an Unbiased API key

    Choose a plan, then create a key in the Unbiased platform. New accounts are reviewed and approved by a person before the key works.

  2. Write the record as a Pydantic class

    Every field your code needs, with real types. Use Literal for a fixed set of labels and | None for anything a message might leave out, so the model has an honest answer when the text is silent.

  3. Point the client at Pareto

    Base URL https://api.unbiased.ai/v1, your Unbiased key, and the model name pareto, typed by hand: Unbiased does not list models at /v1/models.

  4. Send the class as a tool, not a response format

    Pydantic AI does this by default. With the OpenAI SDK, wrap the class in pydantic_function_tool and force the call with tool_choice. A JSON schema in response_format gets an HTTP 400 from Pareto.

  5. Loop over your text and store the records

    One message, one request, one validated object. Write model_dump_json() to a file, a queue or a database row.

Tested on October 20, 2026Pydantic AI 2.58.0 and openai (Python) 3.35.1 on Python 3.15.0, model pareto. Both recipes turned three messy support messages into validated Ticket records with the right names, order number, amount and category, 3 runs out of 3 each. Run record.

Pydantic AI

Agent(model, output_type=Ticket) turns the class into a tool the model must call, checks the arguments against the class, and returns a Ticket.

  • Install: pip install "pydantic-ai-slim[openai]"
  • Tested with Pydantic AI 2.58.0 on Python 3.15.0
  • Set PYDANTIC_AI_NO_BANNER=1 to hide the startup banner
tickets.py
import os
from typing import Literal
from pydantic import BaseModel
from pydantic_ai import Agent
from pydantic_ai.models.openai import OpenAIChatModel
from pydantic_ai.providers.openai import OpenAIProvider

class Ticket(BaseModel):
    customer: str
    order_id: str | None
    category: Literal['shipping', 'billing', 'bug', 'other']
    amount: float | None
    urgent: bool

model = OpenAIChatModel(
    'pareto',
    provider=OpenAIProvider(
        base_url='https://api.unbiased.ai/v1',
        api_key=os.environ['UNBIASED_API_KEY'],
    ),
)
agent = Agent(
    model,
    output_type=Ticket,
    instructions='Extract one support ticket. Use null for anything the message does not say.',
)

inbox = [
    "hi its Priya Nair, order #A-10442 never showed up, tracking says delivered?? need it by friday for my sisters wedding",
    "Charged twice for my annual plan ($96 each). pls refund one. Tom Okafor",
    "app crashes every time i export a pdf on ipad. not urgent, just annoying. cheers, M. Lindqvist",
]
for text in inbox:
    ticket = agent.run_sync(text).output
    print(ticket.model_dump_json())
Shell
pip install "pydantic-ai-slim[openai]"
export UNBIASED_API_KEY=your-unbiased-api-key
python tickets.py
Output from our run (both versions)
{"customer":"Priya Nair","order_id":"A-10442","category":"shipping","amount":null,"urgent":true}
{"customer":"Tom Okafor","order_id":null,"category":"billing","amount":96.0,"urgent":false}
{"customer":"M. Lindqvist","order_id":null,"category":"bug","amount":null,"urgent":false}

What it costs on Pareto

Extraction is short in, short out, so a record is a few hundred tokens. These are the counts the API reported in our runs, not an estimate for your messages: longer text and bigger classes cost more.

Tokens per recordMeasured: 18 records, October 20, 2026
371–694
input plus output, as the API reported
Personal at $10/month50M tokens a week, 20 requests a minute
~72,000
records a week at 694 tokens each
Team, pay as you go$0.80 in, $3.20 out per million tokens
$1.49
per 1,000 records like our largest

Records a week assume every record is as large as our largest (694 tokens) and ignore the request rate; Team cost uses our largest record’s split of 303 input and 391 output tokens at the published rates. Your messages and classes will differ. Running this for a business is production use, which needs Team or a written agreement. Run record.

One key for every tool on this page

Pick a monthly allowance, or pay per token on Team.

Personal

$10/month

  • 50M tokens a week
  • 20 requests per minute
  • For one person
Choose Personal

Personal Max

$100/month

  • 600M tokens a week
  • 60 requests per minute
  • For one person
Choose Personal Max

Team

Pay as you go

  • Prepaid credits at published rates
  • 300 requests per minute
  • No weekly token cap
Get started with Team

Subscriptions are for one person and are not unlimited. New API accounts are reviewed by a person before access turns on, and commercial or production use needs a separate written agreement. Rates and limits are on Pricing.

Questions about this recipe

Why use a tool call instead of response_format?

Because that is what works on Pareto today. In our test on October 20, 2026, a JSON schema sent in response_format came back as HTTP 400 unsupported_parameter, both from the OpenAI SDK’s parse(response_format=Ticket) and from Pydantic AI’s NativeOutput. Tool calls worked, and so did Pydantic AI’s PromptedOutput. Format compatibility is not a promise that every feature works, so tell us if one doesn’t.

What happens when the model returns something that doesn’t fit the class?

Pydantic AI sends the validation error back to the model and asks again, then raises if the retries run out. With the OpenAI SDK, parse raises a Pydantic ValidationError, so wrap the call and retry or set the message aside for a person. None of our 6 runs needed a retry.

Will the same message always give the same record?

Facts written in the text came back the same in every run we made: names, order numbers, amounts and categories. Judgment calls can move: in 1 of the 12 runs we saved, the double charge came back as urgent. If a field needs a rule, put the rule in the instructions, for example “urgent only if the customer names a deadline”.

How fast can I process a backlog?

Each record is one request, so your plan’s request rate sets the pace: 20 requests per minute on Personal. Each of our 18 measured records took between 0.9 and 2.5 seconds. Requests over your plan’s limits return HTTP 429, so back off and retry.

Do I need an OpenAI account?

No. The class names say OpenAI because that is the request format. With base_url and api_key set, requests go to Unbiased with your Unbiased key, and your Unbiased plan pays for them.

What happens when I hit the weekly allowance?

Requests over your plan’s limits return an HTTP 429 error. Personal includes 50M tokens a week and 20 requests per minute. Personal Max raises that to 600M a week and 60 requests per minute for $100/month. Team uses prepaid pay-as-you-go credits with no weekly token cap.

Do I choose between models?

No. You send every request to one model string, pareto. Pareto selects frontier and open models for each request and blends their outputs into one answer.

Is my code kept?

Personal and Personal Max are not our zero data retention tier: prompts and responses may be kept up to 30 days, as section 5(c) of the Terms explains. For work that must not be kept, ask about the Zero Data Retention API on the Private AI page.

Same key, same model string, different job. See every use case. Or find your tool on Works with.

Product names belong to their owners. Unbiased is not affiliated with or endorsed by the makers of the tools on this page.

Give Pareto the inbox nobody wants to read

One class, one loop and an API key. Pareto starts at $10 a month for 50M tokens a week.