Documentation

Decisions with an honest probability for every option, on text, images and recordings.

Try it in the playground

Quick start

Base URL
https://api.parallelfoundry.com/v1
Model
parallel-foundry/parallel
Key
header Authorization: Bearer pf-... (or X-API-Key); make one on Keys in the console
Price
$0.025 per million input tokens; output is free; no minimum per call (how tokens are counted)
curl https://api.parallelfoundry.com/v1/decide \
  -H "Authorization: Bearer $PARALLEL_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "My card was charged twice, please refund one payment.",
    "heads": ["intent=refund,card lost,balance", "urgent=bool", "frustration=score:1-5"]
  }'
{
  "intent": {"type": "choice", "answer": "refund",
             "probabilities": {"refund": 0.9942, "balance": 0.0037, "card lost": 0.0021}},
  "urgent": {"type": "bool", "answer": "no", "p_yes": 0.3758},
  "frustration": {"type": "score", "answer": "2", "expected": 2.263,
                  "probabilities": {"2": 0.3698, "1": 0.2707, "3": 0.2189, "4": 0.1072, "5": 0.0334}},
  "usage": {"input_tokens": 76, "output_tokens": 0,
            "input_tokens_detail": {"decision": 76, "media": 0, "writing": 0, "unread": 0}},
  "seconds": 0.021
}

POST /v1/decide

One request: an input (a text, images, a recording, or a text with them) and any number of questions about it, all answered in one pass. The questions can be strings or objects.

FieldWhat
textthe text to decide on
headsthe questions. Strings: name="a,b,c" (one of the options), name=bool (yes/no), name=score:1-5 or name=score:"low,medium,high" (a scale), name=multi:"a,b,c" (every option that applies), name=free:"Question?" (no options: Parallel proposes them). Objects: {name, type, question, options} with type choice, bool, score or multi. Names are read as words (refund_request = "refund request"); usage and seconds are taken by the answer
questionwith options: one choice question written out
freea question (or a list of up to 3) without options: Parallel proposes the answers and gives them %
reasontrue: a short written explanation of every answer (text only, up to 8 questions)
toolsa list of tools (name, description, JSON-schema parameters; plain or OpenAI-style): Parallel picks one, or "none of these tools", with %, and writes its arguments. tool_choice: auto (default), required, or {"type": "function", "function": {"name": ...}}
imagesup to 4, each a base64 data URL (JPEG, PNG, WebP, GIF, BMP); links are not fetched
image_tokensthe detail of the images: 70, 140, 280 (default), 560 or 1120. More detail reads finer print and costs more (pricing); photos do well at 70-140, documents at 280-560
audiorecordings, each a base64 data URL (WAV, FLAC or OGG, any sample rate), up to 60 seconds in all
{
  "text": "The food was great but we waited 40 minutes.",
  "heads": [
    {"name": "sentiment", "type": "choice", "question": "overall sentiment", "options": ["positive", "mixed", "negative"]},
    {"name": "urgent", "type": "bool", "question": "Is it urgent?"},
    {"name": "rating", "type": "score", "question": "rating", "options": ["1", "2", "3", "4", "5"]},
    {"name": "aspects", "type": "multi", "question": "aspects", "options": ["food", "service", "price", "wait"]}
  ],
  "reason": true
}
{
  "images": ["data:image/jpeg;base64,/9j/4AAQ..."],
  "image_tokens": 280,
  "heads": ["type=invoice,receipt,letter,form", "amount_over_300=bool"]
}

{
  "audio": ["data:audio/wav;base64,UklGR..."],
  "heads": ["intent=card issues,balance,pay bill,other"]
}
{
  "text": "Can you book a table for four tomorrow at 7:30 pm at Luma Bistro?",
  "tools": [
    {"name": "book_table", "description": "Book a restaurant table",
     "parameters": {"type": "object", "properties": {"restaurant": {"type": "string"},
                    "people": {"type": "integer"}, "time": {"type": "string"}}, "required": ["restaurant", "people"]}},
    {"name": "get_weather", "description": "Current weather in a city",
     "parameters": {"type": "object", "properties": {"city": {"type": "string"}}}}
  ],
  "tool_choice": "auto"
}

Answers

One entry per question, under its name, and the call's usage:

