| name | figma-rate-limits |
| description | Handle Figma REST API rate limits with exponential backoff and request queuing.
Use when encountering 429 errors, implementing retry logic,
or optimizing API request throughput for Figma.
Trigger with phrases like "figma rate limit", "figma throttling",
"figma 429", "figma retry", "figma backoff".
|
| allowed-tools | Read, Write, Edit |
| version | 1.6.0 |
| license | MIT |
| author | Jeremy Longshore <jeremy@intentsolutions.io> |
| tags | ["saas","figma"] |
| compatibility | Designed for Claude Code |
Figma Rate Limits
Overview
Figma uses a leaky bucket algorithm for rate limiting. When the bucket is full, the API returns 429 with a Retry-After header. Limits vary by plan tier, seat type, and endpoint tier.
Prerequisites
- Figma REST API integration working
- Understanding of async/await patterns
Instructions
Step 1: Understand the Rate Limit Model
Endpoint tiers (limits are per-user, per-minute):
| Tier | Endpoints | Typical Limit |
|---|
| Tier 1 | GET /v1/files, GET /v1/images | Higher quota |
| Tier 2 | GET /v1/files/:key/comments, GET /v1/files/:key/variables/local | Moderate quota |
| Tier 3 | GET /v1/teams/:id/components, GET /v1/teams/:id/styles | Lower quota |
429 response headers:
| Header | Type | Meaning |
|---|
Retry-After | Integer (seconds) | Wait this long before retrying |
X-Figma-Plan-Tier | String | Your Figma plan level |
X-Figma-Rate-Limit-Type | String | "low" or "high" rate limit |
X-Figma-Upgrade-Link | String | URL to upgrade for higher limits |
Step 2: Implement Exponential Backoff
async function figmaFetchWithRetry(
path: string,
token: string,
maxRetries = 5
): Promise<any> {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
const res = await fetch(`https://api.figma.com${path}`, {
headers: { 'X-Figma-Token': token },
});
if (res.status === 429) {
const retryAfter = parseInt(res.headers.get('Retry-After') || '60');
const limitType = res.headers.get('X-Figma-Rate-Limit-Type') || 'unknown';
if (attempt === maxRetries) {
throw new Error(`Rate limited after ${maxRetries} retries (${limitType})`);
}
const jitter = Math.random() * 1000;
const delay = retryAfter * 1000 + jitter;
console.();
( (r, delay));
;
}
(res. >= && attempt < maxRetries) {
delay = .( * .(, attempt), );
( (r, delay));
;
}
(!res.) {
();
}
res.();
}
}
Step 3: Request Queue with Concurrency Control
import PQueue from 'p-queue';
const figmaQueue = new PQueue({
concurrency: 3,
interval: 1000,
intervalCap: 5,
});
async function queuedFigmaRequest<T>(
path: string,
token: string
): Promise<T> {
return figmaQueue.add(() => figmaFetchWithRetry(path, token));
}
const [file, comments, images] = await Promise.all([
queuedFigmaRequest(`/v1/files/${fileKey}`, token),
queuedFigmaRequest(`/v1/files/${fileKey}/comments`, token),
queuedFigmaRequest(`/v1/images/${fileKey}?ids=0:1&format=svg`, token),
]);
Step 4: Rate Limit Monitor
class FigmaRateLimitMonitor {
private requestLog: number[] = [];
private windowMs = 60_000;
recordRequest() {
this.requestLog.push(Date.now());
const cutoff = Date.now() - this.windowMs;
this.requestLog = this.requestLog.filter(t => t > cutoff);
}
getRequestsInWindow(): number {
const cutoff = Date.now() - this.windowMs;
return this.requestLog.filter(t => t > cutoff).length;
}
shouldThrottle(safetyMargin = 0.8): boolean {
const estimatedLimit = 30;
return .() > estimatedLimit * safetyMargin;
}
}
monitor = ();
() {
(monitor.()) {
.();
( (r, ));
}
monitor.();
(path, token);
}
Step 5: Batch Node Requests
async function batchFetchNodes(
fileKey: string,
nodeIds: string[],
batchSize = 50,
token: string
) {
const results: Record<string, any> = {};
for (let i = 0; i < nodeIds.length; i += batchSize) {
const batch = nodeIds.slice(i, i + batchSize);
const ids = encodeURIComponent(batch.join(','));
const data = await queuedFigmaRequest(
`/v1/files/${fileKey}/nodes?ids=${ids}`,
token
);
Object.assign(results, data.nodes);
}
return results;
}
Output
- Automatic retry with
Retry-After header compliance
- Request queue preventing burst overload
- Rate limit monitoring with proactive throttling
- Batch operations reducing total request count
Error Handling
| Scenario | Detection | Response |
|---|
| Single 429 | Retry-After header | Wait exactly that duration |
| Repeated 429s | Multiple retries exhausted | Log, alert, back off longer |
low rate limit type | X-Figma-Rate-Limit-Type: low | Consider upgrading Figma plan |
| Batch too large | 400 Bad Request | Reduce batch size to 50 IDs |
Examples
Reproduce a 429 and read the headers that drive every pattern in this skill (Step 1):
for i in $(seq 1 60); do
curl -s -o /dev/null -D - -H "X-Figma-Token: ${FIGMA_PAT}" \
"https://api.figma.com/v1/files/${FIGMA_FILE_KEY}?depth=1" | /usr/bin/grep -iE '^(HTTP|retry-after)'
done | sort | uniq -c
54 HTTP/2 200
6 HTTP/2 429
6 retry-after: 30
Collapse N per-node calls into one batched request (Step 5) — the single biggest budget win:
curl -s -H "X-Figma-Token: ${FIGMA_PAT}" \
"https://api.figma.com/v1/files/${FIGMA_FILE_KEY}/nodes?ids=1:2,1:5,1:9,2:14" | jq '.nodes | keys'
Backoff implementation and the queue: references/implement-exponential-backoff.md, references/request-queue-with-concurrency-control.md.
Resources
Next Steps
For security configuration, see figma-security-basics.