Saltar al contenido principal

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 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")

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:

NivelMinutos/MesPrecio por MinutoBase Mensual
Starter0 - 1,000$0.10$0
Growth1,001 - 10,000$0.08$0
Business10,001 - 100,000$0.06$0
Enterprise100,000+PersonalizadoContacta 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}")
# Usage
pdf_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}")
# 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")

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