
Cost Accrual Tracker
- 115 installs
- 178 repo stars
- Updated July 14, 2026
- erichowens/some_claude_skills
Track accruing infrastructure and vendor costs over billing periods, allocate spend to teams or features, and surface burn trends before invoices close or budgets overrun.
About
Implements cost accrual tracking patterns for erichowens/some_claude_skills operated services: tag-based attribution, period-close estimates, budget alerts, and reconciliation workflows so SaaS and API spend stays visible to engineering and finance during live operations.
- Maps usage metrics to accrual schedules
- Splits shared infra costs by team or feature tags
- Flags month-to-date burn versus budget caps
- Reconciles estimates with finalized invoices
- Exports rollup views for finance review
Cost Accrual Tracker by the numbers
- 115 all-time installs (skills.sh)
- Ranked #513 of 1,106 Finance & Trading skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/erichowens/some_claude_skills --skill cost-accrual-trackerAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 115 |
|---|---|
| repo stars | ★ 178 |
| Last updated | July 14, 2026 |
| Repository | erichowens/some_claude_skills ↗ |
What it does
Track accruing infrastructure and vendor costs over billing periods, allocate spend to teams or features, and surface burn trends before invoices close or budgets overrun.
Files
Cost Accrual Tracker
Real-time tracking of API costs during LLM execution with support for partial costs on abort.
When to Use
✅ Use for:
- Implementing real-time cost tracking during execution
- Capturing partial costs when executions are aborted
- Building cost display widgets for execution UIs
- Integrating token counting into execution pipelines
- Adding budget thresholds with auto-stop
❌ NOT for:
- Cost estimation before execution (use pricing calculators)
- Billing system design (use billing-system skill)
- Price tier management or discounts
- Historical cost analytics dashboards
Core Patterns
1. Token-Based Cost Calculation
interface TokenUsage {
inputTokens: number;
outputTokens: number;
cacheReadTokens?: number; // Prompt caching hits
cacheWriteTokens?: number; // Prompt caching misses
}
interface CostCalculation {
inputCostUsd: number;
outputCostUsd: number;
cacheSavingsUsd?: number;
totalCostUsd: number;
}
function calculateCost(usage: TokenUsage, model: string): CostCalculation {
const pricing = MODEL_PRICING[model];
const inputCostUsd = (usage.inputTokens / 1_000_000) * pricing.inputPerMTok;
const outputCostUsd = (usage.outputTokens / 1_000_000) * pricing.outputPerMTok;
return {
inputCostUsd,
outputCostUsd,
totalCostUsd: inputCostUsd + outputCostUsd,
};
}2. Incremental Accrual Pattern
Track costs as they accrue, not just at completion:
class CostAccrualTracker {
private totalInputTokens = 0;
private totalOutputTokens = 0;
private accruedCostUsd = 0;
private readonly model: string;
constructor(model: string) {
this.model = model;
}
/**
* Called after each API response (streaming or complete)
*/
recordUsage(usage: TokenUsage): void {
this.totalInputTokens += usage.inputTokens;
this.totalOutputTokens += usage.outputTokens;
const cost = calculateCost(usage, this.model);
this.accruedCostUsd += cost.totalCostUsd;
}
/**
* Get current accrued cost (for real-time display)
*/
getCurrentCost(): number {
return this.accruedCostUsd;
}
/**
* Finalize on completion or abort
*/
finalize(reason: 'completed' | 'aborted' | 'failed'): CostReport {
return {
totalInputTokens: this.totalInputTokens,
totalOutputTokens: this.totalOutputTokens,
totalCostUsd: this.accruedCostUsd,
completionReason: reason,
finalizedAt: Date.now(),
};
}
}3. Abort-Aware Cost Capture
Critical: Always capture partial costs on abort:
// In execution handler
const tracker = new CostAccrualTracker(model);
try {
for await (const chunk of executeStream(request)) {
if (abortSignal.aborted) {
// CRITICAL: Capture cost BEFORE throwing
const partialCost = tracker.finalize('aborted');
onCostUpdate(partialCost);
throw new AbortError('Execution aborted');
}
tracker.recordUsage(chunk.usage);
onCostUpdate(tracker.getCurrentCost());
}
return tracker.finalize('completed');
} catch (error) {
if (error instanceof AbortError) {
throw error; // Already handled
}
return tracker.finalize('failed');
}4. Budget Threshold Pattern
Auto-stop execution when budget is exceeded:
interface BudgetConfig {
maxCostUsd: number;
warnAtPercentage: number; // e.g., 0.8 for 80%
onWarn?: (current: number, max: number) => void;
onExceed?: (current: number, max: number) => void;
}
function createBudgetGuard(config: BudgetConfig) {
return {
check(currentCostUsd: number): 'ok' | 'warn' | 'exceed' {
const percentage = currentCostUsd / config.maxCostUsd;
if (percentage >= 1.0) {
config.onExceed?.(currentCostUsd, config.maxCostUsd);
return 'exceed';
}
if (percentage >= config.warnAtPercentage) {
config.onWarn?.(currentCostUsd, config.maxCostUsd);
return 'warn';
}
return 'ok';
}
};
}Anti-Patterns
Lost Costs on Abort
Novice thinking: "Just throw an error when aborted"
Reality: If you don't capture costs before aborting, you lose:
- Token usage data for partial execution
- Accurate cost reporting for billing
- Audit trail for debugging
Timeline: Always been an issue, but became critical with expensive models (GPT-4, Claude Opus)
Correct approach: Always call finalize() with partial data BEFORE throwing abort errors.
Polling Without Debounce
Novice thinking: "Poll cost endpoint every 100ms for real-time updates"
Reality:
- Wastes bandwidth and CPU
- Cost updates only happen after API responses
- Polling faster than response rate is pointless
Correct approach: Poll at 1-2 second intervals, or use event-driven updates from the execution stream.
Ignoring Prompt Caching
Novice thinking: "Just multiply tokens by price per token"
Reality: Claude's prompt caching changes the cost model:
- Cache reads are 90% cheaper
- Cache writes cost extra on first use
- Ignoring caching leads to inaccurate costs
Timeline:
- Pre-2024: No caching, simple calculation
- 2024+: Claude prompt caching requires separate tracking
Correct approach: Track cache_read_input_tokens and cache_creation_input_tokens separately.
Per-Request Cost Objects
Novice thinking: "Create new tracker for each request"
Reality: For DAG execution with multiple nodes:
- Need aggregate cost across all nodes
- Need to attribute costs to specific nodes
- Need rollup for parent execution
Correct approach: Hierarchical tracking - per-node trackers that roll up to execution-level.
State Flow
┌─────────────────────────────────────────┐
│ CostAccrualTracker │
└─────────────────────────────────────────┘
│
┌─────────────────────────┼─────────────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ recordUsage() │ │ getCurrentCost()│ │ finalize() │
│ │ │ │ │ │
│ After each API │ │ For real-time │ │ On completion, │
│ response │ │ display │ │ abort, or fail │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │ │
│ │ │
▼ ▼ ▼
┌─────────────────────────────────────────────────────────────────┐
│ CostReport │
│ { inputTokens, outputTokens, totalCostUsd, completionReason } │
└─────────────────────────────────────────────────────────────────┘UI Display Pattern
For real-time cost display in execution UIs:
// Poll every 2 seconds while executing
useEffect(() => {
if (status !== 'running') return;
const interval = setInterval(async () => {
const response = await fetch(`/api/execute/${executionId}`);
const data = await response.json();
setAccruedCost(data.cost.accruedUsd);
setTokens({
input: data.cost.inputTokens,
output: data.cost.outputTokens,
});
}, 2000);
return () => clearInterval(interval);
}, [executionId, status]);
// Display format
<div className="cost-display">
<span className="cost-amount">${accruedCost.toFixed(4)}</span>
<span className="token-count">
{tokens.input.toLocaleString()} in / {tokens.output.toLocaleString()} out
</span>
</div>Integration Points
| Component | Responsibility |
|---|---|
CostAccrualTracker | Per-execution token counting and cost calculation |
ExecutionManager | Aggregates costs across DAG executions |
BudgetGuard | Threshold monitoring and auto-stop |
/api/execute/:id | Exposes current cost via polling |
| Cost Display Widget | Real-time UI rendering |
References
See /references/claude-api-pricing.md for current Claude API pricing.
Claude API Pricing Reference
Current Pricing (January 2026)
Claude 3.5 Sonnet (claude-3-5-sonnet-20241022)
| Token Type | Price per Million Tokens |
|---|---|
| Input | $3.00 |
| Output | $15.00 |
| Prompt Cache Write | $3.75 |
| Prompt Cache Read | $0.30 |
Claude 3.5 Haiku (claude-3-5-haiku-20241022)
| Token Type | Price per Million Tokens |
|---|---|
| Input | $1.00 |
| Output | $5.00 |
| Prompt Cache Write | $1.25 |
| Prompt Cache Read | $0.10 |
Claude 3 Opus (claude-3-opus-20240229)
| Token Type | Price per Million Tokens |
|---|---|
| Input | $15.00 |
| Output | $75.00 |
| Prompt Cache Write | $18.75 |
| Prompt Cache Read | $1.50 |
Claude Opus 4.5 (claude-opus-4-5-20251101)
| Token Type | Price per Million Tokens |
|---|---|
| Input | $15.00 |
| Output | $75.00 |
| Prompt Cache Write | $18.75 |
| Prompt Cache Read | $1.50 |
TypeScript Pricing Constants
export const MODEL_PRICING: Record<string, {
inputPerMTok: number;
outputPerMTok: number;
cacheWritePerMTok?: number;
cacheReadPerMTok?: number;
}> = {
'claude-3-5-sonnet-20241022': {
inputPerMTok: 3.00,
outputPerMTok: 15.00,
cacheWritePerMTok: 3.75,
cacheReadPerMTok: 0.30,
},
'claude-3-5-haiku-20241022': {
inputPerMTok: 1.00,
outputPerMTok: 5.00,
cacheWritePerMTok: 1.25,
cacheReadPerMTok: 0.10,
},
'claude-3-opus-20240229': {
inputPerMTok: 15.00,
outputPerMTok: 75.00,
cacheWritePerMTok: 18.75,
cacheReadPerMTok: 1.50,
},
'claude-opus-4-5-20251101': {
inputPerMTok: 15.00,
outputPerMTok: 75.00,
cacheWritePerMTok: 18.75,
cacheReadPerMTok: 1.50,
},
// Aliases
'sonnet': {
inputPerMTok: 3.00,
outputPerMTok: 15.00,
},
'haiku': {
inputPerMTok: 1.00,
outputPerMTok: 5.00,
},
'opus': {
inputPerMTok: 15.00,
outputPerMTok: 75.00,
},
};Cost Calculation Example
function calculateCostUsd(
inputTokens: number,
outputTokens: number,
model: string = 'sonnet'
): number {
const pricing = MODEL_PRICING[model];
if (!pricing) {
throw new Error(`Unknown model: ${model}`);
}
const inputCost = (inputTokens / 1_000_000) * pricing.inputPerMTok;
const outputCost = (outputTokens / 1_000_000) * pricing.outputPerMTok;
return inputCost + outputCost;
}
// Example usage
const cost = calculateCostUsd(50000, 2000, 'sonnet');
// Input: 50k tokens × $3.00/MTok = $0.15
// Output: 2k tokens × $15.00/MTok = $0.03
// Total: $0.18Prompt Caching Impact
Prompt caching can reduce costs by up to 90% for repeated prompts:
function calculateCostWithCaching(
inputTokens: number,
outputTokens: number,
cacheReadTokens: number,
cacheWriteTokens: number,
model: string
): { totalCost: number; savings: number } {
const pricing = MODEL_PRICING[model];
// Regular input (non-cached portion)
const regularInputCost = (inputTokens / 1_000_000) * pricing.inputPerMTok;
// Cache read (90% discount)
const cacheReadCost = (cacheReadTokens / 1_000_000) * (pricing.cacheReadPerMTok || pricing.inputPerMTok * 0.1);
// Cache write (25% premium)
const cacheWriteCost = (cacheWriteTokens / 1_000_000) * (pricing.cacheWritePerMTok || pricing.inputPerMTok * 1.25);
// Output
const outputCost = (outputTokens / 1_000_000) * pricing.outputPerMTok;
const totalCost = regularInputCost + cacheReadCost + cacheWriteCost + outputCost;
// Calculate savings vs. no caching
const noCacheCost = ((inputTokens + cacheReadTokens + cacheWriteTokens) / 1_000_000) * pricing.inputPerMTok + outputCost;
const savings = noCacheCost - totalCost;
return { totalCost, savings };
}API Response Format
The Claude API returns token usage in the response:
{
"id": "msg_01XFDUDYJgAACzvnptvVoYEL",
"type": "message",
"role": "assistant",
"content": [...],
"model": "claude-3-5-sonnet-20241022",
"stop_reason": "end_turn",
"usage": {
"input_tokens": 2095,
"output_tokens": 503,
"cache_creation_input_tokens": 0,
"cache_read_input_tokens": 1500
}
}Batch API Pricing
The Batch API offers 50% discount on all models:
| Model | Batch Input (per MTok) | Batch Output (per MTok) |
|---|---|---|
| Sonnet | $1.50 | $7.50 |
| Haiku | $0.50 | $2.50 |
| Opus | $7.50 | $37.50 |
Use the Batch API for non-time-sensitive workloads to significantly reduce costs.
Rate Limits
| Tier | Requests/min | Input tokens/min | Output tokens/min |
|---|---|---|---|
| Free | 5 | 20,000 | 4,000 |
| Tier 1 | 50 | 40,000 | 8,000 |
| Tier 2 | 1,000 | 80,000 | 16,000 |
| Tier 3 | 2,000 | 160,000 | 32,000 |
| Tier 4 | 4,000 | 400,000 | 80,000 |
Notes
- Prices are in USD
- Minimum charge is 1 token
- Token counts include special tokens added by the API
- Check https://www.anthropic.com/pricing for the latest prices
- This reference current as of January 2026