Skip to content

How to Audit OpenAI API Usage with Token Tracking and Logs

Purpose

This post demonstrates how to audit OpenAI API usage with detailed token tracking and request logs.

My team was getting charged for OpenAI API usage, but we had no idea which application or user was consuming tokens. I logged into the OpenAI dashboard and saw:

Total Usage: $127.43 this month
API Calls: 15,234
Tokens: 2.1M
But which app caused this? No clue.

The dashboard shows aggregate numbers but nothing about per-request granularity. I needed to know:

  • Which applications are making the most calls?
  • What prompts are being sent?
  • How many tokens per request?

Environment

  • Python 3.11
  • OpenAI API (multiple applications)
  • ai-menshen v0.3.0
  • SQLite 3.39

What happened?

We have multiple applications calling the OpenAI API:

+------------------+ +------------------+
| App A | | App B |
| (Chatbot) | | (Code Gen) |
+--------+---------+ +--------+---------+
| |
v v
+-----------------------------------------------+
| OpenAI API |
| (api.openai.com) |
+-----------------------------------------------+
Each app has its own API key, but the dashboard only shows totals.

I tried using OpenAI’s usage API:

check_usage.py
import openai
# Get usage for the month
usage = openai.Usage.list(
start_date="2026-03-01",
end_date="2026-03-30"
)
print(usage)

The response:

<OpenAIObject at 0x7f8a1b2c3d40> JSON: {
"object": "list",
"data": [
{
"object": "usage",
"total_tokens": 2100000,
"n_requests": 15234
}
]
}

Just totals. No breakdown by app, no individual request details, no prompt/response bodies.

I tried another approach - adding logging to each application manually. But this meant modifying every codebase:

app_a.py
import openai
import logging
logger = logging.getLogger("openai-usage")
def chat_with_logging(prompt):
logger.info(f"Prompt: {prompt}")
response = openai.ChatCompletion.create(
model="gpt-4",
messages=[{"role": "user", "content": prompt}]
)
logger.info(f"Response: {response}")
logger.info(f"Tokens: {response.usage.total_tokens}")
return response

This approach failed because:

  1. We have 12 different applications - too many to modify
  2. Some apps use streaming responses - harder to track
  3. Each app needed its own logging setup
  4. No centralized view of all usage

How to solve it?

I found ai-menshen, a local proxy that sits between your applications and OpenAI. It logs everything automatically.

Step 1: Install and configure ai-menshen

install.sh
pip install ai-menshen
# Create config directory
mkdir -p ~/.config/ai-menshen

Create the configuration file:

~/.config/ai-menshen/config.toml
[proxy]
port = 8080
host = "127.0.0.1"
[storage]
retention_days = 90 # Auto-purge logs older than 90 days
[storage.sqlite]
path = "~/.local/share/ai-menshen/audit.db"
[logging]
log_request_body = true # Persist full request bodies
log_response_body = true # Persist full response bodies

Step 2: Point applications to the proxy

Instead of modifying each app, I just changed the base URL:

app_a.py
import openai
# Before: direct to OpenAI
# openai.api_base = "https://api.openai.com/v1"
# After: through ai-menshen proxy
openai.api_base = "http://localhost:8080/v1"
# That's it. No other changes needed.

Or set the environment variable once:

environment.sh
export OPENAI_API_BASE=http://localhost:8080/v1

Step 3: Start the proxy and view the dashboard

start_proxy.sh
ai-menshen start
# Output:
# Starting ai-menshen proxy on http://127.0.0.1:8080
# Dashboard available at http://localhost:8080
# SQLite database: ~/.local/share/ai-menshen/audit.db

Now when I open http://localhost:8080 in my browser, I see:

+----------------------------------------------------------+
| ai-menshen Dashboard |
+----------------------------------------------------------+
| Overview | Trends | Audit Logs |
+----------------------------------------------------------+
| Today's Usage: | [Graph showing | Request List |
| - Requests: 342 | daily token | - 10:23:45 |
| - Tokens: 45,678 | consumption] | gpt-4 |
| - Est. Cost: $0.91| | 1,234 tokens|
+----------------------------------------------------------+

