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.
https://openspender.com/v1The 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/searchQuickstart
Point an existing client at the base URL. Anything that speaks the OpenAI or Anthropic wire format works unmodified.
export ANTHROPIC_BASE_URL=https://openspender.com/v1/anthropic
export ANTHROPIC_AUTH_TOKEN=$OPENSPENDER_ALLOWANCE_TOKENcurl 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"}]
}'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.
- 1Explicit path.
/v1/anthropic/messagesgoes to Anthropic. No ambiguity, no lookup. - 2Prefixed model. Post to
/v1/chat/completionswith"model": "anthropic/claude-opus-5"and the prefix picks the provider — the OpenRouter convention. - 3Catalog 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.
| Slug | Example | Forwards to | Protocol |
|---|---|---|---|
anthropic | /v1/anthropic/messages | https://anthropic.mpp.tempo.xyz/v1 | MPP |
openai | /v1/openai/chat/completions | https://openai.mpp.tempo.xyz/v1 | MPP |
exa | /v1/exa/search | x402 · Bazaar catalog | x402 |
firecrawl | /v1/firecrawl/scrape | x402 · Bazaar catalog | x402 |
fal | /v1/fal/flux-1.1-pro | x402 · Bazaar catalog | x402 |
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.
→ 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": {...}}