ctoken
A high-performance Python library for estimating costs of OpenAI API calls. Track your API expenses precisely with support for all OpenAI models and APIs.
Install
pip install ctoken
Features
- Accurate cost estimation for all OpenAI models
- Compatible with all OpenAI APIs (Chat Completions and Responses)
- Detailed cost breakdown (prompt, completion, cached tokens)
- Works with streaming responses
- External pricing data with no hardcoded values
- Dynamic data refresh from remote sources
- Mock response support for testing
- High-performance token counting with efficient caching
- Smart model detection for accurate pricing
- Robust error handling through unified exception type
Quick Start
from openai import OpenAI
from ctoken import ctoken
client = OpenAI(api_key="YOUR_API_KEY")
# Make your API call
response = client.chat.completions.create(
model="gpt-4",
messages=[{"role": "user", "content": "Explain quantum computing"}]
)
# Get the cost estimate and token counts
result = ctoken(response)
print(f"Tokens: {result['total_tokens']} | Cost: ${result['total_cost']}")
Real-world Examples
1. Chat Completion API
from openai import OpenAI
from ctoken import ctoken
client = OpenAI(api_key="sk-...")
resp = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": "Hi there!"}],
)
# Get token counts and cost breakdown
result = ctoken(resp)
print(f"Token counts: {result['prompt_tokens']} prompt, {result['completion_tokens']} completion")
print(f"Total cost: ${result['total_cost']}")
# Complete result:
# {'prompt_tokens' : 3,
# 'completion_tokens' : 12,
# 'total_tokens' : 15,
# 'cached_tokens' : 0,
# 'prompt_cost_uncached': 0.00000150,
# 'prompt_cost_cached' : 0.00000000,
# 'completion_cost' : 0.00000600,
# 'total_cost' : 0.00000750}
2. Responses API
from openai import OpenAI
from ctoken import ctoken
client = OpenAI(api_key="sk-...")
resp = client.responses.create(
model="gpt-4.1-mini",
input=[{"role": "user", "content": "Hi there!"}],
)
# Get detailed cost breakdown
print(ctoken(resp))
# {'prompt_cost_uncached': 0.00000120,
# 'prompt_cost_cached' : 0.00000000,
# 'completion_cost' : 0.00001920,
# 'total_cost' : 0.00002040}
3. Streaming Responses
from openai import OpenAI
from ctoken import ctoken
client = OpenAI(api_key="sk-...")
stream = client.chat.completions.create(
model="gpt-4",
messages=[{"role": "user", "content": "Write a poem about AI"}],
stream=True
)
# Collect the stream and use it
response_chunks = []
for chunk in stream:
response_chunks.append(chunk)
# Use the chunk for whatever you need
# ...
# Get cost at the end of the stream
cost = ctoken(response_chunks)
print(f"Streaming API call cost: ${cost['total_cost']}")
4. Batch Estimation
from openai import OpenAI
from ctoken import ctoken
import pandas as pd
client = OpenAI(api_key="sk-...")
responses = []
total_cost = 0
# Collect multiple responses
questions = ["What is AI?", "How does quantum computing work?", "Explain neural networks"]
for question in questions:
resp = client.chat.completions.create(
model="gpt-4",
messages=[{"role": "user", "content": question}],
)
responses.append(resp)
# Accumulate costs
cost_estimate = ctoken(resp)
total_cost += cost_estimate['total_cost']
# Create a report
df = pd.DataFrame([{
'prompt': responses[i].choices[0].message.content[:50] + "...",
'tokens': responses[i].usage.total_tokens,
'cost': ctoken(responses[i])['total_cost']
} for i in range(len(responses))])
print(f"Total batch cost: ${total_cost:.8f}")
print(df)
5. Refresh Pricing Data
from ctoken import refresh_pricing
# Force-reload the current pricing data (cache TTL is 24h)
refresh_pricing()
6. Pricing Data Sources
The library contains pricing data in a bundled Python dictionary format. It also supports fetching pricing from:
- Local file:
data/gpt_pricing_data.csvif available - Remote source: Official GitHub repository
- Bundled fallback data: A default dataset included with the package
This approach ensures pricing data remains up-to-date without requiring library updates but also provides fast access through the bundled dictionary.
7. Error Handling
from ctoken import ctoken, CostEstimateError
try:
cost = ctoken(response)
print(f"Cost: ${cost['total_cost']}")
except CostEstimateError as e:
print(f"Error estimating cost: {e}")
# Handle gracefully
API Reference
from ctoken import ctoken, estimate_cost, refresh_pricing, CostEstimateError
# Main function for cost estimation
ctoken(response) → dict[str, Any]
"""
Accepts a ChatCompletion, streamed chunks, or Response object.
Returns a dict with:
prompt_tokens : int # Number of prompt tokens
completion_tokens : int # Number of completion tokens
total_tokens : int # Total tokens used
cached_tokens : int # Number of cached tokens
prompt_cost_uncached : float # Cost of non-cached prompt tokens
prompt_cost_cached : float # Cost of cached prompt tokens (often reduced rate)
completion_cost : float # Cost of model-generated tokens
total_cost : float # Total cost of the API call
"""
# Alias for backward compatibility
estimate_cost = ctoken
# Force reload pricing data
refresh_pricing() → None
"""Force-reload the remote pricing CSV (cache TTL is 24h)."""
# Exception for errors
CostEstimateError
"""Unified exception for recoverable input, parsing, or pricing errors."""
Production Usage Tips
Cost Monitoring in Flask Application
from flask import Flask, request, jsonify
from openai import OpenAI
from ctoken import ctoken
import logging
app = Flask(__name__)
client = OpenAI(api_key="sk-...")
# Configure cost logging
cost_logger = logging.getLogger("api_costs")
handler = logging.FileHandler("api_costs.log")
handler.setFormatter(logging.Formatter('%(asctime)s - %(message)s'))
cost_logger.addHandler(handler)
cost_logger.setLevel(logging.INFO)
@app.route("/ask", methods=["POST"])
def ask_ai():
user_query = request.json.get("query", "")
user_id = request.json.get("user_id", "anonymous")
response = client.chat.completions.create(
model="gpt-4",
messages=[{"role": "user", "content": user_query}]
)
# Log the cost with user ID
result = ctoken(response)
cost_logger.info(
f"User: {user_id} | Query: {user_query[:30]}... | "
f"Tokens: {result['total_tokens']} | Cost: ${result['total_cost']}"
)
return jsonify({
"answer": response.choices[0].message.content,
"tokens": {
"prompt": result['prompt_tokens'],
"completion": result['completion_tokens'],
"total": result['total_tokens']
},
"cost": result['total_cost']
})
if __name__ == "__main__":
app.run(debug=True)
Budget Management
from openai import OpenAI
from ctoken import ctoken
import os
from datetime import datetime
client = OpenAI(api_key="sk-...")
DAILY_BUDGET = 1.0 # $1 per day
# Track daily spending
today = datetime.now().strftime("%Y-%m-%d")
spent_today = 0.0
def make_api_call(query):
global spent_today
# Check budget before making the call
if spent_today >= DAILY_BUDGET:
return {"error": "Daily budget exceeded"}
# Make the API call
response = client.chat.completions.create(
model="gpt-4",
messages=[{"role": "user", "content": query}]
)
# Calculate and add to spending
cost = ctoken(response)
spent_today += cost['total_cost']
return {
"response": response.choices[0].message.content,
"cost": cost['total_cost'],
"remaining_budget": DAILY_BUDGET - spent_today
}
Performance Improvements
This library has been optimized for performance with:
- Efficient caching of pricing data
- Improved error handling with detailed messages
- Smart model name parsing for versioned models
- Optimized token calculations using Python's Decimal for financial accuracy
- Reduced memory usage with streamlined data structures
- Enhanced attribute access for safe navigation of nested objects
License
MIT
Links
Project Structure
The project is organized as follows:
ctoken/
├── ctoken/ # Main package
│ ├── __init__.py # Package initialization
│ ├── core.py # Core functionality
│ ├── estimate.py # Cost estimation
│ ├── parser.py # Token parsing
│ ├── pricing.py # Pricing data handling
│ └── data/ # Package data files
│ └── gpt_pricing_data.csv # Pricing data
├── data/ # External data files
│ └── openai_text_tokens_pricing.csv # Latest scraped pricing
├── examples/ # Example code
│ ├── basic_usage.py # Basic token counting
│ ├── cost_estimation.py # Cost estimation examples
│ ├── demo_ctoken_usage.py # Complete usage demo
│ └── demo_cost_estimation.py # Detailed cost estimations
├── scripts/ # Utility scripts
│ ├── openai_pricing_scraper.py # Scraper for OpenAI pricing
│ └── verify_pricing_data.py # Verify pricing data
├── tests/ # Test suite
│ ├── __init__.py
│ ├── test_api_cost_estimation.py
│ ├── test_ctoken.py
│ └── test_model_pricing.py
├── .gitignore
├── MANIFEST.in
├── pyproject.toml
├── pytest.ini
├── README.md
├── requirements-dev.txt
└── setup.py
Directory Overview
- ctoken/: Main package source code
- data/: External data files including scraped pricing information
- examples/: Example code showing how to use the package
- scripts/: Utility scripts for maintenance tasks
- tests/: Test suite for the package
Metadata
Release files for ctoken 1.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| ctoken-1.1.1.tar.gz | 21.2 kB | With provenance | Details for ctoken-1.1.1.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| ctoken-1.1.1-py3-none-any.whl | Python 3 | none | any | Details for ctoken-1.1.1-py3-none-any.whl |
Total release size: 37.1 kB
Release files / ctoken-1.1.1.tar.gz
| Download URL | ctoken-1.1.1.tar.gz |
|---|---|
| Size | 21.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
99fead665fd5a38e6fa893da7605dd7020de5637b7eee43b428a156be8982c62
|
|
BLAKE2b-256 checksum How to use checksums |
2edd27ee6deb58874957618da7424790d1322822a30aa50b09e14f7cd2f21296
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.12.8
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 24, 2025.
Transparency logRelease files / ctoken-1.1.1-py3-none-any.whl
| Download URL | ctoken-1.1.1-py3-none-any.whl |
|---|---|
| Size | 15.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
506e10cebf10f78cfd73b059c59633bbf4f97e8178679450fb4d3dc1effca2dc
|
|
BLAKE2b-256 checksum How to use checksums |
0fe3f6b9bc5b4cfd3978d394493af5778f1fd6d953502cee1575c3a777b5e2da
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.12.8
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 24, 2025.
Transparency logSecurity for ctoken 1.1.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for ctoken-1.1.1.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 24, 2025.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.