[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6F7ZH0WWCBMNTSTKAQGE0CD":3,"skill-same:01M6F7ZH0WWCBMNTSTKAQGE0CD":78},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":23,"publishedAt":44,"linkOnly":45,"markdownContent":46,"markdownBody":47,"frontMatter":48,"refName":55,"commitSha":56,"skillDirectory":57,"forksCount":58,"repositoryHtmlUrl":59,"repositoryHomepageUrl":60,"ownerHtmlUrl":61,"ownerBlogUrl":6,"ownerDescription":62,"updatedAt":63,"install":64,"resources":77},"01M6F7ZH0WWCBMNTSTKAQGE0CD","canva-rate-limits",null,"Handle Canva Connect API rate limits with backoff, queuing, and monitoring.\nUse when hitting 429 errors, implementing retry logic,\nor optimizing API request throughput for Canva integrations.\nTrigger with phrases like \"canva rate limit\", \"canva throttling\",\n\"canva 429\", \"canva retry\", \"canva backoff\".","ComeOnOliver\u002Fskillshub","ComeOnOliver","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F43215937?v=4","01M6E5JNFECJAAVFE0W7SMWWD2","Haoran Shu","01M6E5JNNDQS7AJZDYFQ8TPZCK",67,"MIT","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fjeremylongshore\u002Fclaude-code-plugins-plus-skills\u002Fcanva-rate-limits\u002FSKILL.md",[18],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000030","开发工程师","PROFESSION","适用职业",[24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43],"canva rate limits","canva","use","429","backoff","retry","with","trigger","io","logic","design","write","read","integrations","api","throttling","saas","monitoring","code","connect","2026-11-10 10:12:30",false,"---\nname: canva-rate-limits\ndescription: |\n  Handle Canva Connect API rate limits with backoff, queuing, and monitoring.\n  Use when hitting 429 errors, implementing retry logic,\n  or optimizing API request throughput for Canva integrations.\n  Trigger with phrases like \"canva rate limit\", \"canva throttling\",\n  \"canva 429\", \"canva retry\", \"canva backoff\".\nallowed-tools: Read, Write, Edit\nversion: 1.0.0\nlicense: MIT\nauthor: Jeremy Longshore \u003Cjeremy@intentsolutions.io>\ntags: [saas, design, canva]\ncompatible-with: claude-code\n---\n\n# Canva Rate Limits\n\n## Overview\n\nThe Canva Connect API enforces per-user, per-endpoint rate limits. Each endpoint has different thresholds. A 429 response means you must wait before retrying.\n\n## Canva Connect API Rate Limits\n\n| Endpoint | Method | Limit |\n|----------|--------|-------|\n| `\u002Fv1\u002Fusers\u002Fme` | GET | 10 req\u002Fmin |\n| `\u002Fv1\u002Fusers\u002Fme\u002Fprofile` | GET | 10 req\u002Fmin |\n| `\u002Fv1\u002Fdesigns` | GET | 100 req\u002Fmin |\n| `\u002Fv1\u002Fdesigns` | POST | 20 req\u002Fmin |\n| `\u002Fv1\u002Fdesigns\u002F{id}` | GET | 100 req\u002Fmin |\n| `\u002Fv1\u002Fexports` | POST | 75 req\u002F5min, 500\u002F24hr per user |\n| `\u002Fv1\u002Fexports` (integration) | POST | 750 req\u002F5min, 5000\u002F24hr |\n| `\u002Fv1\u002Fexports` (per document) | POST | 75 req\u002F5min |\n| `\u002Fv1\u002Fasset-uploads` | POST | 30 req\u002Fmin |\n| `\u002Fv1\u002Fautofills` | POST | 60 req\u002Fmin |\n| `\u002Fv1\u002Ffolders` | POST | 20 req\u002Fmin |\n| `\u002Fv1\u002Fbrand-templates` | GET | 100 req\u002Fmin |\n\nAll limits are **per user** of your integration unless noted otherwise.\n\n## Exponential Backoff with Jitter\n\n```typescript\nasync function canvaRequestWithBackoff\u003CT>(\n  fn: () => Promise\u003CT>,\n  config = { maxRetries: 5, baseDelayMs: 1000, maxDelayMs: 60000 }\n): Promise\u003CT> {\n  for (let attempt = 0; attempt \u003C= config.maxRetries; attempt++) {\n    try {\n      return await fn();\n    } catch (error: any) {\n      if (attempt === config.maxRetries) throw error;\n\n      \u002F\u002F Only retry on 429 or 5xx\n      const status = error.status || error.response?.status;\n      if (status !== 429 && (status \u003C 500 || status >= 600)) throw error;\n\n      \u002F\u002F Honor Retry-After header if present\n      const retryAfter = error.headers?.get?.('Retry-After');\n      const delay = retryAfter\n        ? parseInt(retryAfter) * 1000\n        : Math.min(\n            config.baseDelayMs * Math.pow(2, attempt) + Math.random() * 1000,\n            config.maxDelayMs\n          );\n\n      console.warn(`Rate limited (attempt ${attempt + 1}\u002F${config.maxRetries}). Waiting ${(delay \u002F 1000).toFixed(1)}s`);\n      await new Promise(r => setTimeout(r, delay));\n    }\n  }\n  throw new Error('Unreachable');\n}\n```\n\n## Queue-Based Rate Limiting\n\n```typescript\nimport PQueue from 'p-queue';\n\n\u002F\u002F Match per-user endpoint limits\nconst canvaQueues = {\n  designs: new PQueue({ concurrency: 1, interval: 3000, intervalCap: 1 }),     \u002F\u002F ~20\u002Fmin\n  exports: new PQueue({ concurrency: 1, interval: 4000, intervalCap: 1 }),     \u002F\u002F ~15\u002Fmin (conservative)\n  assets:  new PQueue({ concurrency: 1, interval: 2000, intervalCap: 1 }),     \u002F\u002F ~30\u002Fmin\n  reads:   new PQueue({ concurrency: 3, interval: 1000, intervalCap: 3 }),     \u002F\u002F ~100\u002Fmin (shared reads)\n};\n\n\u002F\u002F Usage — automatically queued to stay under limits\nconst design = await canvaQueues.designs.add(() =>\n  client.createDesign({ design_type: { type: 'custom', width: 1080, height: 1080 }, title: 'Queued' })\n);\n\n\u002F\u002F Batch export with rate control\nconst designIds = ['DAV1', 'DAV2', 'DAV3', 'DAV4', 'DAV5'];\nconst exports = await Promise.all(\n  designIds.map(id =>\n    canvaQueues.exports.add(() =>\n      client.createExport({ design_id: id, format: { type: 'pdf' } })\n    )\n  )\n);\n```\n\n## Rate Limit Monitor\n\n```typescript\nclass CanvaRateLimitTracker {\n  private windows: Map\u003Cstring, { count: number; resetAt: number }> = new Map();\n\n  track(endpoint: string, response: Response): void {\n    const remaining = response.headers.get('X-RateLimit-Remaining');\n    const reset = response.headers.get('X-RateLimit-Reset');\n\n    if (remaining !== null) {\n      this.windows.set(endpoint, {\n        count: parseInt(remaining),\n        resetAt: reset ? parseInt(reset) * 1000 : Date.now() + 60000,\n      });\n    }\n  }\n\n  shouldThrottle(endpoint: string): boolean {\n    const window = this.windows.get(endpoint);\n    if (!window) return false;\n    return window.count \u003C 3 && Date.now() \u003C window.resetAt;\n  }\n\n  getWaitMs(endpoint: string): number {\n    const window = this.windows.get(endpoint);\n    if (!window) return 0;\n    return Math.max(0, window.resetAt - Date.now());\n  }\n\n  report(): Record\u003Cstring, { remaining: number; resetsIn: string }> {\n    const report: Record\u003Cstring, any> = {};\n    for (const [ep, w] of this.windows) {\n      report[ep] = {\n        remaining: w.count,\n        resetsIn: `${Math.max(0, (w.resetAt - Date.now()) \u002F 1000).toFixed(0)}s`,\n      };\n    }\n    return report;\n  }\n}\n```\n\n## Proactive Throttling\n\n```typescript\n\u002F\u002F Wrap the client to throttle before hitting limits\nasync function throttledCanvaRequest\u003CT>(\n  tracker: CanvaRateLimitTracker,\n  endpoint: string,\n  fn: () => Promise\u003CT>\n): Promise\u003CT> {\n  if (tracker.shouldThrottle(endpoint)) {\n    const waitMs = tracker.getWaitMs(endpoint);\n    console.log(`Proactively waiting ${waitMs}ms for ${endpoint}`);\n    await new Promise(r => setTimeout(r, waitMs));\n  }\n  return fn();\n}\n```\n\n## Error Handling\n\n| Scenario | Detection | Action |\n|----------|-----------|--------|\n| Single 429 | HTTP status | Wait `Retry-After` seconds, retry |\n| Sustained 429s | Multiple retries fail | Reduce request rate, increase backoff |\n| Export quota hit | 500\u002F24hr per user | Queue exports, spread across hours |\n| Integration quota | 5000\u002F24hr exports | Distribute across users |\n\n## Resources\n\n- [API Requests & Responses](https:\u002F\u002Fwww.canva.dev\u002Fdocs\u002Fconnect\u002Fapi-requests-responses\u002F)\n- [p-queue](https:\u002F\u002Fgithub.com\u002Fsindresorhus\u002Fp-queue)\n\n## Next Steps\n\nFor security configuration, see `canva-security-basics`.\n\n","# Canva Rate Limits\n\n## Overview\n\nThe Canva Connect API enforces per-user, per-endpoint rate limits. Each endpoint has different thresholds. A 429 response means you must wait before retrying.\n\n## Canva Connect API Rate Limits\n\n| Endpoint | Method | Limit |\n|----------|--------|-------|\n| `\u002Fv1\u002Fusers\u002Fme` | GET | 10 req\u002Fmin |\n| `\u002Fv1\u002Fusers\u002Fme\u002Fprofile` | GET | 10 req\u002Fmin |\n| `\u002Fv1\u002Fdesigns` | GET | 100 req\u002Fmin |\n| `\u002Fv1\u002Fdesigns` | POST | 20 req\u002Fmin |\n| `\u002Fv1\u002Fdesigns\u002F{id}` | GET | 100 req\u002Fmin |\n| `\u002Fv1\u002Fexports` | POST | 75 req\u002F5min, 500\u002F24hr per user |\n| `\u002Fv1\u002Fexports` (integration) | POST | 750 req\u002F5min, 5000\u002F24hr |\n| `\u002Fv1\u002Fexports` (per document) | POST | 75 req\u002F5min |\n| `\u002Fv1\u002Fasset-uploads` | POST | 30 req\u002Fmin |\n| `\u002Fv1\u002Fautofills` | POST | 60 req\u002Fmin |\n| `\u002Fv1\u002Ffolders` | POST | 20 req\u002Fmin |\n| `\u002Fv1\u002Fbrand-templates` | GET | 100 req\u002Fmin |\n\nAll limits are **per user** of your integration unless noted otherwise.\n\n## Exponential Backoff with Jitter\n\n```typescript\nasync function canvaRequestWithBackoff\u003CT>(\n  fn: () => Promise\u003CT>,\n  config = { maxRetries: 5, baseDelayMs: 1000, maxDelayMs: 60000 }\n): Promise\u003CT> {\n  for (let attempt = 0; attempt \u003C= config.maxRetries; attempt++) {\n    try {\n      return await fn();\n    } catch (error: any) {\n      if (attempt === config.maxRetries) throw error;\n\n      \u002F\u002F Only retry on 429 or 5xx\n      const status = error.status || error.response?.status;\n      if (status !== 429 && (status \u003C 500 || status >= 600)) throw error;\n\n      \u002F\u002F Honor Retry-After header if present\n      const retryAfter = error.headers?.get?.('Retry-After');\n      const delay = retryAfter\n        ? parseInt(retryAfter) * 1000\n        : Math.min(\n            config.baseDelayMs * Math.pow(2, attempt) + Math.random() * 1000,\n            config.maxDelayMs\n          );\n\n      console.warn(`Rate limited (attempt ${attempt + 1}\u002F${config.maxRetries}). Waiting ${(delay \u002F 1000).toFixed(1)}s`);\n      await new Promise(r => setTimeout(r, delay));\n    }\n  }\n  throw new Error('Unreachable');\n}\n```\n\n## Queue-Based Rate Limiting\n\n```typescript\nimport PQueue from 'p-queue';\n\n\u002F\u002F Match per-user endpoint limits\nconst canvaQueues = {\n  designs: new PQueue({ concurrency: 1, interval: 3000, intervalCap: 1 }),     \u002F\u002F ~20\u002Fmin\n  exports: new PQueue({ concurrency: 1, interval: 4000, intervalCap: 1 }),     \u002F\u002F ~15\u002Fmin (conservative)\n  assets:  new PQueue({ concurrency: 1, interval: 2000, intervalCap: 1 }),     \u002F\u002F ~30\u002Fmin\n  reads:   new PQueue({ concurrency: 3, interval: 1000, intervalCap: 3 }),     \u002F\u002F ~100\u002Fmin (shared reads)\n};\n\n\u002F\u002F Usage — automatically queued to stay under limits\nconst design = await canvaQueues.designs.add(() =>\n  client.createDesign({ design_type: { type: 'custom', width: 1080, height: 1080 }, title: 'Queued' })\n);\n\n\u002F\u002F Batch export with rate control\nconst designIds = ['DAV1', 'DAV2', 'DAV3', 'DAV4', 'DAV5'];\nconst exports = await Promise.all(\n  designIds.map(id =>\n    canvaQueues.exports.add(() =>\n      client.createExport({ design_id: id, format: { type: 'pdf' } })\n    )\n  )\n);\n```\n\n## Rate Limit Monitor\n\n```typescript\nclass CanvaRateLimitTracker {\n  private windows: Map\u003Cstring, { count: number; resetAt: number }> = new Map();\n\n  track(endpoint: string, response: Response): void {\n    const remaining = response.headers.get('X-RateLimit-Remaining');\n    const reset = response.headers.get('X-RateLimit-Reset');\n\n    if (remaining !== null) {\n      this.windows.set(endpoint, {\n        count: parseInt(remaining),\n        resetAt: reset ? parseInt(reset) * 1000 : Date.now() + 60000,\n      });\n    }\n  }\n\n  shouldThrottle(endpoint: string): boolean {\n    const window = this.windows.get(endpoint);\n    if (!window) return false;\n    return window.count \u003C 3 && Date.now() \u003C window.resetAt;\n  }\n\n  getWaitMs(endpoint: string): number {\n    const window = this.windows.get(endpoint);\n    if (!window) return 0;\n    return Math.max(0, window.resetAt - Date.now());\n  }\n\n  report(): Record\u003Cstring, { remaining: number; resetsIn: string }> {\n    const report: Record\u003Cstring, any> = {};\n    for (const [ep, w] of this.windows) {\n      report[ep] = {\n        remaining: w.count,\n        resetsIn: `${Math.max(0, (w.resetAt - Date.now()) \u002F 1000).toFixed(0)}s`,\n      };\n    }\n    return report;\n  }\n}\n```\n\n## Proactive Throttling\n\n```typescript\n\u002F\u002F Wrap the client to throttle before hitting limits\nasync function throttledCanvaRequest\u003CT>(\n  tracker: CanvaRateLimitTracker,\n  endpoint: string,\n  fn: () => Promise\u003CT>\n): Promise\u003CT> {\n  if (tracker.shouldThrottle(endpoint)) {\n    const waitMs = tracker.getWaitMs(endpoint);\n    console.log(`Proactively waiting ${waitMs}ms for ${endpoint}`);\n    await new Promise(r => setTimeout(r, waitMs));\n  }\n  return fn();\n}\n```\n\n## Error Handling\n\n| Scenario | Detection | Action |\n|----------|-----------|--------|\n| Single 429 | HTTP status | Wait `Retry-After` seconds, retry |\n| Sustained 429s | Multiple retries fail | Reduce request rate, increase backoff |\n| Export quota hit | 500\u002F24hr per user | Queue exports, spread across hours |\n| Integration quota | 5000\u002F24hr exports | Distribute across users |\n\n## Resources\n\n- [API Requests & Responses](https:\u002F\u002Fwww.canva.dev\u002Fdocs\u002Fconnect\u002Fapi-requests-responses\u002F)\n- [p-queue](https:\u002F\u002Fgithub.com\u002Fsindresorhus\u002Fp-queue)\n\n## Next Steps\n\nFor security configuration, see `canva-security-basics`.\n\n",{"name":5,"tags":49,"author":50,"license":15,"version":51,"description":52,"allowed-tools":53,"compatible-with":54},[40,34,25],"Jeremy Longshore \u003Cjeremy@intentsolutions.io>","1.0.0","Handle Canva Connect API rate limits with backoff, queuing, and monitoring.\nUse when hitting 429 errors, implementing retry logic,\nor optimizing API request throughput for Canva integrations.\nTrigger with phrases like \"canva rate limit\", \"canva throttling\",\n\"canva 429\", \"canva retry\", \"canva backoff\".\n","Read, Write, Edit","claude-code","main","def8531e65114c0fca8fb8551c1871ee0eed705c","skills\u002Fjeremylongshore\u002Fclaude-code-plugins-plus-skills\u002Fcanva-rate-limits",24,"https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub","https:\u002F\u002Fskillshub.wtf","https:\u002F\u002Fgithub.com\u002FComeOnOliver","AI & Blockchain Engineer\r\n","2026-06-24 22:07:19",{"source":8,"skillName":5,"commands":65,"prompt":75,"zipAvailable":76},[66,69,72],{"tool":67,"command":68},"npx","npx skills add ComeOnOliver\u002Fskillshub --skill canva-rate-limits",{"tool":70,"command":71},"bunx","bunx skills add ComeOnOliver\u002Fskillshub --skill canva-rate-limits",{"tool":73,"command":74},"pnpm","pnpm dlx skills add ComeOnOliver\u002Fskillshub --skill canva-rate-limits","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fjeremylongshore\u002Fclaude-code-plugins-plus-skills\u002Fcanva-rate-limits\u002FSKILL.md",true,[],[79,93,103,112,118,125],{"skillNo":80,"name":81,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":82,"categories":83,"keywords":89,"publishedAt":44},"01M6FKJN13GMG4KY57J3QD8GY5","model-hierarchy-skill","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzscole\u002Fmodel-hierarchy-skill\u002Fmodel-hierarchy-skill\u002FSKILL.md",[84],{"categoryNo":85,"categoryName":86,"taxonomyType":87,"taxonomyTypeDesc":88},"01JBSEEDCAT000000000000023","Skill与智能体开发","SCENARIO","使用场景",[90,91,92],"model","model hierarchy","model hierarchy skill",{"skillNo":94,"name":95,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":96,"categories":97,"keywords":99,"publishedAt":44},"01M6FKJJJ6GCAMR4PPPRD12M5X","zoom-mcp","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fzoom-mcp\u002FSKILL.md",[98],{"categoryNo":85,"categoryName":86,"taxonomyType":87,"taxonomyTypeDesc":88},[100,101,102],"MCP","zoom","zoom mcp",{"skillNo":104,"name":105,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":106,"categories":107,"keywords":108,"publishedAt":44},"01M6FKJE3KYT3KPEZMEEHYKA6K","zoom-apps-sdk","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fzoom-apps-sdk\u002FSKILL.md",[],[109,110,111,101],"sdk","zoom apps sdk","apps",{"skillNo":113,"name":114,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":115,"categories":116,"keywords":117,"publishedAt":44},"01M6FKJ9EDH9Q5VWDF8HAJRSP0","windows","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwindows\u002FSKILL.md",[],[114],{"skillNo":119,"name":120,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":121,"categories":122,"keywords":124,"publishedAt":44},"01M6FKJ666BEEFC3AE9GJ72Q93","whiteboard","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwhiteboard\u002FSKILL.md",[123],{"categoryNo":85,"categoryName":86,"taxonomyType":87,"taxonomyTypeDesc":88},[120],{"skillNo":126,"name":127,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":128,"categories":129,"keywords":130,"publishedAt":44},"01M6FKJ21WFCDGTJQ2Q91T9DVW","websockets","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwebsockets\u002FSKILL.md",[],[127]]