Documentation

myopenai is an OpenAI-compatible API gateway with hard per-key spend caps. Existing OpenAI SDK code works after swapping the base_url and the API key.

Quickstart

  1. Create an account and add credit from the billing page.
  2. Create an API key. Optionally set a dollar cap and reset window (daily, monthly or total).
  3. Set the base_url in your code to https://myopenai.site/v1.

SDK examples

Python

from openai import OpenAI

client = OpenAI(
    api_key="mo_live_YOUR_KEY",
    base_url="https://myopenai.site/v1",
)

response = client.chat.completions.create(
    model="claude-sonnet-5",
    messages=[{"role": "user", "content": "Hello"}],
    max_tokens=512,
)
print(response.choices[0].message.content)

Python — streaming

stream = client.chat.completions.create(
    model="claude-sonnet-5",
    messages=[{"role": "user", "content": "Write a haiku"}],
    stream=True,
    stream_options={"include_usage": True},  # final chunk carries token usage
)
for chunk in stream:
    if chunk.choices and chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="")

Python — tool calling

import json

tools = [{
    "type": "function",
    "function": {
        "name": "get_weather",
        "description": "Current weather for a city",
        "parameters": {
            "type": "object",
            "properties": {"city": {"type": "string"}},
            "required": ["city"],
        },
    },
}]
messages = [{"role": "user", "content": "What is the weather in Berlin?"}]

response = client.chat.completions.create(
    model="claude-sonnet-5", messages=messages, tools=tools,
)
message = response.choices[0].message
if message.tool_calls:
    messages.append(message)
    for call in message.tool_calls:
        args = json.loads(call.function.arguments)
        messages.append({
            "role": "tool",
            "tool_call_id": call.id,
            "content": get_weather(**args),  # your function
        })
    response = client.chat.completions.create(
        model="claude-sonnet-5", messages=messages, tools=tools,
    )
print(response.choices[0].message.content)

Node.js

import OpenAI from 'openai';

const client = new OpenAI({
  apiKey: 'mo_live_YOUR_KEY',
  baseURL: 'https://myopenai.site/v1',
});

const response = await client.chat.completions.create({
  model: 'claude-sonnet-5',
  messages: [{ role: 'user', content: 'Hello' }],
});
console.log(response.choices[0].message.content);

curl

curl https://myopenai.site/v1/chat/completions \
  -H "Authorization: Bearer mo_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "claude-sonnet-5",
    "messages": [{"role": "user", "content": "Hello"}]
  }'

Models & pricing

Prices are per million tokens in US dollars and are what your credit is charged. The same list is available to SDKs from GET /v1/models.

Loading current models and prices…

Endpoints & parameters

  • POST /v1/chat/completions — chat completions, streaming and non-streaming.
  • GET /v1/models and GET /v1/models/{model} — the models your key can use.

Tool calling works with the standard OpenAI parameters: tools (function tools), tool_choice (auto, required, none or a named function), tool role messages, and tool_calls deltas when streaming. With tool_choice: "none" the model answers in text and earlier tool calls in the conversation are passed to it as text. Claude models may return several tool calls even with parallel_tool_calls: false.

So that a request never silently does something other than what you asked, these return 400 instead of being ignored:

  • the deprecated functions and function_call parameters (use tools)
  • image or other non-text message content
  • for Claude models: n greater than 1, logprobs, and response_format other than text

temperature and top_p are not accepted by Claude 5 models and are ignored. stop takes up to 4 sequences; whitespace-only sequences are ignored. developer messages are treated as system messages.

Spend caps, holds and HTTP 402

  • Caps are per key, not per account: one key per project gives one budget per project.
  • Before each call the gateway places a hold: an upper bound on what the call can cost, from the request size and its max_tokens. The hold is checked against both your wallet and the key’s cap. If either cannot cover it, the request is refused with HTTP 402 and the upstream model is never called.
  • After the call only the tokens the model reports are charged and the rest of the hold is released. If a stream is interrupted before the model reports usage, the hold is charged.
  • If you omit max_tokens, the default below is applied and sent upstream, so no response can outgrow its hold. Set a smaller max_tokens for smaller holds.
  • Windows: day resets at 00:00 UTC, month on the 1st at 00:00 UTC, total never resets.
  • You can change a key’s cap at any time on the keys page.

The 402 body is OpenAI-shaped; amounts are integer micro-dollars (1 USD = 1,000,000 micros):

HTTP/2 402

{
  "error": {
    "message": "api key spend cap exceeded: cap_micros=5000000 spent_micros=4990000 required_micros=57036 cap_window=month",
    "type": "cap_exceeded",
    "code": "cap_exceeded"
  }
}

Handle it by catching the 402 status (the OpenAI SDKs raise an API status error) and alerting the user or pausing automated requests. insufficient_funds means the wallet needs credit; cap_exceeded means the key’s budget for the window is spent.

Limits

Loading current limits…

Errors

Every error has the OpenAI shape {"error": {"message", "type", "code"}}.

StatusTypeMeaning
400invalid_request_errorMalformed body, missing model, unpriced model, or a parameter the model does not support. The message names the problem.
401invalid_request_errorMissing, unknown or revoked API key.
402insufficient_fundsThe wallet cannot cover the hold for this request. Add credit.
402cap_exceededThe hold would push the key past its spend cap in the current window.
404model_not_foundThe model id is not offered. List models with GET /v1/models.
413invalid_request_errorRequest body larger than the limit below.
429rate_limit_errorPer-key rate or concurrency limit, or upstream throttling. Honour Retry-After.
502 / 503 / 504api_errorUpstream failure, unavailability or timeout. Nothing is charged; retry with backoff.