From canva-pack
Optimizes Canva Connect API costs via caching, export tracking, and pagination to cut unnecessary calls and manage rate limits.
How this skill is triggered — by the user, by Claude, or both
Slash command
/canva-pack:canva-cost-tuningThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
Optimize Canva Connect API usage. While the Connect API itself is free to call, rate limits constrain throughput. Canva Enterprise (required for autofill) has per-seat licensing costs. Optimize by reducing unnecessary calls, caching effectively, and batching operations.
Optimize Canva Connect API usage. While the Connect API itself is free to call, rate limits constrain throughput. Canva Enterprise (required for autofill) has per-seat licensing costs. Optimize by reducing unnecessary calls, caching effectively, and batching operations.
| Tier | Cost | Connect API Access | Autofill API | Brand Templates |
|---|---|---|---|---|
| Canva Free | $0/user | Yes | No | No |
| Canva Pro | $15/user/mo | Yes | No | No |
| Canva Teams | $10/user/mo (5+) | Yes | No | Limited |
| Canva Enterprise | Custom | Yes | Yes | Yes |
Key insight: The REST API is free — costs come from Canva subscriptions. Autofill and brand template APIs require Enterprise.
// Design metadata rarely changes — cache aggressively
// Save: ~100 GET /designs/{id} calls/min per user
const designMetadata = await cachedCanvaCall(
`design:${designId}`,
() => canvaAPI(`/designs/${designId}`, token),
300 // 5 min TTL
);
// Track exported designs to prevent duplicate exports
class ExportTracker {
private exportedDesigns = new Map<string, { urls: string[]; expiresAt: number }>();
async exportIfNeeded(designId: string, format: object, token: string): Promise<string[]> {
const cached = this.exportedDesigns.get(designId);
// Export URLs valid for 24 hours — reuse if still valid
if (cached && Date.now() < cached.expiresAt) {
return cached.urls;
}
const { job } = await canvaAPI('/exports', token, {
method: 'POST',
body: JSON.stringify({ design_id: designId, format }),
});
const urls = await pollExport(job.id, token);
this.exportedDesigns.set(designId, {
urls,
expiresAt: Date.now() + 23 * 60 * 60 * 1000, // 23 hours (1h buffer)
});
return urls;
}
}
// Stop listing when you find what you need
async function findDesignByTitle(title: string, token: string): Promise<any | null> {
let continuation: string | undefined;
do {
const params = new URLSearchParams({
query: title, // Use server-side search instead of client filtering
limit: '25',
...(continuation && { continuation }),
});
const data = await canvaAPI(`/designs?${params}`, token);
const match = data.items.find((d: any) => d.title === title);
if (match) return match; // Early exit — don't fetch remaining pages
continuation = data.continuation;
} while (continuation);
return null;
}
class CanvaUsageTracker {
private calls: Map<string, number> = new Map();
track(endpoint: string): void {
const key = `${new Date().toISOString().slice(0, 13)}:${endpoint}`; // Hourly bucket
this.calls.set(key, (this.calls.get(key) || 0) + 1);
}
report(): { endpoint: string; callsPerHour: number }[] {
const hourly: Record<string, number> = {};
for (const [key, count] of this.calls) {
const endpoint = key.split(':').slice(1).join(':');
hourly[endpoint] = (hourly[endpoint] || 0) + count;
}
return Object.entries(hourly)
.map(([endpoint, callsPerHour]) => ({ endpoint, callsPerHour }))
.sort((a, b) => b.callsPerHour - a.callsPerHour);
}
}
query parameter for server-side search| Issue | Cause | Solution |
|---|---|---|
| Rate limits hit frequently | Too many calls | Add caching layer |
| Export quota exceeded | Duplicate exports | Track and reuse URLs |
| Autofill not available | Not Enterprise tier | Upgrade Canva plan |
| Slow list queries | No search filter | Use query parameter |
For architecture patterns, see canva-reference-architecture.
npx claudepluginhub jeremylongshore/claude-code-plugins-plus-skills --plugin canva-packOptimizes Canva Connect REST API performance with LRU/Redis caching, continuation pagination, and async polling in TypeScript for faster integrations.
Automates Canva operations: list/browse designs, create from brand templates/assets, upload assets via Rube MCP Composio toolkit. Requires active Canva connection.
Automates Canva design operations via Rube MCP and Composio: list designs, create from scratch or templates, upload assets, manage folders, brand templates, and autofill.