Claude-code-plugins-plus-skills miro-cost-tuning
install
source · Clone the upstream repo
git clone https://github.com/jeremylongshore/claude-code-plugins-plus-skills
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/jeremylongshore/claude-code-plugins-plus-skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/plugins/saas-packs/miro-pack/skills/miro-cost-tuning" ~/.claude/skills/jeremylongshore-claude-code-plugins-plus-skills-miro-cost-tuning && rm -rf "$T"
manifest:
plugins/saas-packs/miro-pack/skills/miro-cost-tuning/SKILL.mdsource content
Miro Cost Tuning
Overview
Miro's API pricing is based on your plan tier (Free, Business, Enterprise), not per-API-call billing. However, the credit-based rate limiting system (100,000 credits/minute) effectively caps throughput. Cost optimization means minimizing API calls to stay within your plan's rate limits and reduce the need for higher-tier upgrades.
Miro Plan Comparison
| Feature | Free | Business | Enterprise |
|---|---|---|---|
| Price | $0/user | $12-20/user/mo | Custom |
| Boards | 3 editable | Unlimited | Unlimited |
| API access | Yes | Yes | Yes |
| Rate limit | 100K credits/min | 100K credits/min | Higher (negotiable) |
| OAuth scopes | All standard | All standard | All + enterprise scopes |
| SCIM provisioning | No | No | Yes |
| Audit logs API | No | No | Yes |
| SSO/SAML | No | No | Yes |
Credit Usage Tracking
class MiroUsageTracker { private minuteCredits = 0; private dailyRequests = 0; private minuteStart = Date.now(); private dailyStart = Date.now(); trackRequest(response: Response): void { // Reset minute window if (Date.now() - this.minuteStart > 60_000) { this.minuteCredits = 0; this.minuteStart = Date.now(); } // Reset daily window if (Date.now() - this.dailyStart > 86_400_000) { this.dailyRequests = 0; this.dailyStart = Date.now(); } const limit = parseInt(response.headers.get('X-RateLimit-Limit') ?? '100000'); const remaining = parseInt(response.headers.get('X-RateLimit-Remaining') ?? '100000'); this.minuteCredits = limit - remaining; this.dailyRequests++; } getReport(): UsageReport { return { currentMinuteCredits: this.minuteCredits, creditUtilizationPercent: Math.round((this.minuteCredits / 100000) * 100), dailyRequests: this.dailyRequests, projectedMonthlyRequests: this.dailyRequests * 30, recommendation: this.getRecommendation(), }; } private getRecommendation(): string { if (this.minuteCredits > 80000) { return 'CRITICAL: >80% credit usage. Reduce request rate or upgrade to Enterprise.'; } if (this.minuteCredits > 50000) { return 'WARNING: >50% credit usage. Consider caching and batching.'; } return 'Healthy credit usage.'; } }
Cost Reduction Strategies
Strategy 1: Reduce Read Requests with Caching
The biggest cost saver. Most Miro board reads return data that changes infrequently.
// EXPENSIVE: Fetching board on every page load app.get('/dashboard', async (req, res) => { const board = await miroFetch(`/v2/boards/${boardId}`); // 1 credit per load const items = await miroFetch(`/v2/boards/${boardId}/items`); // 1 credit per load res.render('dashboard', { board, items }); }); // OPTIMIZED: Cache board data for 2 minutes app.get('/dashboard', async (req, res) => { const board = await getCachedBoard(boardId); // Cache hit = 0 credits const items = await getCachedItems(boardId); // Cache hit = 0 credits res.render('dashboard', { board, items }); }); // With webhook-driven invalidation, cache hits can be 95%+ // That is a 20x reduction in API calls
Strategy 2: Filter Items by Type
Don't fetch all items if you only need sticky notes.
// EXPENSIVE: Fetch all items, filter client-side const allItems = await miroFetch(`/v2/boards/${boardId}/items?limit=50`); const notes = allItems.data.filter(i => i.type === 'sticky_note'); // OPTIMIZED: Server-side type filter const notes = await miroFetch(`/v2/boards/${boardId}/items?type=sticky_note&limit=50`); // Fewer items returned = smaller response = faster
Strategy 3: Batch Writes with Controlled Concurrency
// EXPENSIVE: Sequential writes (slow + same credits) for (const note of notes) { await createStickyNote(boardId, note); // 200ms * 50 = 10 seconds } // OPTIMIZED: Parallel with concurrency control (same credits, 5x faster) import PQueue from 'p-queue'; const queue = new PQueue({ concurrency: 5 }); for (const note of notes) { queue.add(() => createStickyNote(boardId, note)); } await queue.onIdle(); // ~2 seconds
Strategy 4: Use Webhooks Instead of Polling
// EXPENSIVE: Poll every 10 seconds (8,640 requests/day) setInterval(async () => { const items = await miroFetch(`/v2/boards/${boardId}/items`); detectChanges(items); }, 10_000); // OPTIMIZED: Webhook subscription (0 polling requests) // Miro pushes changes to your endpoint in real-time // See miro-webhooks-events for setup
Strategy 5: Smart Pagination Limits
// WASTEFUL: Small page size = more round trips let cursor; do { const page = await miroFetch(`/v2/boards/${boardId}/items?limit=10&cursor=${cursor ?? ''}`); // 10 items per page = 10 requests for 100 items } while (cursor); // OPTIMIZED: Max page size let cursor; do { const page = await miroFetch(`/v2/boards/${boardId}/items?limit=50&cursor=${cursor ?? ''}`); // 50 items per page = 2 requests for 100 items } while (cursor);
Usage Dashboard Query
If you track API calls in a database:
SELECT DATE_TRUNC('hour', created_at) AS hour, endpoint, COUNT(*) AS requests, AVG(duration_ms) AS avg_latency_ms, COUNT(*) FILTER (WHERE status = 429) AS rate_limited FROM miro_api_logs WHERE created_at >= NOW() - INTERVAL '24 hours' GROUP BY 1, 2 ORDER BY requests DESC;
Budget Alerts
// Alert when approaching credit limit const tracker = new MiroUsageTracker(); // After each API call tracker.trackRequest(response); const report = tracker.getReport(); if (report.creditUtilizationPercent > 80) { await sendSlackAlert({ channel: '#engineering-alerts', text: `Miro API credit usage at ${report.creditUtilizationPercent}%. ${report.recommendation}`, }); }
When to Upgrade to Enterprise
Consider Enterprise if you need:
- Higher rate limits (negotiated per account)
- SCIM API for automated user provisioning
- Audit logs API for compliance
- Organization-level management endpoints
- SSO/SAML integration APIs
- Dedicated support for API issues
Error Handling
| Issue | Cause | Solution |
|---|---|---|
| Hitting 429 frequently | Too many requests | Implement caching + webhooks |
| Credit spikes | Runaway polling loops | Audit all calls |
| Unnecessary full-board fetches | No type filtering | Add parameter |
| Small page sizes | Low parameter | Use (maximum) |
Resources
Next Steps
For architecture patterns, see
miro-reference-architecture.