Skip to main content

Billing and Usage Tracking

Learn how to monitor API usage, understand billing, and optimize costs for your CallCov integration.

Overview​

CallCov uses usage-based pricing where you're billed for:

  • Call analysis minutes - Total audio duration processed
  • API requests - Calls to any API endpoint (rate limited)
  • Storage - Audio files stored in your account (optional retention)

Tracking Usage​

Get Current Usage​

Monitor your usage in real-time to avoid surprises:

import requests
from datetime import datetime, timedelta
API_KEY = "your_api_key_here"
API_URL = "https://api.callcov.com/api/v1"
def get_current_usage():
"""Retrieve current billing period usage"""
headers = {"X-API-Key": API_KEY}
response = requests.get(
f"{API_URL}/billing/usage",
headers=headers
)
if response.status_code == 200:
return response.json()
else:
raise Exception(f"Error: {response.status_code} - {response.text}")
# Usage
usage = get_current_usage()
print(f"Billing Period: {usage['period']['start']} to {usage['period']['end']}")
print(f"Minutes Analyzed: {usage['metrics']['minutes_analyzed']}")
print(f"API Requests: {usage['metrics']['api_requests']}")
print(f"Storage Used: {usage['metrics']['storage_gb']} GB")

Usage Response Structure​

{
"period": {
"start": "2024-01-01T00:00:00Z",
"end": "2024-01-31T23:59:59Z",
"billing_cycle": "monthly"
},
"metrics": {
"minutes_analyzed": 1250.5,
"api_requests": 3420,
"storage_gb": 15.2
},
"limits": {
"minutes_limit": 10000,
"requests_limit": 100000,
"storage_limit_gb": 100
},
"costs": {
"minutes_cost": 125.05,
"api_cost": 0.00,
"storage_cost": 3.04,
"total": 128.09,
"currency": "USD"
}
}

Understanding Pricing Tiers​

CallCov offers tiered pricing that scales with your volume:

TierMinutes/MonthPrice per MinuteMonthly Base
Starter0 - 1,000$0.10$0
Growth1,001 - 10,000$0.08$0
Business10,001 - 100,000$0.06$0
Enterprise100,000+CustomContact sales

Calculate Estimated Costs​

Estimate costs before processing:

Python:

def estimate_analysis_cost(duration_seconds, tier_pricing=None):
"""Calculate estimated cost for analysis"""
if tier_pricing is None:
# Default pricing tiers (per minute)
tier_pricing = [
{'max_minutes': 1000, 'price_per_minute': 0.10},
{'max_minutes': 10000, 'price_per_minute': 0.08},
{'max_minutes': 100000, 'price_per_minute': 0.06},
{'max_minutes': float('inf'), 'price_per_minute': 0.05}
]

minutes = duration_seconds / 60
total_cost = 0

# Get current usage to determine tier
usage = get_current_usage()
current_minutes = usage['metrics']['minutes_analyzed']

# Calculate cost based on tier
remaining_minutes = minutes
for tier in tier_pricing:
if current_minutes >= tier['max_minutes']:
continue

# Minutes in this tier
tier_minutes = min(
remaining_minutes,
tier['max_minutes'] - current_minutes
)

tier_cost = tier_minutes * tier['price_per_minute']
total_cost += tier_cost
remaining_minutes -= tier_minutes
current_minutes += tier_minutes

if remaining_minutes <= 0:
break

return {
'minutes': minutes,
'estimated_cost': round(total_cost, 2),
'currency': 'USD'
}

# Usage
result = estimate_analysis_cost(3600) # 1 hour = 60 minutes
print(f"Processing {result['minutes']} minutes")
print(f"Estimated cost: ${result['estimated_cost']}")

Node.js:

function estimateAnalysisCost(durationSeconds, tierPricing = null) {
if (!tierPricing) {
tierPricing = [
{ max_minutes: 1000, price_per_minute: 0.10 },
{ max_minutes: 10000, price_per_minute: 0.08 },
{ max_minutes: 100000, price_per_minute: 0.06 },
{ max_minutes: Infinity, price_per_minute: 0.05 }
];
}

const minutes = durationSeconds / 60;
let totalCost = 0;

// Get current usage to determine tier
const usage = getCurrentUsage();
let currentMinutes = usage.metrics.minutes_analyzed;

// Calculate cost based on tier
let remainingMinutes = minutes;
for (const tier of tierPricing) {
if (currentMinutes >= tier.max_minutes) continue;

const tierMinutes = Math.min(
remainingMinutes,
tier.max_minutes - currentMinutes
);

const tierCost = tierMinutes * tier.price_per_minute;
totalCost += tierCost;
remainingMinutes -= tierMinutes;
currentMinutes += tierMinutes;

if (remainingMinutes <= 0) break;
}

return {
minutes: minutes,
estimated_cost: Math.round(totalCost * 100) / 100,
currency: 'USD'
};
}

