diff --git a/docs/my-website/docs/enterprise.md b/docs/my-website/docs/enterprise.md
index cfab07c22a..e3758266a1 100644
--- a/docs/my-website/docs/enterprise.md
+++ b/docs/my-website/docs/enterprise.md
@@ -10,22 +10,23 @@ Interested in Enterprise? Schedule a meeting with us here 👉
This covers:
- **Enterprise Features**
- **Security**
- - ✅ [SSO for Admin UI](./ui.md#✨-enterprise-features)
- - ✅ [Audit Logs with retention policy](#audit-logs)
+ - ✅ [SSO for Admin UI](./proxy/ui#✨-enterprise-features)
+ - ✅ [Audit Logs with retention policy](./proxy/enterprise#audit-logs)
- ✅ [JWT-Auth](../docs/proxy/token_auth.md)
- - ✅ [Control available public, private routes](#control-available-public-private-routes)
- - ✅ [[BETA] AWS Key Manager v2 - Key Decryption](#beta-aws-key-manager---key-decryption)
- - ✅ [Use LiteLLM keys/authentication on Pass Through Endpoints](pass_through#✨-enterprise---use-litellm-keysauthentication-on-pass-through-endpoints)
- - ✅ [Enforce Required Params for LLM Requests (ex. Reject requests missing ["metadata"]["generation_name"])](#enforce-required-params-for-llm-requests)
+ - ✅ [Control available public, private routes](./proxy/enterprise#control-available-public-private-routes)
+ - ✅ [[BETA] AWS Key Manager v2 - Key Decryption](./proxy/enterprise#beta-aws-key-manager---key-decryption)
+ - ✅ [Use LiteLLM keys/authentication on Pass Through Endpoints](./proxy/pass_through#✨-enterprise---use-litellm-keysauthentication-on-pass-through-endpoints)
+ - ✅ [Enforce Required Params for LLM Requests (ex. Reject requests missing ["metadata"]["generation_name"])](./proxy/enterprise#enforce-required-params-for-llm-requests)
- **Spend Tracking**
- - ✅ [Tracking Spend for Custom Tags](#tracking-spend-for-custom-tags)
+ - ✅ [Tracking Spend for Custom Tags](./proxy/enterprise#tracking-spend-for-custom-tags)
+ - ✅ [API Endpoints to get Spend Reports per Team, API Key, Customer](./proxy/cost_tracking.md#✨-enterprise-api-endpoints-to-get-spend)
- **Guardrails, PII Masking, Content Moderation**
- - ✅ [Content Moderation with LLM Guard, LlamaGuard, Secret Detection, Google Text Moderations](#content-moderation)
- - ✅ [Prompt Injection Detection (with LakeraAI API)](#prompt-injection-detection---lakeraai)
+ - ✅ [Content Moderation with LLM Guard, LlamaGuard, Secret Detection, Google Text Moderations](./proxy/enterprise#content-moderation)
+ - ✅ [Prompt Injection Detection (with LakeraAI API)](./proxy/enterprise#prompt-injection-detection---lakeraai)
- ✅ Reject calls from Blocked User list
- ✅ Reject calls (incoming / outgoing) with Banned Keywords (e.g. competitors)
- **Custom Branding**
- - ✅ [Custom Branding + Routes on Swagger Docs](#swagger-docs---custom-routes--branding)
+ - ✅ [Custom Branding + Routes on Swagger Docs](./proxy/enterprise#swagger-docs---custom-routes--branding)
- ✅ [Public Model Hub](../docs/proxy/enterprise.md#public-model-hub)
- ✅ [Custom Email Branding](../docs/proxy/email.md#customizing-email-branding)
- ✅ **Feature Prioritization**
diff --git a/docs/my-website/docs/proxy/cost_tracking.md b/docs/my-website/docs/proxy/cost_tracking.md
index 3ccf8f383a..fe3a462508 100644
--- a/docs/my-website/docs/proxy/cost_tracking.md
+++ b/docs/my-website/docs/proxy/cost_tracking.md
@@ -117,6 +117,8 @@ That's IT. Now Verify your spend was tracked
+Expect to see `x-litellm-response-cost` in the response headers with calculated cost
+
@@ -145,16 +147,16 @@ Navigate to the Usage Tab on the LiteLLM UI (found on https://your-proxy-endpoin
-## API Endpoints to get Spend
-#### Getting Spend Reports - To Charge Other Teams, Customers
-
-Use the `/global/spend/report` endpoint to get daily spend report per
-- team
-- customer [this is `user` passed to `/chat/completions` request](#how-to-track-spend-with-litellm)
-
+## ✨ (Enterprise) API Endpoints to get Spend
+#### Getting Spend Reports - To Charge Other Teams, Customers
+
+Use the `/global/spend/report` endpoint to get daily spend report per
+- Team
+- Customer [this is `user` passed to `/chat/completions` request](#how-to-track-spend-with-litellm)
+- [LiteLLM API key](virtual_keys.md)
@@ -337,6 +339,61 @@ curl -X GET 'http://localhost:4000/global/spend/report?start_date=2024-04-01&end
```
+
+
+
+
+
+👉 Key Change: Specify `group_by=api_key`
+
+
+```shell
+curl -X GET 'http://localhost:4000/global/spend/report?start_date=2024-04-01&end_date=2024-06-30&group_by=api_key' \
+ -H 'Authorization: Bearer sk-1234'
+```
+
+##### Example Response
+
+
+```shell
+[
+ {
+ "api_key": "ad64768847d05d978d62f623d872bff0f9616cc14b9c1e651c84d14fe3b9f539",
+ "total_cost": 0.0002157,
+ "total_input_tokens": 45.0,
+ "total_output_tokens": 1375.0,
+ "model_details": [
+ {
+ "model": "gpt-3.5-turbo",
+ "total_cost": 0.0001095,
+ "total_input_tokens": 9,
+ "total_output_tokens": 70
+ },
+ {
+ "model": "llama3-8b-8192",
+ "total_cost": 0.0001062,
+ "total_input_tokens": 36,
+ "total_output_tokens": 1305
+ }
+ ]
+ },
+ {
+ "api_key": "88dc28d0f030c55ed4ab77ed8faf098196cb1c05df778539800c9f1243fe6b4b",
+ "total_cost": 0.00012924,
+ "total_input_tokens": 36.0,
+ "total_output_tokens": 1593.0,
+ "model_details": [
+ {
+ "model": "llama3-8b-8192",
+ "total_cost": 0.00012924,
+ "total_input_tokens": 36,
+ "total_output_tokens": 1593
+ }
+ ]
+ }
+]
+```
+
diff --git a/docs/my-website/docs/proxy/enterprise.md b/docs/my-website/docs/proxy/enterprise.md
index d580f58b6b..e061a917e2 100644
--- a/docs/my-website/docs/proxy/enterprise.md
+++ b/docs/my-website/docs/proxy/enterprise.md
@@ -22,6 +22,7 @@ Features:
- ✅ [Enforce Required Params for LLM Requests (ex. Reject requests missing ["metadata"]["generation_name"])](#enforce-required-params-for-llm-requests)
- **Spend Tracking**
- ✅ [Tracking Spend for Custom Tags](#tracking-spend-for-custom-tags)
+ - ✅ [API Endpoints to get Spend Reports per Team, API Key, Customer](cost_tracking.md#✨-enterprise-api-endpoints-to-get-spend)
- **Guardrails, PII Masking, Content Moderation**
- ✅ [Content Moderation with LLM Guard, LlamaGuard, Secret Detection, Google Text Moderations](#content-moderation)
- ✅ [Prompt Injection Detection (with LakeraAI API)](#prompt-injection-detection---lakeraai)
diff --git a/docs/my-website/img/response_cost_img.png b/docs/my-website/img/response_cost_img.png
index 9f466b3fbb..2fa9c20095 100644
Binary files a/docs/my-website/img/response_cost_img.png and b/docs/my-website/img/response_cost_img.png differ
diff --git a/litellm/proxy/_types.py b/litellm/proxy/_types.py
index 640c7695a0..1f1aaf0eea 100644
--- a/litellm/proxy/_types.py
+++ b/litellm/proxy/_types.py
@@ -1622,7 +1622,7 @@ class ProxyException(Exception):
}
-class CommonProxyErrors(enum.Enum):
+class CommonProxyErrors(str, enum.Enum):
db_not_connected_error = "DB not connected"
no_llm_router = "No models configured on proxy"
not_allowed_access = "Admin-only endpoint. Not allowed to access this."
diff --git a/litellm/proxy/spend_tracking/spend_management_endpoints.py b/litellm/proxy/spend_tracking/spend_management_endpoints.py
index 1fbd95b3cf..11406b162f 100644
--- a/litellm/proxy/spend_tracking/spend_management_endpoints.py
+++ b/litellm/proxy/spend_tracking/spend_management_endpoints.py
@@ -817,9 +817,9 @@ async def get_global_spend_report(
default=None,
description="Time till which to view spend",
),
- group_by: Optional[Literal["team", "customer"]] = fastapi.Query(
+ group_by: Optional[Literal["team", "customer", "api_key"]] = fastapi.Query(
default="team",
- description="Group spend by internal team or customer",
+ description="Group spend by internal team or customer or api_key",
),
):
"""
@@ -860,7 +860,7 @@ async def get_global_spend_report(
start_date_obj = datetime.strptime(start_date, "%Y-%m-%d")
end_date_obj = datetime.strptime(end_date, "%Y-%m-%d")
- from litellm.proxy.proxy_server import prisma_client
+ from litellm.proxy.proxy_server import premium_user, prisma_client
try:
if prisma_client is None:
@@ -868,6 +868,11 @@ async def get_global_spend_report(
f"Database not connected. Connect a database to your proxy - https://docs.litellm.ai/docs/simple_proxy#managing-auth---virtual-keys"
)
+ if premium_user is not True:
+ raise ValueError(
+ "/spend/report endpoint" + CommonProxyErrors.not_premium_user.value
+ )
+
if group_by == "team":
# first get data from spend logs -> SpendByModelApiKey
# then read data from "SpendByModelApiKey" to format the response obj
@@ -992,6 +997,48 @@ async def get_global_spend_report(
return []
return db_response
+ elif group_by == "api_key":
+ sql_query = """
+ WITH SpendByModelApiKey AS (
+ SELECT
+ sl.api_key,
+ sl.model,
+ SUM(sl.spend) AS model_cost,
+ SUM(sl.prompt_tokens) AS model_input_tokens,
+ SUM(sl.completion_tokens) AS model_output_tokens
+ FROM
+ "LiteLLM_SpendLogs" sl
+ WHERE
+ sl."startTime" BETWEEN $1::date AND $2::date
+ GROUP BY
+ sl.api_key,
+ sl.model
+ )
+ SELECT
+ api_key,
+ SUM(model_cost) AS total_cost,
+ SUM(model_input_tokens) AS total_input_tokens,
+ SUM(model_output_tokens) AS total_output_tokens,
+ jsonb_agg(jsonb_build_object(
+ 'model', model,
+ 'total_cost', model_cost,
+ 'total_input_tokens', model_input_tokens,
+ 'total_output_tokens', model_output_tokens
+ )) AS model_details
+ FROM
+ SpendByModelApiKey
+ GROUP BY
+ api_key
+ ORDER BY
+ total_cost DESC;
+ """
+ db_response = await prisma_client.db.query_raw(
+ sql_query, start_date_obj, end_date_obj
+ )
+ if db_response is None:
+ return []
+
+ return db_response
except Exception as e:
raise HTTPException(