Skip to main content

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
Discount Scope

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:

optimized-analysis.ts
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​

  1. Submit feedback - 50% savings on every report
  2. Reuse processed media - Multiple reports from one file
  3. Use entity IDs - Skip re-identification for known speakers
  4. Lock important files - Prevent re-upload costs
  5. Monitor usage - Track credits daily

❌ Never do​

  1. Re-upload the same file multiple times
  2. Skip feedback submission
  3. Delete and re-upload files
  4. Generate reports before processing completes
  5. 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​