Speak Cost Tuning
Overview
Optimize Speak costs through smart tier selection, efficient lesson design, and usage monitoring.
Prerequisites
- Access to Speak billing dashboard
- Understanding of current usage patterns
- Database for usage tracking (optional)
- Alerting system configured (optional)
Pricing Model
Subscription Tiers
| Tier |
Monthly Cost |
Lessons/mo |
Audio Min/mo |
Users |
| Free |
$0 |
50 |
30 |
1 |
| Personal |
$29 |
500 |
300 |
1 |
| Team |
$199 |
5,000 |
2,000 |
10 |
| Business |
$499 |
20,000 |
10,000 |
50 |
| Enterprise |
Custom |
Unlimited |
Unlimited |
Unlimited |
Usage-Based Pricing (Overages)
| Resource |
Unit |
Overage Cost |
| Lesson Sessions |
per session |
$0.05 |
| Audio Recognition |
per minute |
$0.02 |
| Pronunciation Scoring |
per evaluation |
$0.01 |
| AI Tutor Interactions |
per exchange |
$0.02 |
Cost Estimation
interface UsageEstimate {
lessonsPerMonth: number;
audioMinutesPerMonth: number;
tier: string;
baseCost: number;
overageCost: number;
totalCost: number;
recommendation?: string;
}
function estimateSpeakCost(
lessonsPerMonth: number,
audioMinutesPerMonth: number,
currentTier: 'free' | 'personal' | 'team' | 'business' = 'personal'
): UsageEstimate {
const tiers = {
free: { cost: 0, lessons: 50, audio: 30 },
personal: { cost: 29, lessons: 500, audio: 300 },
team: { cost: 199, lessons: 5000, audio: 2000 },
business: { cost: 499, lessons: 20000, audio: 10000 },
};
const tier = tiers[currentTier];
const baseCost = tier.cost;
// Calculate overages
const lessonOverage = Math.max(0, lessonsPerMonth - tier.lessons);
const audioOverage = Math.max(0, audioMinutesPerMonth - tier.audio);
const overageCost =
lessonOverage * 0.05 +
audioOverage * 0.02;
const totalCost = baseCost + overageCost;
// Recommend upgrade if overage is high
let recommendation: string | undefined;
if (overageCost > baseCost * 0.5) {
const nextTier = getNextTier(currentTier);
if (nextTier) {
const nextTierCost = estimateSpeakCost(
lessonsPerMonth,
audioMinutesPerMonth,
nextTier
);
if (nextTierCost.totalCost < totalCost) {
recommendation = `Consider upgrading to ${nextTier} tier to save $${(totalCost - nextTierCost.totalCost).toFixed(2)}/month`;
}
}
}
return {
lessonsPerMonth,
audioMinutesPerMonth,
tier: currentTier,
baseCost,
overageCost,
totalCost,
recommendation,
};
}
Usage Monitoring
class SpeakUsageMonitor {
private lessonCount = 0;
private audioMinutes = 0;
private pronunciationScores = 0;
private monthStart: Date;
private alertThreshold: number;
constructor(monthlyBudget: number) {
this.alertThreshold = monthlyBudget * 0.8; // 80% warning
this.monthStart = new Date();
this.monthStart.setDate(1);
}
trackLesson(duration: number, audioMinutes: number, scores: number): void {
this.lessonCount++;
this.audioMinutes += audioMinutes;
this.pronunciationScores += scores;
const currentCost = this.estimatedCost();
if (currentCost > this.alertThreshold) {
this.sendAlert(`Approaching Speak budget: $${currentCost.toFixed(2)}`);
}
}
estimatedCost(): number {
// Base tier cost prorated + overages
const lessonCost = Math.max(0, this.lessonCount - 500) * 0.05;
const audioCost = Math.max(0, this.audioMinutes - 300) * 0.02;
const scoreCost = Math.max(0, this.pronunciationScores - 1000) * 0.01;
return 29 + lessonCost + audioCost + scoreCost; // Assuming personal tier
}
getUsageReport(): UsageReport {
return {
lessons: this.lessonCount,
audioMinutes: this.audioMinutes,
pronunciationScores: this.pronunciationScores,
estimatedCost: this.estimatedCost(),
period: {
start: this.monthStart,
end: new Date(),
},
};
}
private sendAlert(message: string): void {
// Send to Slack, email, PagerDuty, etc.
console.warn('[SPEAK BUDGET ALERT]', message);
}
}
Cost Reduction Strategies
Strategy 1: Efficient Lesson Design
// Reduce unnecessary API calls by batching
async function efficientLesson(
session: LessonSession,
exchanges: number
): Promise<void> {
// Pre-fetch all prompts at once
const prompts = await session.getPromptsBatch(exchanges);
// Process in sequence but with prepared data
for (const prompt of prompts) {
displayPrompt(prompt);
const response = await getUserResponse();
// Submit when ready
}
}
Strategy 2: Client-Side Audio Pre-processing
// Reduce audio minutes billed by trimming silence
async function optimizedAudioSubmit(
session: LessonSession,
rawAudio: ArrayBuffer
): Promise<Feedback> {
// Trim silence locally (free)
const trimmed = await trimSilence(rawAudio);
// Only upload meaningful audio (billed)
const durationReduction = 1 - (trimmed.byteLength / rawAudio.byteLength);
console.log(`Saved ${(durationReduction * 100).toFixed(0)}% audio cost`);
return session.submitAudio(trimmed);
}
Strategy 3: Caching Vocabulary Lookups
// Cache vocabulary to reduce API calls
const vocabularyCache = new Map<string, VocabularyEntry>();
async function cachedVocabularyLookup(
word: string,
language: string
): Promise<VocabularyEntry> {
const key = `${language}:${word.toLowerCase()}`;
if (vocabularyCache.has(key)) {
return vocabularyCache.get(key)!; // Free
}
const entry = await speakClient.vocabulary.lookup(word, language); // Costs
vocabularyCache.set(key, entry);
return entry;
}
Strategy 4: Pronunciation Scoring Optimization
// Only score pronunciation on final attempts
async function smartPronunciationScoring(
session: LessonSession,
phrase: string,
audioAttempts: ArrayBuffer[]
): Promise<PronunciationResult> {
// Quick local validation for early attempts (free)
for (let i = 0; i < audioAttempts.length - 1; i++) {
const basic = await localAudioValidation(audioAttempts[i]);
if (!basic.acceptable) {
return { needsRetry: true, feedback: basic.feedback };
}
}
// Only call paid API for final attempt
return session.scorePronunciation(audioAttempts[audioAttempts.length - 1], phrase);
}
Strategy 5: Off-Peak Usage
// Schedule non-urgent operations for off-peak
async function scheduleProgressSync(userId: string): Promise<void> {
const now = new Date();
const hour = now.getUTCHours();
// Off-peak: 2am-6am UTC
if (hour >= 2 && hour < 6) {
// Immediate sync
await speakClient.users.syncProgress(userId);
} else {
// Queue for off-peak
await queue.add('progress-sync', { userId }, {
delay: getDelayUntilOffPeak(),
});
}
}
Budget Alerts Configuration
// Set up billing alerts
interface BudgetAlert {
threshold: number; // Percentage of budget
channels: ('email' | 'slack' | 'pagerduty')[];
action?: 'notify' | 'throttle' | 'pause';
}
const budgetAlerts: BudgetAlert[] = [
{ threshold: 50, channels: ['email'], action: 'notify' },
{ threshold: 75, channels: ['email', 'slack'], action: 'notify' },
{ threshold: 90, channels: ['email', 'slack', 'pagerduty'], action: 'throttle' },
{ threshold: 100, channels: ['email', 'slack', 'pagerduty'], action: 'pause' },
];
async function checkBudget(usage: UsageReport): Promise<void> {
const budget = 500; // Monthly budget
const percentUsed = (usage.estimatedCost / budget) * 100;
for (const alert of budgetAlerts) {
if (percentUsed >= alert.threshold) {
await sendBudgetAlert(alert, usage, percentUsed);
if (alert.action === 'throttle') {
await enableRateLimiting();
} else if (alert.action === 'pause') {
await pauseNonEssentialFeatures();
}
}
}
}
Cost Dashboard Query
-- Track Speak usage costs by user and feature
SELECT
DATE_TRUNC('day', created_at) as date,
user_id,
feature,
COUNT(*) as operations,
SUM(audio_seconds) / 60.0 as audio_minutes,
SUM(
CASE
WHEN feature = 'lesson' THEN 0.05
WHEN feature = 'audio' THEN audio_seconds / 60.0 * 0.02
WHEN feature = 'pronunciation' THEN 0.01
ELSE 0
END
) as estimated_cost
FROM speak_usage_logs
WHERE created_at >= DATE_TRUNC('month', CURRENT_DATE)
GROUP BY 1, 2, 3
ORDER BY estimated_cost DESC;
Output
- Optimized tier selection
- Usage monitoring implemented
- Budget alerts configured
- Cost reduction strategies applied
- Efficient lesson design patterns
Error Handling
| Issue |
Cause |
Solution |
| Unexpected charges |
Untracked usage |
Implement monitoring |
| Overage fees |
Wrong tier |
Upgrade tier |
| Budget exceeded |
No alerts |
Set up alerts |
| Inefficient audio |
No preprocessing |
Add client-side optimization |
Examples
Quick Cost Check
const usage = usageMonitor.getUsageReport();
const estimate = estimateSpeakCost(usage.lessons, usage.audioMinutes, 'personal');
console.log(`Current spend: $${estimate.totalCost.toFixed(2)}`);
console.log(`Base: $${estimate.baseCost} | Overage: $${estimate.overageCost.toFixed(2)}`);
if (estimate.recommendation) {
console.log(`Recommendation: ${estimate.recommendation}`);
}
Resources
Next Steps
For architecture patterns, see speak-reference-architecture.