OpenAI
Route your OpenAI API calls through AI SpendOps for automatic usage tracking and cost attribution.
Configuration
| Setting | Value |
|---|---|
| Route | /v1/openai/* |
| Upstream | https://api.openai.com |
| Auth header | Authorization: Bearer sk-... |
| Streaming usage | Auto-injected (stream_options.include_usage) |
SDK base URL
https://proxy.aispendops.com/v1/openai/v1
Example
curl https://proxy.aispendops.com/v1/openai/v1/chat/completions \
-H "Authorization: Bearer sk-your-openai-key" \
-H "X-ASO-API-Key: aso_k_yourkey.secret" \
-H "Content-Type: application/json" \
-d '{"model":"gpt-4o","messages":[{"role":"user","content":"Hello"}]}'
Python SDK
from openai import OpenAI
client = OpenAI(
api_key="sk-your-openai-key",
base_url="https://proxy.aispendops.com/v1/openai/v1",
default_headers={"X-ASO-API-Key": "aso_k_yourkey.secret"},
)
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "Hello"}],
)
print(response.choices[0].message.content)
Usage fields
| Field | Description |
|---|---|
prompt_tokens | Input tokens, including any cached tokens |
completion_tokens | Output tokens, including any reasoning tokens |
total_tokens | Sum of prompt and completion tokens |
prompt_tokens_details.cached_tokens | Portion of prompt_tokens served from cache |
prompt_tokens_details.audio_tokens | Audio input tokens |
prompt_tokens_details.image_tokens | Image input tokens |
completion_tokens_details.reasoning_tokens | Portion of completion_tokens spent on reasoning |
completion_tokens_details.audio_tokens | Audio output tokens |
OpenAI reports the most granular usage of any supported provider. The important
structural point is that the _details objects are breakdowns of the totals
above them, not additions to them.
Cache token accounting
cached_tokens is a subset of prompt_tokens, not a separate category. Before
pricing, AI SpendOps subtracts the cached portion from the prompt total and
charges it at the cache-read rate, so the cached input is billed once at the
discounted rate rather than twice.
This is the opposite of Anthropic, where input_tokens,
cache_read_input_tokens and cache_creation_input_tokens are three separate
non-overlapping categories and nothing is subtracted. Getting the two the wrong
way round silently over-bills one provider and under-bills the other, which is
why the distinction is handled per provider rather than generically.
There is no cache write cost on OpenAI. Caching is automatic above the minimum prompt length, with no cache control parameter to set and nothing to pay for populating the cache.
Reasoning tokens
For o-series and other reasoning models, reasoning_tokens counts thinking that
is billed at the output rate but never appears in the response text. It is
already included in completion_tokens, so AI SpendOps records it as a separate
field for visibility without adding it to the total again.
This is worth watching in its own right. A reasoning model can return a short
answer while billing a large number of output tokens, and reasoning spend is
usually the largest gap between what teams expect a model to cost and what it
actually costs. Filter your usage events on reasoning_tokens > 0 to see how
much of your output spend is thinking rather than answers.
Notes
- Any OpenAI endpoint works through the proxy. Prefix the path with
/v1/openai, including embeddings, images, and audio. - For
/images/*calls there are no tokens to count, so AI SpendOps recordsimage_count,image_size, andimage_qualityfrom the request instead. - The proxy injects
stream_options: { include_usage: true }on streaming requests, so streamed calls report real token counts. See Streaming. - Full field definitions are in the usage event field reference.