The router

One base URL in front of every machine-payable endpoint. Point a client at it, name a provider in the path, and the request goes where it should — paid per call from your allowance.

Base URL

One host in front of every provider. Everything below is this base plus two more path segments.

base url
https://openspender.com/v1

The route

A provider slug, then whatever path that provider expects. Nothing is rewritten, and the body is passed through untouched.

POST  {base}/{provider}/{endpoint}

So a Messages call to Anthropic and a search call to Exa differ only in the two segments after the base:

POST  https://openspender.com/v1/anthropic/messages
POST  https://openspender.com/v1/exa/search

Quickstart

Point an existing client at the base URL. Anything that speaks the OpenAI or Anthropic wire format works unmodified.

claude code
export ANTHROPIC_BASE_URL=https://openspender.com/v1/anthropic
export ANTHROPIC_AUTH_TOKEN=$OPENSPENDER_ALLOWANCE_TOKEN
curl
curl https://openspender.com/v1/anthropic/messages \
  -H "Authorization: Bearer $OPENSPENDER_ALLOWANCE_TOKEN" \
  -H "content-type: application/json" \
  -d '{
    "model": "claude-opus-5",
    "max_tokens": 1024,
    "messages": [{"role": "user", "content": "hello"}]
  }'
openai sdk
from openai import OpenAI

client = OpenAI(
    base_url="https://openspender.com/v1/openai",
    api_key=os.environ["OPENSPENDER_ALLOWANCE_TOKEN"],
)

client.chat.completions.create(
    model="gpt-5.1",
    messages=[{"role": "user", "content": "hello"}],
)

How a route resolves

Three ways to name a destination, tried in order. The first one that matches wins, so an explicit path always beats inference.

  1. 1
    Explicit path. /v1/anthropic/messages goes to Anthropic. No ambiguity, no lookup.
  2. 2
    Prefixed model. Post to /v1/chat/completions with "model": "anthropic/claude-opus-5" and the prefix picks the provider — the OpenRouter convention.
  3. 3
    Catalog lookup. A bare model or service name is resolved against the indexed catalog. Ambiguous names are rejected rather than guessed.

Providers

openspender never resells inference. Each slug forwards to the provider's own machine-payable endpoint.

SlugExampleForwards toProtocol
anthropic/v1/anthropic/messageshttps://anthropic.mpp.tempo.xyz/v1MPP
openai/v1/openai/chat/completionshttps://openai.mpp.tempo.xyz/v1MPP
exa/v1/exa/searchx402 · Bazaar catalogx402
firecrawl/v1/firecrawl/scrapex402 · Bazaar catalogx402
fal/v1/fal/flux-1.1-prox402 · Bazaar catalogx402

Plus every x402 service in the Bazaar catalog and every service in the MPP registry, addressable by the same grammar.

Payment

Providers answer an unpaid request with a challenge — 402 Payment Required for x402, a WWW-Authenticate: Payment header for MPP. openspender reads the challenge, checks it against your allowance, pays, and replays the request. Your client sees one response.

what your client never has to handle
→ POST /v1/anthropic/messages          (unpaid probe)
← 402  WWW-Authenticate: Payment …     (provider quotes)
   ·   policy check against your allowance
→ POST /v1/anthropic/messages          (payment attached)
← 200  {"content": [...], "usage": {...}}