Credit optimization
Master strategies to minimize credit usage while maximizing the value you get from Mappa. This guide covers proven patterns for cost-efficient behavioral analysis.
Understanding credit costsβ
For pricing details used by the public API docs, see Pricing.
Summary:
- Media processing: 1 credit per minute (charged once at upload)
- Report generation: 10 credits per report
- Feedback discount: 5 credits back per report (50% of report cost)
Key insight: Media is processed once; you can generate unlimited reports from processed files.
Top optimization strategiesβ
1. Submit feedback (50% refund on reports)β
Impact: Save 5 credits per report
// Generate report (10 credits)
const job = await mappa.reports.createJob({
media: { mediaId: "media_123" },
output: { template: "general_report" },
target: { strategy: "dominant" },
});
const report = await job.handle!.wait();
// Submit feedback (get 5 credits back on report cost)
await mappa.feedback.create({
jobId: job.jobId,
rating: "thumbs_up",
comment: "Helpful analysis",
});
// Net report cost: 5 credits instead of 10
Calculation:
Report cost: 10 credits
Feedback refund: floor(10 Γ 0.50) = 5 credits
Final report cost: 5 credits
The feedback discount applies to report generation only, not media processing costs.
Best practice: Submit feedback for every reportβsaves 5 credits each time.
2. Reuse processed mediaβ
Impact: Eliminates re-processing costs
// β Bad: Re-upload same file
const file1 = await mappa.files.upload({ file: "./interview.mp3" });
await generateReport(file1.mediaId, "candidate");
const file2 = await mappa.files.upload({ file: "./interview.mp3" }); // Duplicate!
await generateReport(file2.mediaId, "interviewer");
// β
Good: Upload once, analyze multiple times
const file = await mappa.files.upload({ file: "./interview.mp3" });
await waitForProcessing(file.mediaId);
await generateReport(file.mediaId, "candidate");
await generateReport(file.mediaId, "interviewer");
Savings:
- Processing happens once (included in first report)
- Additional reports use cached data
- No duplicate transcription or voiceprinting
3. Use entity IDs for recurring speakersβ
Impact: Skip re-identification
// First session - extract entity ID
const report1 = await mappa.reports.createJob({
media: { mediaId: "media_session1" },
output: { template: "general_report" },
target: { strategy: "magic_hint", hint: "the client" },
});
const entityId = report1.entity?.id;
await database.save({ clientId, entityId });
// Future sessions - use entity ID (more efficient)
const report2 = await mappa.reports.createJob({
media: { mediaId: "media_session2" },
output: { template: "general_report" },
target: { strategy: "entity_id", entityId: entityId },
});
4. Lock Frequently-used filesβ
Impact: Prevents accidental deletion and re-upload
// Lock reference files
await mappa.files.setRetentionLock(trainingMediaId, true);
// File never expires - no risk of deletion and re-upload
5. Batch operationsβ
Impact: Reduce API overhead
// β Bad: Sequential processing
for (const file of files) {
await processFile(file);
}
// β
Good: Parallel processing
await Promise.all(files.map(file => processFile(file)));
Cost calculation examplesβ
Scenario 1: single interview analysis (30 min)β
Media processing (30 min): 30 credits
Generate hiring report: 10 credits
Submit feedback: -5 credits (50% of report)
---
Net cost: 35 credits
Scenario 2: Multi-round interview (3 Γ 30-min rounds)β
WITHOUT OPTIMIZATION:
Round 1: Media (30) + Report (10) = 40 credits
Round 2: Media (30) + Report (10) = 40 credits
Round 3: Media (30) + Report (10) = 40 credits
Total: 120 credits
WITH OPTIMIZATION (entity ID + feedback):
Round 1: Media (30) + Report (10) - Feedback (5) = 35 credits
Round 2: Media (30) + Report (10) - Feedback (5) = 35 credits
Round 3: Media (30) + Report (10) - Feedback (5) = 35 credits
Total: 105 credits
Savings: 15 credits (12.5%)
Scenario 3: Multi-speaker analysis (30-min file, 2 speakers)β
WITHOUT OPTIMIZATION:
Media processing (30 min): 30 credits (once)
Report A: 10 credits
Report B: 10 credits
Total: 50 credits
WITH OPTIMIZATION (single file + feedback):
Media processing (30 min): 30 credits (once)
Report A: 10 - 5 (feedback) = 5 credits
Report B: 10 - 5 (feedback) = 5 credits
Total: 40 credits
Savings: 10 credits (20%)
Complete optimization exampleβ
Implement all strategies together:
import { Mappa } from "@mappa-ai/mappa-node";
const mappa = new Mappa({ apiKey: process.env.MAPPA_API_KEY! });
class OptimizedAnalyzer {
private entityCache = new Map<string, string>();
async analyzeCandidate(
candidateId: string,
mediaId: string,
round: number
) {
// 1. Check if we have entity ID (optimization: skip re-identification)
let entityId = this.entityCache.get(candidateId);
// 2. Determine target strategy
const target = entityId
? { strategy: "entity_id" as const, entityId: entityId }
: {
strategy: "magic_hint" as const,
hint: "the job candidate",
onMiss: "fallback_dominant" as const
};
// 3. Generate report
const job = await mappa.reports.createJob({
media: { mediaId },
output: {
type: round === 1 ? "json" : "markdown", // JSON for first (to get entity)
template: "hiring_report",
templateParams: {
roleTitle: "Senior Engineer",
roleDescription: "Backend development",
companyCulture: "Collaborative startup"
}
},
target,
idempotencyKey: `candidate_${candidateId}_round_${round}`, // Prevent duplicates
target: { strategy: "dominant" },
});
const report = await job.handle!.wait();
// 4. Cache entity ID from first round
if (round === 1 && report.entity?.id) {
entityId = report.subject.id;
this.entityCache.set(candidateId, entityId);
await database.candidates.update(candidateId, { mappaEntityId: entityId });
}
// 5. Submit feedback for 50% refund (optimization: always do this!)
await mappa.feedback.create({
jobId: job.jobId,
rating: "thumbs_up",
comment: `Round ${round} analysis`,
idempotencyKey: `feedback_${job.jobId}`,
});
console.info(`Round ${round}: Report cost ~5 credits (after feedback). Total includes media processing.`);
return report;
}
}
// Usage
const analyzer = new OptimizedAnalyzer();
// Process all rounds efficiently
const mediaIds = ["media_round1", "media_round2", "media_round3"];
for (let i = 0; i < mediaIds.length; i++) {
const report = await analyzer.analyzeCandidate(
"candidate_123",
mediaIds[i],
i + 1
);
console.log(`Round ${i + 1} complete`);
}
// Total cost: ~105 credits for 3 rounds (vs 120 without optimization)
// Savings come from feedback discount on report generation
Monitoring credit usageβ
Track usage per customerβ
async function getCustomerCreditUsage(customerId: string, month: string) {
const jobs = await database.jobs.findByCustomer(customerId, month);
let totalReserved = 0;
let totalCharged = 0;
let totalRefunded = 0;
for (const job of jobs) {
const usage = await mappa.credits.getUsage(job.jobId);
totalReserved += usage.reservation?.reserved_credits || 0;
totalCharged += usage.usage_meter?.credits_charged || 0;
// Check for feedback discount
const feedbackEntry = usage.ledger_entries.find(
e => e.type === "FEEDBACK_DISCOUNT"
);
if (feedbackEntry) {
totalRefunded += feedbackEntry.amount;
}
}
return {
customer: customerId,
month,
totalReserved,
totalCharged,
totalRefunded,
netCost: totalCharged - totalRefunded,
feedbackRate: jobs.length > 0 ? (totalRefunded > 0 ? 100 : 0) : 0,
};
}
// Generate monthly reports
const usage = await getCustomerCreditUsage("cust_123", "2026-01");
console.log(`Net cost: ${usage.netCost} credits`);
console.log(`Savings from feedback: ${usage.totalRefunded} credits`);
Set budget alertsβ
async function checkBudget(teamId: string, monthlyBudget: number) {
const balance = await mappa.credits.getBalance();
const usage = await getMonthlyUsage(teamId);
const projected = usage.averageDaily * 30;
if (projected > monthlyBudget) {
await alertService.send({
type: "warning",
message: `Projected usage (${projected}) exceeds budget (${monthlyBudget})`,
recommendation: "Increase feedback submission rate to save 50%",
});
}
if (balance.available < 100) {
await alertService.send({
type: "critical",
message: `Low balance: ${balance.available} credits remaining`,
});
}
}
Cost-Saving patternsβ
Pattern 1: feedback automationβ
Automatically submit feedback for successful reports:
async function generateReportWithAutoFeedback(params: any) {
const job = await mappa.reports.createJob(params);
const report = await job.handle!.wait();
// Auto-submit positive feedback for successful reports
if (report.markdown || report.sections) {
await mappa.feedback.create({
jobId: job.jobId,
rating: "thumbs_up",
comment: "Auto-generated feedback",
});
console.log("β Auto-feedback submitted (50% refund applied)");
}
return report;
}
Pattern 2: shared media poolβ
Share processed media across your organization:
class MediaPool {
async getOrUpload(fileHash: string, filePath: string): Promise<string> {
// Check if file already uploaded by team
const existing = await database.media.findByHash(fileHash);
if (existing && existing.processingStatus === "COMPLETED") {
console.log(`Using existing media: ${existing.mediaId}`);
return existing.mediaId;
}
// Upload new file
const file = await mappa.files.upload({ file: filePath });
await database.media.save({ mediaId: file.mediaId, hash: fileHash });
await waitForProcessing(file.mediaId);
return file.mediaId;
}
}
// Usage - avoid duplicate uploads across team
const pool = new MediaPool();
const mediaId = await pool.getOrUpload(fileHash, "./interview.mp4");
Pattern 3: template prioritizationβ
Use cheaper templates when appropriate:
// High-value decision: Use full hiring_report
if (candidate.stage === "final_round") {
await generateReport(mediaId, "hiring_report"); // More comprehensive
}
// Early screening: Use general_report
if (candidate.stage === "phone_screen") {
await generateReport(mediaId, "general_report"); // Faster, simpler
}
Monitoring & alertingβ
Daily usage trackingβ
async function trackDailyUsage() {
const today = new Date().toISOString().split("T")[0];
const usage = await getTodayUsage();
await database.usageMetrics.create({
date: today,
creditsCharged: usage.charged,
creditsRefunded: usage.refunded,
netCost: usage.charged - usage.refunded,
jobCount: usage.jobs,
feedbackRate: (usage.feedbackCount / usage.jobs * 100).toFixed(1),
});
console.log(`Today: ${usage.netCost} credits (${usage.feedbackRate}% feedback rate)`);
}
// Run daily via cron
Budget complianceβ
async function enforceMonthlyBudget(maxCredits: number) {
const monthUsage = await getMonthUsage();
const balance = await mappa.credits.getBalance();
if (monthUsage.netCost >= maxCredits) {
console.warn("β οΈ Monthly budget exceeded");
// Pause non-critical jobs
await jobQueue.pauseNonCritical();
// Alert team
await notifyTeam({
subject: "Mappa budget exceeded",
message: `Used ${monthUsage.netCost} of ${maxCredits} credits`,
});
}
if (balance.available < maxCredits * 0.1) {
await notifyTeam({
subject: "Low credit balance",
message: "Consider purchasing more credits",
});
}
}
Best practices summaryβ
β Always doβ
- Submit feedback - 50% savings on every report
- Reuse processed media - Multiple reports from one file
- Use entity IDs - Skip re-identification for known speakers
- Lock important files - Prevent re-upload costs
- Monitor usage - Track credits daily
β Never doβ
- Re-upload the same file multiple times
- Skip feedback submission
- Delete and re-upload files
- Generate reports before processing completes
- Ignore low balance warnings
ROI calculationβ
Example: hiring pipeline (100 candidates/month)β
Assuming 30-minute interviews:
WITHOUT optimization:
100 candidates Γ 3 rounds Γ (30 media + 10 report) = 12,000 credits/month
WITH optimization (feedback on every report):
100 candidates Γ 3 rounds Γ 40 credits = 12,000 credits
Feedback refunds (5 credits Γ 300 reports): -1,500 credits
---
Net cost: 10,500 credits/month
Savings: 1,500 credits (12.5%)
Annual savings: 1,500 Γ 12 = 18,000 credits/year
Advanced patternsβ
Pattern 1: credit reservation monitoringβ
Monitor reserved credits to forecast usage:
async function monitorReservations() {
const balance = await mappa.credits.getBalance();
console.log(`Available: ${balance.available} credits`);
console.log(`Reserved: ${balance.reserved} credits`);
if (balance.reserved > balance.available * 0.8) {
console.warn("β οΈ High reservation ratio - most credits locked");
console.warn("Consider cancelling non-critical jobs to free credits");
}
}
Pattern 2: feedback quality trackingβ
Track which reports benefit most from feedback:
async function analyzeFeedbackROI() {
const feedback = await database.feedback.getAll();
const byTemplate = groupBy(feedback, f => f.template);
for (const [template, items] of Object.entries(byTemplate)) {
const avgRefund = items.reduce((sum, f) => sum + f.discountApplied, 0) / items.length;
const totalRefunded = items.reduce((sum, f) => sum + f.discountApplied, 0);
console.log(`${template}:`);
console.log(` Feedback count: ${items.length}`);
console.log(` Avg refund: ${avgRefund.toFixed(1)} credits`);
console.log(` Total saved: ${totalRefunded} credits`);
}
}
Next stepsβ
- Feedback API - Submit feedback for refunds
- Pricing - Credit cost model
- Entity Tracking - Reuse speaker identities
- Retention Management - Optimize storage