llm11

Quickstart

Your request body and response body do not change. The base URL does, and everything else follows from that.

Step 1

Create a key

Sign up and create a key from /app/keys. Start with a sandbox key: it runs the full pipeline against real models so the receipts are real, and it is clearly marked as sandbox on every request so you can filter it out later.

Step 2

Point your client at us

Two lines change in a typical OpenAI setup, and one of them is the key.

curl

curl https://www.llm11.com/v1/chat/completions \
  -H "Authorization: Bearer $LLM11_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "messages": [
      {"role": "user", "content": "Summarise this in one sentence."}
    ]
  }'

Using an Anthropic SDK instead, point it at https://www.llm11.com/v1/messages. Same key, same behaviour, the wire format you already speak.

Step 3

Pass context, and the groundedness check turns on

context is the one addition to the OpenAI shape. Put whatever your app already retrieved into it, and every factual claim in the answer gets held against it. Leave it out and that check does not run. Nothing else about the request changes either way.

curl

curl https://www.llm11.com/v1/chat/completions \
  -H "Authorization: Bearer $LLM11_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "context": "Refunds are issued within 5 business days of a qualifying cancellation request made within 14 days of the original charge.",
    "messages": [
      {"role": "user", "content": "How long does a refund take?"}
    ]
  }'

Step 4

Read the receipt

The response is a normal completion with an extra _llm11 block, and the headline figures are mirrored into x-llm11-* headers so you can log them without parsing the body.

response (trimmed)

{
  "id": "5f0c…",
  "object": "chat.completion",
  "model": "openai/gpt-5.6-luna",
  "choices": [{ "index": 0, "message": { "role": "assistant", "content": "…" } }],
  "usage": { "prompt_tokens": 214, "completion_tokens": 38 },
  "_llm11": {
    "verdict": "pass",
    "taskType": "classification_or_extraction",
    "triageBackend": "jev",
    "candidatePool": ["openai/gpt-5.6-luna", "google/gemini-3.8-flash", "openai/gpt-5.6-terra"],
    "rungsRun": [
      { "rung": "schema", "passed": true },
      { "rung": "groundedness", "passed": true, "confidence": 0.94 }
    ],
    "costCents": 0.0041,
    "verificationCostCents": 0.0009,
    "triageCostCents": 0.0000009,
    "totalCostCents": 0.005,
    "baselineCostCents": 0.0462,
    "savingsCents": 0.0412
  }
}

costCents is what generating the answer cost, verificationCostCents is what checking it cost, and triageCostCents is what the routing decision itself cost. None of the three overlap. baselineCostCents prices the same tokens on the most expensive model in candidatePool, the pool this request was actually offered. When that model cannot be priced, it comes back null rather than zero.

Step 5

Pick a routing pack, or your own pool

By default a project routes over the llm11-balanced pack. Change it in project settings, to llm11-fast, llm11-smart, or a custom list of exactly the models you want eligible. Override it on any single request with model or llm11_models, without touching the project default.

Things that will trip you up

  • Streaming is not implemented yet. A request with stream: true gets a clear 501 rather than a non-streaming response pretending otherwise. Verification needs the finished answer, so streaming needs a design that holds the stream back until the checks clear.
  • Requests stop at a zero balance. Tokens, checks and triage all come out of prepaid credit at provider list price. At zero you get a 402, not a surprise invoice.
  • An unknown id in llm11_models is refused, not ignored. A pool with a typo in it returns a 400 naming what was wrong, so your traffic never silently narrows.

Moving an existing app across? The migration guide covers rollback, dual-running and what to watch in the first week.