Meter tokens. Enforce quotas. Invoice automatically.
12 AI Providers Β· 5 Pricing Models Β· 30+ API Endpoints Β· Python & Node SDKs
π Quick Start Β· π― Features Β· π¦ SDKs Β· π API Reference Β· βοΈ Configuration Β· π³ Deployment Β· π€ Contributing
The problem: You're building a SaaS on top of LLMs. You need to track usage per customer, enforce quotas, support free tiers and prepaid credits, and generate invoices. Building all that from scratch takes weeks.
The solution: TokenToll gives you a single API to handle all of it. Ingest usage events, define pricing in YAML, and let TokenToll compute costs, enforce limits, generate invoices, and sync to Stripe β or run fully self-hosted.
ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ
β β report β β invoice β β
β Your App ββββββββββΆ β TokenToll ββββββββββΆ β Customer β
β calls LLM β tokens β API β auto β gets bill β
β β β β β β
ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ
|
β
Event ingestion via REST or SDK β
Per-unit β |
β
Hard limits β block at cap β
Auto monthly invoice generation |
β
Full tenant isolation β
HMAC-SHA256 signed delivery |
TokenToll ships with pre-configured cost tables for 12 providers and 100+ models. Pass provider and model with your events β costs are calculated automatically.
GPT-4o Β· o1 Β· o3 |
Claude 4 Β· 3.5 Β· Haiku |
Gemini 2.5 Β· Flash |
Large Β· Codestral |
V3 Β· R1 |
Llama 3 Β· Llama 4 |
Command R+ |
LPU Inference |
Grok |
Sonar Pro |
Open Models |
Hosted OpenAI |
Tip
Rates are YAML files in config/provider_pricing/ β update them anytime and hot-reload without restart.
βββββββββββββββββββ ββββββββββββββββββββ βββββββββββββββββ
β β β β β β
β Your App ββββββββββΆβ TokenToll API ββββββββββΆβ MongoDB β
β (SDK / HTTP) β β (FastAPI) β β β
β β β β βββββββββββββββββ
βββββββββββββββββββ ββββββββββ¬ββββββββββ
β
ββββββββββΌββββββββββ
β Redis Streams β
ββββββββββ¬ββββββββββ
β
ββββββββββββββββββΌββββββββββββββββ-β
β β β
ββββββββΌβββββββ ββββββββΌβββββββ ββββββββΌβββββββ
β Ingestion β β Invoice β β Webhook β
β Worker β β Worker β β Worker β
βββββββββββββββ ββββββββ¬βββββββ βββββββββββββββ
β
βββββββββΌβββββββββ
β Stripe β
β (optional) β
ββββββββββββββββββ
| Component | Role |
|---|---|
| π API Server | FastAPI REST API β auth, rate limiting, multi-tenant routing |
| β‘ Ingestion Worker | Processes usage events, updates wallets, checks entitlements |
| π΅ Invoice Worker | Monthly invoice generation, Stripe sync, credit application |
| π¨ Webhook Worker | HMAC-signed delivery with retries to your endpoints |
| π MongoDB | Persistent storage β tenants, customers, events, invoices, audit logs |
| π΄ Redis | Stream-based queue + per-tenant rate limiting |
Note
You need Docker & Docker Compose. Stripe is optional β without it, TokenToll runs in manual invoicing mode.
git clone https://github.com/AlameerAshraf/tokentoll.git
cd tokentoll
docker compose -f docker/docker-compose.yml up -dcurl http://localhost:8000/health | jq
# β
{"status": "ok", "database": "ok"}curl -s -X POST http://localhost:8000/v1/tenants \
-H "Content-Type: application/json" \
-d '{"name": "My SaaS", "slug": "my-saas"}' | jqcurl -s -X POST http://localhost:8000/v1/tenants/{tenant_id}/api-keys \
-d '{"name": "production"}' | jq
# β οΈ Save the "key" field β shown only once!# Create customer on the "default" plan
curl -s -X POST http://localhost:8000/v1/tenants/{tenant_id}/customers \
-H "Authorization: Bearer tt_your_key" \
-H "Content-Type: application/json" \
-d '{"external_id": "user_123", "plan_id": "default", "name": "Alice"}' | jq
# Report usage
curl -s -X POST http://localhost:8000/v1/events \
-H "Authorization: Bearer tt_your_key" \
-H "Content-Type: application/json" \
-d '{
"event_id": "evt_001",
"customer_id": "user_123",
"meter_name": "token_count",
"quantity": 1500,
"provider": "openai",
"model": "gpt-4o"
}' | jq
# β
{"accepted": 1, "rejected": 0, "errors": []}curl -s http://localhost:8000/v1/tenants/{tenant_id}/customers/{customer_id}/balance \
-H "Authorization: Bearer tt_your_key" | jqTip
For the full 14-phase walkthrough covering every feature with curl, see docs/full-scenario-guide.md.
The SDKs are not published to package registries. Build and use them locally from your macine or PUBLISH THEM IF YOU WANT ππ» πΎ.
Python SDK
# From repo root β editable install (changes apply immediately)
pip install -e ./packages/sdk-python
# or with uv:
uv pip install -e ./packages/sdk-pythonNode.js SDK
# 1. Build the package (from repo root)
cd packages/sdk-node && npm run build
# 2. In your app, install from the local path
# If your app is inside tokentoll (e.g. apps/dashboard):
npm install ../../packages/sdk-node
# If your app is elsewhere, use an absolute or relative path:
npm install /path/to/tokentoll/packages/sdk-node
# Or add to your package.json dependencies:
# "@tokentoll/sdk-node": "file:../path/to/tokentoll/packages/sdk-node"
from tokentoll import TokenToll
client = TokenToll(
api_key="tt_your_key",
base_url="http://localhost:8000",
tenant_id="your_tenant_id"
)
# πΎ Emit usage β one line
client.emit("user_123", "token_count", 1500)
# Check balance
balance = client.get_balance("user_123")
# Gate requests on quota
result = client.check_entitlement(
"user_123", "token_count", 1000
)
if result["allowed"]:
# serve the request
... |
import { TokenToll } from '@tokentoll/sdk-node';
const client = new TokenToll({
apiKey: 'tt_your_key',
baseUrl: 'http://localhost:8000',
tenantId: 'your_tenant_id',
});
// πΎ Emit usage β one line
await client.emit('user_123', 'token_count', 1500);
// Check balance
const balance = await client.getBalance('user_123');
// Gate requests on quota
const { allowed, remaining } =
await client.checkEntitlement(
'user_123', 'token_count', 1000
); |
Environment variables (both SDKs): TOKENTOLL_API_KEY Β· TOKENTOLL_BASE_URL Β· TOKENTOLL_TENANT_ID
Note
πΎ Interactive Swagger UI is available at http://localhost:8000/docs when running locally.
| Method | Path | Auth | Description | |
|---|---|---|---|---|
| π€ | POST |
/v1/events |
Tenant | Ingest usage events (single or batch) |
| π’ | POST |
/v1/tenants |
Operator | Create a tenant |
| π’ | GET |
/v1/tenants |
Any | List tenants |
| π’ | PATCH |
/v1/tenants/{id} |
Any | Update tenant |
| π’ | DELETE |
/v1/tenants/{id} |
Any | Delete tenant (cascade) |
| π€ | POST |
/v1/tenants/{id}/customers |
Tenant | Create a customer |
| π€ | GET |
/v1/tenants/{id}/customers |
Tenant | List customers |
| π€ | PATCH |
/v1/tenants/{id}/customers/{id} |
Tenant | Update customer / change plan |
| π€ | DELETE |
/v1/tenants/{id}/customers/{id} |
Tenant | Delete customer |
| π° | GET |
.../customers/{id}/balance |
Tenant | Get usage & credit balance |
| π° | POST |
.../customers/{id}/credits |
Operator | Add prepaid credits |
| π | POST |
.../customers/{id}/check-entitlement |
Tenant | Check quota before serving |
| π | PUT |
.../customers/{id}/budget |
Tenant | Set budget cap & alert thresholds |
| π | GET |
.../customers/{id}/budget |
Tenant | Get budget |
| π | DELETE |
.../customers/{id}/budget |
Tenant | Remove budget |
| π§Ύ | GET |
.../customers/{id}/invoices |
Tenant | List invoices |
| π§Ύ | GET |
/v1/tenants/{id}/invoices/{id} |
Tenant | Get single invoice |
| π | POST |
/v1/tenants/{id}/api-keys |
Any | Create API key |
| π | GET |
/v1/tenants/{id}/api-keys |
Any | List API keys |
| π | DELETE |
/v1/tenants/{id}/api-keys/{id} |
Any | Revoke API key |
| π | POST |
/v1/tenants/{id}/webhooks |
Tenant | Register webhook |
| π | GET |
/v1/tenants/{id}/webhooks |
Tenant | List webhooks |
| π | PATCH |
/v1/tenants/{id}/webhooks/{id} |
Tenant | Update webhook |
| π | DELETE |
/v1/tenants/{id}/webhooks/{id} |
Tenant | Delete webhook |
| π | GET |
/v1/tenants/{id}/plans |
Tenant | List plans for tenant |
| Method | Path | Description | |
|---|---|---|---|
| βοΈ | GET |
/v1/config/plans |
List all plan configs |
| βοΈ | GET |
/v1/config/plans/{id} |
Get plan config detail |
| βοΈ | PUT |
/v1/config/plans/{id} |
Create or update plan |
| βοΈ | DELETE |
/v1/config/plans/{id} |
Delete plan |
| π | POST |
/v1/config/reload |
Hot-reload config from YAML |
| π | GET |
/v1/analytics |
Platform-wide aggregate stats |
| βͺ | POST |
/v1/tenants/{id}/replay |
Replay events to rebuild usage |
| π€ | GET |
/v1/providers |
List available provider pricing |
Authorization: Bearer tt_... β Recommended
X-API-Key: tt_... β Alternative header
?api_key=tt_... β Query parameter
# config/plans/pro.yaml
name: pro
pricing_model: tiered
provider: openai # Use built-in provider rates
meters:
token_count:
tiers:
- up_to: 1_000_000 # First 1M tokens
rate: 0.0001
- up_to: 10_000_000 # 1M β 10M tokens
rate: 0.00008
- up_to: null # 10M+ tokens
rate: 0.00005
entitlements:
token_count:
hard_limit: 50_000_000 # π Block at 50M
soft_limit: 40_000_000 # β οΈ Alert at 40M
free_tier:
token_count: 100_000 # π First 100K free| Model | Formula | Use Case |
|---|---|---|
per_unit |
quantity Γ rate |
Simple token billing β $0.001/token |
tiered |
Rate changes per bracket | Volume discounts β cheaper at scale |
volume |
Total volume picks the rate | Enterprise pricing β all units at best rate |
flat |
Fixed fee | SaaS subscriptions β $99/month |
prepaid |
Deduct from credit balance | Buy $100 in credits, use until gone |
name: token_count
type: token_count # token_count | request_count | compute_time | custom
unit: tokensprovider: openai
currency: USD
updated_at: "2025-03-01"
models:
gpt-4o:
input_per_million: 2.50
output_per_million: 10.00
gpt-4o-mini:
input_per_million: 0.15
output_per_million: 0.60curl -X POST http://localhost:8000/v1/config/reload \
-H "Authorization: Bearer $OPERATOR_KEY"
# β
{"reloaded": true, "plans_synced": 4, "meters_synced": 1}TokenToll includes integration tests covering critical billing guarantees:
tests/
βββ integration/
β βββ test_ingestion_idempotency.py β Duplicate events never double-counted
β βββ test_tenant_isolation.py β Cross-tenant access blocked (403)
βββ conftest.py β Shared fixtures
| Test | What It Proves |
|---|---|
| β Idempotency | Same event_id sent twice β usage counted only once |
| β Tenant Isolation | Tenant A cannot read Tenant B's data β returns 403 |
| β Cross-Tenant Events | Events for another tenant's customer β rejected as unknown_customer |
# Start dependencies
docker compose -f docker/docker-compose.yml up -d mongodb redis
# API integration tests
cd apps/api && uv run pytest -v
# Node SDK tests
cd packages/sdk-node && npm test
# Lint
cd apps/api && ruff check .STRIPE_SECRET_KEY=sk_live_... \
OPERATOR_API_KEY=your-secret \
docker compose -f docker/docker-compose.yml up -d| Service | Port | Description |
|---|---|---|
π api |
8000 |
FastAPI REST API |
β‘ workers |
β | Ingestion event processor |
π΅ invoice-worker |
β | Monthly invoice generator |
π¨ webhook-worker |
β | Webhook delivery with retries |
π₯οΈ dashboard |
5174 |
React operator & customer UI |
π mongodb |
27018 |
Persistent storage |
π΄ redis |
6379 |
Queue + rate limiting |
| Variable | Default | Description |
|---|---|---|
MONGODB_URI |
mongodb://localhost:27017/tokentoll |
MongoDB connection |
REDIS_URL |
redis://localhost:6379/0 |
Redis connection |
STRIPE_SECRET_KEY |
(none) | Stripe key β omit for manual invoicing |
OPERATOR_API_KEY |
(none) | Super-admin key β omit for open bootstrap |
RATE_LIMIT_REQUESTS_PER_MINUTE |
1000 |
Per-tenant rate limit |
LOG_LEVEL |
INFO |
Logging verbosity |
CONFIG_PATH |
(auto) | Path to config/ directory |
INVOICE_INTERVAL_MIN |
60 |
Invoice worker interval (minutes) |
tokentoll/
β
βββ πΎ apps/
β βββ api/ β FastAPI REST API
β β βββ src/
β β βββ main.py β App entry point & lifespan
β β βββ api/
β β β βββ routes/ β 15 route modules
β β β βββ middleware/ β Auth (API key) + Rate limiting
β β β βββ errors.py β Standardized error responses
β β βββ models/ β 12 Beanie ODM document classes
β β βββ services/ β 7 business logic modules
β β βββ adapters/ β MongoDB, Redis, Stripe, Manual
β β βββ config/ β YAML loaders + JSON schemas
β β
β βββ workers/ β Background processors
β β βββ src/workers/
β β βββ ingestion_worker.py β Usage event processing
β β βββ invoice_worker.py β Monthly billing
β β βββ webhook_worker.py β Webhook delivery
β β
β βββ dashboard/ β React + Vite + TypeScript
β βββ src/pages/ β Operator & customer views
β
βββ π¦ packages/
β βββ sdk-python/ β Python SDK (httpx)
β βββ sdk-node/ β Node.js SDK (TypeScript)
β
βββ βοΈ config/
β βββ plans/ β Pricing plan definitions
β βββ meters/ β Meter definitions
β βββ provider_pricing/ β 12 AI provider cost tables
β
βββ π³ docker/
β βββ docker-compose.yml β Full stack (7 services)
β βββ Dockerfile.api
β βββ Dockerfile.workers
β βββ Dockerfile.dashboard
β
βββ π docs/
βββ full-scenario-guide.md β Complete HTTP walkthrough (14 phases)
βββ video-script.md β Demo video script
βββ deployment.md β Production deployment
βββ testing-scenario.md β Test scenarios
We welcome contributions! TokenToll is open source and community-driven.
# 1. Fork & clone
git clone https://github.com/your-username/tokentoll.git && cd tokentoll
# 2. Start dependencies
docker compose -f docker/docker-compose.yml up -d mongodb redis
# 3. Install & run API
cd apps/api && uv sync && uvicorn src.main:app --reload --port 8000
# 4. Run workers (separate terminals)
cd apps/workers && uv sync
python -m src.workers.main ingestion
python -m src.workers.main invoice
python -m src.workers.main webhook
# 5. Build SDKs (for local use in your projects β see [SDKs](#-sdks))
cd packages/sdk-python && uv sync && pip install -e . # or: uv pip install -e .
cd packages/sdk-node && npm install && npm run build| Step | Action |
|---|---|
| 1 | π΄ Fork the repo |
| 2 | πΏ Branch β git checkout -b feat/my-feature |
| 3 | β
Test β cd apps/api && uv run pytest -v |
| 4 | π§Ή Lint β ruff check . |
| 5 | π¬ PR β open with a clear description |
MIT β use it however you want. See LICENSE for details.
π Get Started Β· π Read the Docs Β· π View API
Built with β€οΈ for the AI developer community in Egypt πͺπ¬