Skip to main content

LiteLLM Integration

LiteLLM is a popular library for calling 100+ LLM APIs through a unified interface. It works with AI SpendOps by setting the API base URL per provider.

How the two layers fit together

LiteLLM and AI SpendOps both sit between your code and the provider, so it is worth being clear about which does what.

LiteLLM normalises the call: one function signature across providers, with retries, fallbacks, and translation between provider-specific request shapes. AI SpendOps records the spend: token-level cost per request, attribution to a team or feature, and enforcement of budgets and model policies at call time.

They stack cleanly because LiteLLM still makes a normal HTTPS request to whatever api_base you give it. Point that at the AI SpendOps route for the provider and the call is tracked without changing anything else about how you use LiteLLM.

Python

import litellm

response = litellm.completion(
model="openai/gpt-4.1",
messages=[{"role": "user", "content": "Hello"}],
api_key="sk-your-openai-key",
api_base="https://proxy.aispendops.com/v1/openai/v1",
extra_headers={
"X-ASO-API-Key": "aso_k_yourkey.secret",
"X-ASO-Dims": "team=ml,app=chatbot",
},
)
print(response.choices[0].message.content)

The model prefix and the route are separate

This is the most common mistake. LiteLLM's openai/ or anthropic/ prefix tells LiteLLM which request format to use. The /v1/openai or /v1/anthropic segment in api_base tells AI SpendOps which upstream to call and which rate card to price against.

They have to agree. Pointing model="anthropic/..." at https://proxy.aispendops.com/v1/openai/v1 will not work, and there is no sensible way for either layer to guess what you meant.

# OpenAI through the proxy
response = litellm.completion(
model="openai/gpt-4.1",
messages=messages,
api_key="sk-openai-key",
api_base="https://proxy.aispendops.com/v1/openai/v1",
extra_headers={"X-ASO-API-Key": "aso_k_yourkey.secret"},
)

# Anthropic through the proxy — note there is no trailing /v1 here,
# because the Anthropic SDK path already includes it
response = litellm.completion(
model="anthropic/claude-sonnet-4-5-20250929",
messages=messages,
api_key="sk-ant-key",
api_base="https://proxy.aispendops.com/v1/anthropic",
extra_headers={"X-ASO-API-Key": "aso_k_yourkey.secret"},
)

Every provider's exact base URL is listed on its own page under Providers.

When LiteLLM's cost and AI SpendOps' cost disagree

LiteLLM can calculate a cost locally from a price map bundled with the library. AI SpendOps calculates cost centrally from a rate card that is synced on a schedule and can carry your own negotiated rates.

The two will not always match, and the usual reasons are worth knowing:

  • Stale price map. A pinned LiteLLM version carries the prices that shipped with it. A model repriced by the provider since then will be costed at the old rate locally.
  • Cache and reasoning handling. Providers disagree about whether cached tokens are inside or outside the prompt total. AI SpendOps applies per-provider rules, described on each provider page.
  • Custom rates. If you have negotiated pricing loaded into AI SpendOps, no local library can know about it.

Where they differ, the AI SpendOps figure is the one to reconcile against your invoice, and events carry a cost_source field recording whether the number came from the provider in-band, your custom rates, or the standard rate card.

Environment variables

export OPENAI_API_BASE="https://proxy.aispendops.com/v1/openai/v1"
export OPENAI_API_KEY="sk-your-openai-key"

Then pass the ASO headers per request:

response = litellm.completion(
model="openai/gpt-4.1",
messages=messages,
extra_headers={"X-ASO-API-Key": "aso_k_yourkey.secret"},
)

Notes

  • Fallbacks work normally, but each fallback target needs its own api_base pointing at that provider's AI SpendOps route, otherwise a failover silently bypasses tracking.
  • Retries produce one usage event per attempt that reaches the provider, which is intentional: a retried request costs money twice.
  • Use X-ASO-Dims to tag calls by team, feature, or customer. Set them per request rather than on the client if a single service handles work for more than one of them.