// Usage
const result = estimateAnalysisCost(3600);
console.log(`Processing ${result.minutes} minutes`);
console.log(`Estimated cost: $${result.estimated_cost}`);

PHP:

<?php

function estimateAnalysisCost($durationSeconds, $tierPricing = null) {
if ($tierPricing === null) {
$tierPricing = [
['max_minutes' => 1000, 'price_per_minute' => 0.10],
['max_minutes' => 10000, 'price_per_minute' => 0.08],
['max_minutes' => 100000, 'price_per_minute' => 0.06],
['max_minutes' => INF, 'price_per_minute' => 0.05]
];
}

$minutes = $durationSeconds / 60;
$totalCost = 0;

$usage = getCurrentUsage();
$currentMinutes = $usage['metrics']['minutes_analyzed'];

$remainingMinutes = $minutes;
foreach ($tierPricing as $tier) {
if ($currentMinutes >= $tier['max_minutes']) continue;

$tierMinutes = min(
$remainingMinutes,
$tier['max_minutes'] - $currentMinutes
);

$tierCost = $tierMinutes * $tier['price_per_minute'];
$totalCost += $tierCost;
$remainingMinutes -= $tierMinutes;
$currentMinutes += $tierMinutes;

if ($remainingMinutes <= 0) break;
}

return [
'minutes' => $minutes,
'estimated_cost' => round($totalCost, 2),
'currency' => 'USD'
];
}

// Usage
$result = estimateAnalysisCost(3600);
echo "Processing " . $result['minutes'] . " minutes\n";
echo "Estimated cost: $" . $result['estimated_cost'] . "\n";

Viewing Invoices​

Get Invoice History​

Python:

def get_invoices(limit=10, starting_after=None):
"""List billing invoices"""
headers = {"X-API-Key": API_KEY}
params = {"limit": limit}
if starting_after:
params["starting_after"] = starting_after

response = requests.get(
f"{API_URL}/billing/invoices",
headers=headers,
params=params
)

return response.json()

# Usage
result = get_invoices(limit=5)
print(f"Total Invoices: {result['total_count']}")
for inv in result['data']:
print(f" {inv['period_start']} - ${inv['amount_paid']} ({inv['status']})")

Node.js:

async function getInvoices(limit = 10, startingAfter = null) {
try {
const params = { limit: limit };
if (startingAfter) {
params.starting_after = startingAfter;
}

const response = await axios.get(
`${API_URL}/billing/invoices`,
{
headers: { 'X-API-Key': API_KEY },
params: params
}
);

return response.data;
} catch (error) {
throw new Error(`Error: ${error.response.status} - ${error.response.data}`);
}
}

// Usage
getInvoices(5).then(result => {
console.log(`Total Invoices: ${result.total_count}`);
result.data.forEach(inv => {
console.log(` ${inv.period_start} - $${inv.amount_paid} (${inv.status})`);
});
});

Ruby:

def get_invoices(limit = 10, starting_after = nil)
uri = URI("#{API_URL}/billing/invoices")
params = { limit: limit }
params[:starting_after] = starting_after if starting_after
uri.query = URI.encode_www_form(params)

request = Net::HTTP::Get.new(uri)
request['X-API-Key'] = API_KEY

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(request)
end

JSON.parse(response.body)
end

# Usage
result = get_invoices(limit: 5)
puts "Total Invoices: #{result['total_count']}"
result['data'].each do |inv|
puts " #{inv['period_start']} - $#{inv['amount_paid']} (#{inv['status']})"
end

Download Invoice PDF​

Get a downloadable PDF for any invoice:

def download_invoice_pdf(invoice_id, output_path):
"""Download invoice as PDF"""
headers = {"X-API-Key": API_KEY}
response = requests.get(
f"{API_URL}/billing/invoices/{invoice_id}/pdf",
headers=headers,
stream=True
)
if response.status_code == 200:
with open(output_path, 'wb') as f:
for chunk in response.iter_content(chunk_size=8192):
f.write(chunk)
return output_path
else:
raise Exception(f"Error: {response.status_code} - {response.text}")
# Usage
pdf_path = download_invoice_pdf("inv_abc123", "./invoice_january.pdf")
print(f"Invoice saved to: {pdf_path}")

Usage Alerts and Monitoring​

Set Up Usage Alerts​

Configure alerts to notify you when approaching limits:

def create_usage_alert(metric, threshold, notification_email):
"""Create usage alert"""
headers = {"X-API-Key": API_KEY}
data = {
"metric": metric, # 'minutes', 'requests', 'storage'
"threshold": threshold, # Percentage (0-100)
"notification_email": notification_email
}
response = requests.post(
f"{API_URL}/billing/alerts",
headers=headers,
json=data
)
if response.status_code == 201:
return response.json()
else:
raise Exception(f"Error: {response.status_code} - {response.text}")
# Usage
alert = create_usage_alert(
metric='minutes',
threshold=80, # Alert at 80% of limit
notification_email='billing@yourcompany.com'
)
print(f"Alert created: {alert['id']}")
print(f"Will notify at {alert['threshold']}% of {alert['metric']} limit")

Cost Optimization Strategies​

1. Batch Processing​

Process multiple calls together to reduce overhead:

def batch_process_calls(call_urls):
"""Process multiple calls efficiently"""
# Submit all analyses
analysis_ids = []
for url in call_urls:
response = submit_analysis(url)
analysis_ids.append(response['id'])

# Use webhooks to avoid polling costs
# Each poll = 1 API request
# Webhook = 0 API requests from your side
return analysis_ids

2. Cache Results​

Avoid re-analyzing the same call:

from functools import lru_cache

@lru_cache(maxsize=1000)
def get_cached_analysis(call_hash):
"""Cache analysis results to avoid duplicates"""
# Check if call was already analyzed
# Use audio file hash as cache key
return get_analysis_results(call_hash)

3. Use Audio Compression​

Reduce storage costs by compressing audio before upload:

from pydub import AudioSegment

def compress_audio(input_path, output_path, target_bitrate="64k"):
"""Compress audio to reduce size"""
audio = AudioSegment.from_file(input_path)

# Convert to mono (reduces size by ~50%)
audio = audio.set_channels(1)

# Reduce bitrate
audio.export(
output_path,
format="mp3",
bitrate=target_bitrate
)

return output_path

4. Clean Up Old Data​

Delete stored audio files you no longer need:

def cleanup_old_analyses(days_old=90):
"""Delete analyses older than specified days"""
cutoff_date = datetime.now() - timedelta(days=days_old)

# Get old analyses
analyses = get_analyses(created_before=cutoff_date)

for analysis in analyses:
# Delete audio file to reduce storage costs
delete_analysis(analysis['id'])

return len(analyses)

Best Practices​

1. Monitor Usage Regularly​

Set up automated monitoring:

import smtplib
from email.mime.text import MIMEText

def check_usage_and_alert():
"""Daily usage check"""
usage = get_current_usage()

minutes_used = usage['metrics']['minutes_analyzed']
minutes_limit = usage['limits']['minutes_limit']
usage_percent = (minutes_used / minutes_limit) * 100

if usage_percent > 80:
send_alert_email(
subject="CallCov Usage Alert",
body=f"Usage at {usage_percent:.1f}% ({minutes_used}/{minutes_limit} minutes)"
)

2. Estimate Before Processing​

Check costs before submitting large batches:

def process_with_budget_check(call_urls, max_budget=100):
"""Only process if within budget"""
# Calculate total duration
total_duration = sum(get_audio_duration(url) for url in call_urls)

# Estimate cost
estimate = estimate_analysis_cost(total_duration)

if estimate['estimated_cost'] > max_budget:
raise Exception(
f"Estimated cost ${estimate['estimated_cost']} exceeds budget ${max_budget}"
)

# Proceed with processing
return batch_process_calls(call_urls)

3. Use Webhooks Instead of Polling​

Polling wastes API requests:

# ❌ BAD: Polling costs API requests
while True:
analysis = get_analysis(analysis_id) # 1 API request each time
if analysis['status'] == 'completed':
break
time.sleep(10)

# βœ… GOOD: Webhooks are free
submit_analysis(
audio_url=url,
webhook_url='https://yourapp.com/webhooks'
)
# Your webhook receives results when ready

4. Implement Rate Limiting​

Avoid hitting API limits:

import time
from collections import deque

class RateLimiter:
def __init__(self, max_requests, time_window):
self.max_requests = max_requests
self.time_window = time_window
self.requests = deque()

def wait_if_needed(self):
now = time.time()

# Remove old requests outside time window
while self.requests and self.requests[0] < now - self.time_window:
self.requests.popleft()

if len(self.requests) >= self.max_requests:
sleep_time = self.time_window - (now - self.requests[0])
time.sleep(sleep_time)

self.requests.append(time.time())

# Usage
limiter = RateLimiter(max_requests=100, time_window=60) # 100 req/minute

for call in calls:
limiter.wait_if_needed()
submit_analysis(call)

Billing FAQ​

Q: When am I charged? A: Charges accrue as you use the service. You're billed monthly at the end of each billing period.

Q: What counts as an API request? A: Any HTTP request to the API (GET, POST, etc.) except webhooks you receive from us.

Q: How is audio duration calculated? A: Based on actual audio length, not wall-clock processing time. A 10-minute call = 10 billable minutes, even if processing takes 2 minutes.

Q: Can I get a refund? A: Contact support@callcov.com for refund requests. We evaluate on a case-by-case basis.

Q: What happens if I exceed my limit? A: Your requests will be rate-limited. Upgrade your plan or wait for the next billing cycle.

Next Steps​