TypeFields
choiceanswer (the likeliest option), probabilities (every option, summing to 1)
boolanswer ("yes" or "no"), p_yes
scoreanswer, probabilities, expected (the average on a number scale; expected_index on a word scale)
multianswer (every option at 50% or more), probabilities (each its own yes)
toolanswer (the tool, or null for none), probabilities, arguments (only what the tool's schema declares, in its types; anything missing is listed under problems, never invented)
with reasonalso reason: a sentence or two on why
proposed (free)also options_proposed: true
usageinput_tokens (what you pay for), output_tokens (free), and input_tokens_detail: decision (the text and questions read), media (images and recordings), writing (reasons, proposed options, tool arguments), unread (sent but not read)

OpenAI-compatible: POST /v1/chat/completions

The OpenAI SDKs work with base_url="https://api.parallelfoundry.com/v1". Two kinds of call:

  • Decisions: response_format json_schema whose fields are enums, booleans, integer ranges (2 to 11 values) or arrays of enums. Every field is decided in one pass; the content is that JSON, always valid, and decisions holds every option's %. A field's description is its question. Images (image_url, base64) and recordings (input_audio) work too.
  • Tool calls: tools and tool_choice: the tool is picked with %, its arguments written; when no tool fits, the answer is empty with each tool's %.

Free-form answers (plain chat, written JSON, the answer after a tool result) are not offered: they get 400 chat_not_offered. stream: true works (with stream_options.include_usage). GET /v1/models lists the model; GET /v1/key shows your key's credit and limits.

from openai import OpenAI

client = OpenAI(base_url="https://api.parallelfoundry.com/v1", api_key="pf-...")
r = client.chat.completions.create(
    model="parallel-foundry/parallel",
    messages=[{"role": "user", "content": "I was charged twice, please refund one payment now!"}],
    response_format={"type": "json_schema", "json_schema": {"name": "ticket", "schema": {
        "type": "object", "properties": {
            "intent": {"type": "string", "enum": ["refund", "cancel order", "order status", "complaint"]},
            "urgent": {"type": "boolean"},
            "frustration": {"type": "integer", "minimum": 1, "maximum": 5}}}}})
print(r.choices[0].message.content)   # {"intent": "refund", "urgent": true, "frustration": 4}
print(r.model_dump()["decisions"])    # every option's probability

Pricing: how input tokens are counted

$0.025 per million input tokens. Output is free and there is no minimum per call. Input is everything you send and everything the model reads to answer it, at every step:

InputCounts asCosts
Textthe tokens read: about 1.44 tokens a word of English (1,000 words: about 1,440)$0.000036 per 1,000 words
Questions and optionsas read (a question with three options: about 15-30 tokens)
An image at detail 701,000 tokens, whatever its file size (every image is resized to its detail)$0.000025
An image at detail 1401,700 tokens, whatever its file size (every image is resized to its detail)$0.0000425
An image at detail 280 (default)3,800 tokens, whatever its file size (every image is resized to its detail)$0.000095
An image at detail 5608,000 tokens, whatever its file size (every image is resized to its detail)$0.0002
An image at detail 112022,000 tokens, whatever its file size (every image is resized to its detail)$0.00055
A recording160 tokens a second (30 s: 4,800)$0.00024 a minute
Steps that write (reasons, proposed options, tool arguments)what they read (the text again, with their instruction), counted 3 times: writing takes far longer than deciding, as a model that thinks longer costs more. What they write is free
Sent but not readcounted as sent: the tools not picked, system messages in a decision, a text past the 10,000-token limit

Every answer says what it counted (usage.input_tokens_detail); the console's Usage page shows every call.

Limits

A requestat most 10 MB as sent (base64 files included), 10,000 tokens read (a longer text is cut, its rest still counted), 32 questions of up to 200 options
Images4 a request, each at most 50 megapixels
Recordings60 seconds a request in all
Written partsreasons with up to 8 questions (a reason at most 80 tokens), proposed options for up to 3, tool arguments up to 256 tokens
Per key (default)50 calls a second, 2,500 a minute, 5,000,000 input tokens a minute; with images or recordings 20 a second; written parts 10 a second and 6,000 written tokens a minute; 200 at once. Need more? Ask us
Busywhen every GPU is full the answer is 429 at once, with Retry-After: nothing waits in a queue

Errors

Errors are {"error": {"message", "type", "code"}}. Refused calls are never charged.

StatusWhen
400a bad request (the message says what): a malformed question, a file that cannot be read, a reserved name; chat_not_offered: a free-form answer
401no key, a wrong key, or a revoked one (within 30 seconds of revoking)
402no credit left: add credit
404another model or path
413too large: the 10 MB, the 10,000 tokens before the text, an image over 50 megapixels, recordings over 60 seconds
429a limit of your key, or every GPU busy (with Retry-After)
503the model is starting or unreachable: try again after Retry-After

Privacy

No text, image, recording or answer is stored, logged or used for training. For each call we keep its time, key, kind and structure (which inputs, how many questions of which type, reasons on or off), tokens, cost, status and the country it came from, for billing and your call history (the console shows 30 days).

Questions: hello@parallelfoundry.com. The console's playground shows the exact request for every query (curl, Python, Node.js).