Facturación y Seguimiento de Uso
Aprende cómo monitorear el uso de la API, entender la facturación y optimizar costos para tu integración con CallCov.
Resumen
CallCov usa pricing basado en uso donde se te factura por:
- Minutos de análisis de llamadas - Duración total de audio procesada
- Requests API - Llamadas a cualquier endpoint de la API (con límite de rate)
- Almacenamiento - Archivos de audio almacenados en tu cuenta (retención opcional)
Rastrear Uso
Obtener Uso Actual
Monitorea tu uso en tiempo real para evitar sorpresas:
import requestsfrom 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}")
# Usageusage = 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")Estructura de Respuesta de Uso
{
"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"
}
}
Comprender los Niveles de Pricing
CallCov ofrece pricing escalonado que escala con tu volumen:
| Nivel | Minutos/Mes | Precio por Minuto | Base Mensual |
|---|---|---|---|
| Starter | 0 - 1,000 | $0.10 | $0 |
| Growth | 1,001 - 10,000 | $0.08 | $0 |
| Business | 10,001 - 100,000 | $0.06 | $0 |
| Enterprise | 100,000+ | Personalizado | Contacta a ventas |
Calcular Costos Estimados
Estima costos antes de procesar:
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;
const usage = getCurrentUsage();
let currentMinutes = usage.metrics.minutes_analyzed;
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";
Ver Facturas
Listar Todas las Facturas
Listar Todas las Facturas
Recupera tu historial de facturación programáticamente:
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
Descargar Factura en PDF
Obtén un PDF descargable para cualquier factura:
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}")
# Usagepdf_path = download_invoice_pdf("inv_abc123", "./invoice_january.pdf")print(f"Invoice saved to: {pdf_path}")Alertas y Monitoreo de Uso
Configurar Alertas de Uso
Configura alertas para notificarte cuando te acerques a los límites:
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}")
# Usagealert = 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")Estrategias de Optimización de Costos
1. Procesamiento por Lotes
Procesa múltiples llamadas juntas para reducir 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. Cachea Resultados
Evita re-analizar la misma llamada:
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. Usa Compresión de Audio
Reduce costos de almacenamiento comprimiendo el audio antes de subir:
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. Limpia Datos Antiguos
Elimina archivos de audio almacenados que ya no necesitas:
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)
Mejores Prácticas
1. Monitorea el Uso Regularmente
Configura monitoreo automatizado:
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. Estima Antes de Procesar
Verifica los costos antes de enviar lotes grandes:
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. Usa Webhooks en Lugar de Polling
El polling desperdicia requests API:
# ❌ MALO: Polling cuesta requests API
while True:
analysis = get_analysis(analysis_id) # 1 API request cada vez
if analysis['status'] == 'completed':
break
time.sleep(10)
# ✅ BUENO: Webhooks son gratis
submit_analysis(
audio_url=url,
webhook_url='https://yourapp.com/webhooks'
)
# Tu webhook recibe resultados cuando están listos
4. Implementa Rate Limiting
Evita llegar a los límites de la API:
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)
Preguntas Frecuentes sobre Facturación
P: ¿Cuándo se me cobra? R: Los cargos se acumulan a medida que usas el servicio. Se te factura mensualmente al final de cada período de facturación.
P: ¿Qué cuenta como un request API? R: Cualquier request HTTP a la API (GET, POST, etc.) excepto webhooks que recibes de nosotros.
P: ¿Cómo se calcula la duración del audio? R: Basado en la longitud real del audio, no en el tiempo de procesamiento. Una llamada de 10 minutos = 10 minutos facturables, incluso si el procesamiento toma 2 minutos.
P: ¿Puedo obtener un reembolso? R: Contacta a support@callcov.com para solicitudes de reembolso. Evaluamos caso por caso.
P: ¿Qué sucede si excedo mi límite? R: Tus requests tendrán rate limiting. Actualiza tu plan o espera al siguiente ciclo de facturación.
Próximos Pasos
- Guía de Autenticación - Configura el acceso a la API
- Enviar Análisis - Comienza a procesar llamadas
- Mejores Prácticas de Producción - Optimiza tu integración
- Manejo de Errores - Maneja errores de facturación con elegancia