Step 4: Query the SQLite database directly

For custom reports, I query the database:

top_requests.sh
sqlite3 ~/.local/share/ai-menshen/audit.db "
SELECT
timestamp,
model,
prompt_tokens,
completion_tokens,
total_tokens
FROM audit_logs
ORDER BY total_tokens DESC
LIMIT 10;
"

Output:

2026-03-30 10:15:23|gpt-4|8234|2100|10334
2026-03-30 09:42:11|gpt-4|7891|1856|9747
2026-03-30 11:05:33|gpt-3.5-turbo|3456|890|4346
2026-03-30 08:30:45|gpt-4|2890|1023|3913

For programmatic access:

analyze_usage.py
import sqlite3
from datetime import datetime, timedelta
conn = sqlite3.connect('~/.local/share/ai-menshen/audit.db')
cursor = conn.cursor()
# Get daily token usage for the last 7 days
cursor.execute("""
SELECT date(timestamp) as day,
SUM(total_tokens) as tokens,
COUNT(*) as requests
FROM audit_logs
WHERE timestamp > datetime('now', '-7 days')
GROUP BY day
ORDER BY day DESC
""")
print("Daily usage report:")
print("-" * 40)
for row in cursor.fetchall():
day, tokens, requests = row
print(f"{row[0]}: {tokens:,} tokens ({requests} requests)")

Output:

Daily usage report:
----------------------------------------
2026-03-30: 45,678 tokens (342 requests)
2026-03-29: 38,901 tokens (287 requests)
2026-03-28: 52,341 tokens (401 requests)
2026-03-27: 41,234 tokens (312 requests)

The reason

The key insight is that ai-menshen acts as a transparent proxy:

Before:
+--------+ +--------+
| App |------->| OpenAI |
+--------+ +--------+
|
v
[No visibility]
After:
+--------+ +------------+ +--------+
| App |----->| ai-menshen |----->| OpenAI |
+--------+ +------------+ +--------+
|
v
+--------------+
| SQLite DB |
| (Full Audit) |
+--------------+
|
v
+--------------+
| Dashboard |
+--------------+

This works because:

  1. Zero code changes: Just change the API endpoint URL
  2. Streaming support: Token counts are captured even for streaming responses
  3. Full request/response: Bodies are stored when log_request_body and log_response_body are enabled
  4. Automatic retention: Old logs are purged based on retention_days
  5. Local control: All data stays on your machine in SQLite

Common mistakes to avoid

Mistake 1: Not enabling body logging

If you skip log_request_body = true, you lose the ability to debug:

wrong config
[logging]
# Missing: log_request_body = true
# Missing: log_response_body = true

Without body logging, you can only see token counts, not the actual prompts and responses.

Mistake 2: Setting retention too short

short_retention.toml
[storage]
retention_days = 7 # Too short for compliance needs!

For billing disputes or compliance audits, you may need 90+ days of history.

The Trends tab shows patterns:

+--------------------------------------------------+
| Weekly Token Trend |
+--------------------------------------------------+
| Mon: ████████████████ 45,000 |
| Tue: ██████████ 32,000 |
| Wed: ██████████████████████████████ 78,000 <-- |
| Thu: ████████████ 36,000 |
+--------------------------------------------------+
Wednesday spike: Code generation app ran bulk jobs

Use this data for capacity planning and cost optimization.

Summary

In this post, I showed how to audit OpenAI API usage with ai-menshen’s built-in dashboard and SQLite logs. The key points are:

  1. OpenAI’s dashboard shows only aggregate usage - no per-request granularity
  2. ai-menshen acts as a transparent proxy that logs everything automatically
  3. Enable log_request_body and log_response_body for full debugging capability
  4. Set appropriate retention_days for your compliance needs
  5. Query the SQLite database directly for custom reports

The beauty of this approach is that no application code needs to be modified - just point your OpenAI client to the proxy URL.

Final Words + More Resources

My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments