figma-code-agent 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/README.md +133 -0
  2. package/bin/install.js +328 -0
  3. package/knowledge/README.md +62 -0
  4. package/knowledge/css-strategy.md +973 -0
  5. package/knowledge/design-to-code-assets.md +855 -0
  6. package/knowledge/design-to-code-layout.md +929 -0
  7. package/knowledge/design-to-code-semantic.md +1085 -0
  8. package/knowledge/design-to-code-typography.md +1003 -0
  9. package/knowledge/design-to-code-visual.md +1145 -0
  10. package/knowledge/design-tokens-variables.md +1261 -0
  11. package/knowledge/design-tokens.md +960 -0
  12. package/knowledge/figma-api-devmode.md +894 -0
  13. package/knowledge/figma-api-plugin.md +920 -0
  14. package/knowledge/figma-api-rest.md +742 -0
  15. package/knowledge/figma-api-variables.md +848 -0
  16. package/knowledge/figma-api-webhooks.md +876 -0
  17. package/knowledge/payload-blocks.md +1184 -0
  18. package/knowledge/payload-figma-mapping.md +1210 -0
  19. package/knowledge/payload-visual-builder.md +1004 -0
  20. package/knowledge/plugin-architecture.md +1176 -0
  21. package/knowledge/plugin-best-practices.md +1206 -0
  22. package/knowledge/plugin-codegen.md +1313 -0
  23. package/package.json +31 -0
  24. package/skills/README.md +103 -0
  25. package/skills/audit-plugin/SKILL.md +244 -0
  26. package/skills/build-codegen-plugin/SKILL.md +279 -0
  27. package/skills/build-importer/SKILL.md +320 -0
  28. package/skills/build-plugin/SKILL.md +199 -0
  29. package/skills/build-token-pipeline/SKILL.md +363 -0
  30. package/skills/ref-html/SKILL.md +290 -0
  31. package/skills/ref-layout/SKILL.md +150 -0
  32. package/skills/ref-payload-block/SKILL.md +415 -0
  33. package/skills/ref-react/SKILL.md +222 -0
  34. package/skills/ref-tokens/SKILL.md +347 -0
@@ -0,0 +1,742 @@
1
+ # Figma REST API Reference
2
+
3
+ ## Purpose
4
+
5
+ Authoritative reference for the Figma REST API covering authentication, core endpoints, node tree structure, image export, component/style retrieval, pagination, rate limits, and common pitfalls. This is the foundational API module that all other Figma knowledge modules depend on.
6
+
7
+ ## When to Use
8
+
9
+ Reference this module when you need to:
10
+
11
+ - Authenticate against the Figma API (PATs or OAuth2)
12
+ - Fetch file data, specific nodes, or metadata
13
+ - Export images or SVGs from Figma nodes
14
+ - Retrieve published library components or styles
15
+ - Parse Figma URLs to extract file keys and node IDs
16
+ - Understand rate limits and plan access controls
17
+ - Navigate the Figma node tree structure
18
+
19
+ ---
20
+
21
+ ## Content
22
+
23
+ ### Base URL
24
+
25
+ ```
26
+ https://api.figma.com
27
+ ```
28
+
29
+ Government cloud: `https://api.figma-gov.com`
30
+
31
+ All endpoints are versioned under `/v1/` (except Webhooks v2 which uses `/v2/`).
32
+
33
+ ---
34
+
35
+ ### Authentication
36
+
37
+ Figma supports two authentication methods:
38
+
39
+ #### Personal Access Tokens (PATs)
40
+
41
+ Pass the token via the `X-Figma-Token` header:
42
+
43
+ ```ts
44
+ const headers = {
45
+ 'X-Figma-Token': process.env.FIGMA_TOKEN,
46
+ };
47
+ ```
48
+
49
+ ```bash
50
+ curl -H "X-Figma-Token: YOUR_FIGMA_TOKEN" \
51
+ "https://api.figma.com/v1/files/FILE_KEY"
52
+ ```
53
+
54
+ **Creating a PAT:**
55
+
56
+ 1. Log in to Figma, open account menu (top-left) and select **Settings**
57
+ 2. Select the **Security** tab
58
+ 3. Under **Personal access tokens**, click **Generate new token**
59
+ 4. Configure expiration and scopes, then click **Generate token**
60
+ 5. Copy the token immediately (it is only shown once)
61
+
62
+ **Important:** As of 2025, PATs have a maximum expiry of 90 days. Non-expiring PATs can no longer be created.
63
+
64
+ #### OAuth 2.0
65
+
66
+ For apps acting on behalf of users:
67
+
68
+ **Authorization URL:**
69
+
70
+ ```
71
+ GET https://www.figma.com/oauth
72
+ ?client_id=:client_id
73
+ &redirect_uri=:callback
74
+ &scope=:scope
75
+ &state=:state
76
+ &response_type=code
77
+ ```
78
+
79
+ **Token exchange** (must happen within 30 seconds of authorization):
80
+
81
+ ```
82
+ POST https://api.figma.com/v1/oauth/token
83
+ ```
84
+
85
+ Uses HTTP Basic Auth with Base64-encoded `client_id:client_secret`. Returns: `access_token`, `refresh_token`, `expires_in` (90 days default), `user_id`.
86
+
87
+ **Token refresh:**
88
+
89
+ ```
90
+ POST https://api.figma.com/v1/oauth/refresh
91
+ ```
92
+
93
+ **Usage:** Include `Authorization: Bearer <TOKEN>` header.
94
+
95
+ #### Token Scopes
96
+
97
+ Scopes control which endpoints a token can access. Scopes do not override organizational permissions — users can only access files they created or were granted access to.
98
+
99
+ | Scope | Description | Notes |
100
+ |-------|-------------|-------|
101
+ | `file_content:read` | Read file contents (nodes, editor type) | Required for most file operations |
102
+ | `file_metadata:read` | Read file metadata only | |
103
+ | `file_comments:read` | Read file comments | |
104
+ | `file_comments:write` | Post/delete comments and reactions | |
105
+ | `file_dev_resources:read` | Read dev resources | |
106
+ | `file_dev_resources:write` | Write dev resources | |
107
+ | `file_variables:read` | Read variables | Enterprise only |
108
+ | `file_variables:write` | Write variables and collections | Enterprise only |
109
+ | `file_versions:read` | Read version history | |
110
+ | `library_content:read` | Read published components/styles of files | |
111
+ | `library_assets:read` | Read individual published component/style data | |
112
+ | `team_library_content:read` | Read published components/styles of teams | |
113
+ | `library_analytics:read` | Read design system analytics | Enterprise only |
114
+ | `projects:read` | List projects and files in projects | |
115
+ | `current_user:read` | Read user name, email, profile image | |
116
+ | `selections:read` | Read most recent selection in files | |
117
+ | `webhooks:read` | Read webhook metadata | |
118
+ | `webhooks:write` | Create and manage webhooks | |
119
+
120
+ > **Deprecated:** `files:read` grants broad access but is deprecated. Use specific scopes instead.
121
+
122
+ ---
123
+
124
+ ### Core Endpoints
125
+
126
+ #### GET /v1/files/:file_key
127
+
128
+ Returns the entire document tree as JSON.
129
+
130
+ **Scope required:** `file_content:read`
131
+ **Rate limit tier:** Tier 1
132
+
133
+ ```bash
134
+ curl -H "X-Figma-Token: YOUR_FIGMA_TOKEN" \
135
+ "https://api.figma.com/v1/files/FILE_KEY"
136
+ ```
137
+
138
+ **Query parameters:**
139
+
140
+ | Parameter | Type | Description |
141
+ |-----------|------|-------------|
142
+ | `version` | string | Specific version ID (omit for current) |
143
+ | `ids` | string | Comma-separated node IDs to filter returned tree |
144
+ | `depth` | number | Traversal depth (e.g., `2` = pages + top-level objects) |
145
+ | `geometry` | string | Set to `paths` to include vector path data |
146
+ | `plugin_data` | string | Set to `shared` to include shared plugin data |
147
+ | `branch_data` | boolean | Include branch metadata |
148
+
149
+ **Response shape:**
150
+
151
+ ```ts
152
+ interface GetFileResponse {
153
+ name: string;
154
+ role: string;
155
+ lastModified: string; // ISO 8601 timestamp
156
+ editorType: string; // "figma" | "figjam"
157
+ thumbnailUrl: string;
158
+ version: string;
159
+ document: DocumentNode; // Root DOCUMENT node with children
160
+ components: Record<string, ComponentMetadata>;
161
+ componentSets: Record<string, ComponentSetMetadata>;
162
+ schemaVersion: number;
163
+ styles: Record<string, StyleMetadata>;
164
+ mainFileKey?: string; // Present on branches
165
+ branches?: BranchMetadata[];
166
+ }
167
+ ```
168
+
169
+ > **Warning:** Full file fetches can return enormous payloads (tens of MB for complex files). Always use `/nodes` for targeted access when possible.
170
+
171
+ #### GET /v1/files/:file_key/nodes
172
+
173
+ Fetches specific nodes by ID. This is the preferred endpoint for targeted data retrieval.
174
+
175
+ **Scope required:** `file_content:read`
176
+ **Rate limit tier:** Tier 1
177
+
178
+ ```bash
179
+ curl -H "X-Figma-Token: YOUR_FIGMA_TOKEN" \
180
+ "https://api.figma.com/v1/files/FILE_KEY/nodes?ids=1:2,3:4"
181
+ ```
182
+
183
+ **Query parameters:**
184
+
185
+ | Parameter | Type | Description |
186
+ |-----------|------|-------------|
187
+ | `ids` | string | **Required.** Comma-separated node IDs (e.g., `1:2,3:4`) |
188
+ | `version` | string | Specific version ID |
189
+ | `depth` | number | Traversal depth below each requested node |
190
+ | `geometry` | string | Set to `paths` for vector data |
191
+ | `plugin_data` | string | Set to `shared` for shared plugin data |
192
+
193
+ **Response shape:**
194
+
195
+ ```ts
196
+ interface GetFileNodesResponse {
197
+ name: string;
198
+ lastModified: string;
199
+ version: string;
200
+ nodes: Record<string, {
201
+ document: Node;
202
+ components: Record<string, ComponentMetadata>;
203
+ schemaVersion: number;
204
+ styles: Record<string, StyleMetadata>;
205
+ } | null>; // null if node ID is invalid or unrenderable
206
+ }
207
+ ```
208
+
209
+ > **Note:** The `nodes` map may contain `null` values for non-existent or unrenderable node IDs.
210
+
211
+ #### GET /v1/files/:file_key/meta
212
+
213
+ Returns file metadata only (no document tree). Lightweight alternative when you only need file info.
214
+
215
+ **Scope required:** `file_metadata:read`
216
+ **Rate limit tier:** Tier 3
217
+
218
+ ---
219
+
220
+ ### Image Export
221
+
222
+ #### GET /v1/images/:file_key
223
+
224
+ Renders specific nodes as images and returns temporary URLs.
225
+
226
+ **Scope required:** `file_content:read`
227
+ **Rate limit tier:** Tier 1
228
+
229
+ ```bash
230
+ # Export as SVG
231
+ curl -H "X-Figma-Token: YOUR_FIGMA_TOKEN" \
232
+ "https://api.figma.com/v1/images/FILE_KEY?ids=1:2,3:4&format=svg"
233
+
234
+ # Export as PNG at 2x scale
235
+ curl -H "X-Figma-Token: YOUR_FIGMA_TOKEN" \
236
+ "https://api.figma.com/v1/images/FILE_KEY?ids=1:2&format=png&scale=2"
237
+ ```
238
+
239
+ **Query parameters:**
240
+
241
+ | Parameter | Type | Default | Description |
242
+ |-----------|------|---------|-------------|
243
+ | `ids` | string | — | **Required.** Comma-separated node IDs |
244
+ | `format` | string | `png` | `jpg`, `png`, `svg`, or `pdf` |
245
+ | `scale` | number | `1` | Scale factor (0.01 to 4) |
246
+ | `svg_include_id` | boolean | `false` | Include node IDs in SVG output |
247
+ | `svg_include_node_id` | boolean | `false` | Include `data-node-id` attribute in SVG elements |
248
+ | `svg_simplify_stroke` | boolean | `true` | Simplify inside/outside strokes to center strokes |
249
+ | `use_absolute_bounds` | boolean | `false` | Use node's full dimensions (not cropped) |
250
+ | `contents_only` | boolean | `true` | Exclude containing frame from render |
251
+
252
+ **Response shape:**
253
+
254
+ ```ts
255
+ interface GetImageResponse {
256
+ err: string | null;
257
+ images: Record<string, string | null>; // node ID → signed URL (or null on error)
258
+ }
259
+ ```
260
+
261
+ > **Important:** Image URLs expire after **30 days**. Cache the rendered images, not the URLs.
262
+
263
+ #### GET /v1/files/:file_key/images
264
+
265
+ Returns download URLs for all images present in image fills throughout the document. This is different from the rendering endpoint above — it returns the original uploaded images, not rendered node exports.
266
+
267
+ **Scope required:** `file_content:read`
268
+ **Rate limit tier:** Tier 1
269
+
270
+ **Response shape:**
271
+
272
+ ```ts
273
+ interface GetFileImagesResponse {
274
+ err: boolean;
275
+ images: Record<string, string>; // image ref → download URL
276
+ }
277
+ ```
278
+
279
+ > **Important:** These image fill URLs expire within **14 days**.
280
+
281
+ ---
282
+
283
+ ### Components and Styles (Published Library Only)
284
+
285
+ These endpoints return **published library assets only**. They do NOT return all local components or styles in a file. This is a critical distinction.
286
+
287
+ #### GET /v1/files/:file_key/components
288
+
289
+ Returns published components from the file's library.
290
+
291
+ **Scope required:** `library_content:read`
292
+ **Rate limit tier:** Tier 3
293
+
294
+ #### GET /v1/files/:file_key/component_sets
295
+
296
+ Returns published component sets from the file's library.
297
+
298
+ **Scope required:** `library_content:read`
299
+ **Rate limit tier:** Tier 3
300
+
301
+ #### GET /v1/files/:file_key/styles
302
+
303
+ Returns published styles from the file's library.
304
+
305
+ **Scope required:** `library_content:read`
306
+ **Rate limit tier:** Tier 3
307
+
308
+ **Response metadata for each asset:**
309
+
310
+ ```ts
311
+ interface PublishedAssetMetadata {
312
+ key: string;
313
+ file_key: string;
314
+ node_id: string;
315
+ thumbnail_url: string;
316
+ name: string;
317
+ description: string;
318
+ updated_at: string;
319
+ created_at: string;
320
+ user: UserInfo;
321
+ }
322
+ ```
323
+
324
+ #### Team-Level Endpoints
325
+
326
+ Paginated endpoints for browsing an entire team's published library:
327
+
328
+ ```
329
+ GET /v1/teams/:team_id/components
330
+ GET /v1/teams/:team_id/component_sets
331
+ GET /v1/teams/:team_id/styles
332
+ ```
333
+
334
+ **Scope required:** `team_library_content:read`
335
+ **Rate limit tier:** Tier 3
336
+
337
+ #### Individual Asset Lookup
338
+
339
+ ```
340
+ GET /v1/components/:key
341
+ GET /v1/component_sets/:key
342
+ GET /v1/styles/:key
343
+ ```
344
+
345
+ **Scope required:** `library_assets:read`
346
+ **Rate limit tier:** Tier 3
347
+
348
+ #### Finding ALL Components in a File (Including Unpublished)
349
+
350
+ Since the library endpoints only return published assets, to find all components in a file you must traverse the document tree:
351
+
352
+ ```ts
353
+ async function findAllComponents(fileKey: string): Promise<Node[]> {
354
+ const response = await fetch(
355
+ `https://api.figma.com/v1/files/${fileKey}`,
356
+ { headers: { 'X-Figma-Token': process.env.FIGMA_TOKEN! } }
357
+ );
358
+ const file = await response.json();
359
+
360
+ const components: Node[] = [];
361
+ function walk(node: any) {
362
+ if (node.type === 'COMPONENT' || node.type === 'COMPONENT_SET') {
363
+ components.push(node);
364
+ }
365
+ if (node.children) {
366
+ node.children.forEach(walk);
367
+ }
368
+ }
369
+ walk(file.document);
370
+ return components;
371
+ }
372
+ ```
373
+
374
+ > **Note:** It is not possible to publish from branches, so branch file keys cannot be used with library endpoints.
375
+
376
+ ---
377
+
378
+ ### Node Tree Structure
379
+
380
+ Every Figma file is a tree of nodes. The root is always a `DOCUMENT` node, whose children are `CANVAS` nodes (pages).
381
+
382
+ #### Node Type Hierarchy
383
+
384
+ ```
385
+ DOCUMENT
386
+ └── CANVAS (page)
387
+ ├── FRAME
388
+ │ ├── FRAME (nested)
389
+ │ ├── GROUP
390
+ │ ├── COMPONENT
391
+ │ ├── COMPONENT_SET
392
+ │ ├── INSTANCE
393
+ │ ├── TEXT
394
+ │ ├── RECTANGLE
395
+ │ ├── ELLIPSE
396
+ │ ├── VECTOR
397
+ │ ├── LINE
398
+ │ ├── STAR
399
+ │ ├── POLYGON
400
+ │ ├── BOOLEAN_OPERATION
401
+ │ ├── SLICE
402
+ │ └── SECTION
403
+ └── ...
404
+ ```
405
+
406
+ #### Key Node Types for Design-to-Code
407
+
408
+ | Type | Description | Has Children |
409
+ |------|-------------|:---:|
410
+ | `DOCUMENT` | Root node | Yes |
411
+ | `CANVAS` | Page | Yes |
412
+ | `FRAME` | Container with optional Auto Layout | Yes |
413
+ | `GROUP` | Visual grouping (no layout) | Yes |
414
+ | `SECTION` | Organization section on canvas | Yes |
415
+ | `COMPONENT` | Reusable component definition | Yes |
416
+ | `COMPONENT_SET` | Variant container | Yes |
417
+ | `INSTANCE` | Instance of a component | Yes |
418
+ | `TEXT` | Text layer | No |
419
+ | `RECTANGLE` | Rectangle shape | No |
420
+ | `ELLIPSE` | Ellipse/circle shape | No |
421
+ | `VECTOR` | Arbitrary vector path | No |
422
+ | `LINE` | Line | No |
423
+ | `STAR` | Star shape | No |
424
+ | `POLYGON` | Polygon shape | No |
425
+ | `BOOLEAN_OPERATION` | Union/subtract/intersect/exclude | Yes |
426
+ | `SLICE` | Export region (not rendered) | No |
427
+
428
+ #### Global Properties (All Nodes)
429
+
430
+ ```ts
431
+ interface BaseNode {
432
+ id: string; // Unique within document (e.g., "1:2")
433
+ name: string; // User-assigned name
434
+ visible: boolean; // Default: true
435
+ type: string; // Node type enum
436
+ rotation: number; // Rotation in degrees
437
+ pluginData: any; // Plugin-specific data (private)
438
+ sharedPluginData: any; // Plugin-specific data (shared)
439
+ componentPropertyReferences: Record<string, string>;
440
+ boundVariables: Record<string, VariableAlias | VariableAlias[]>;
441
+ explicitVariableModes: Record<string, string>;
442
+ }
443
+ ```
444
+
445
+ #### Layout-Related Properties (Frames, Components, Instances)
446
+
447
+ ```ts
448
+ interface LayoutNode {
449
+ // Bounding boxes
450
+ absoluteBoundingBox: Rectangle; // Position + size on canvas
451
+ absoluteRenderBounds: Rectangle; // Accounts for strokes, shadows, blur
452
+ size: Vector; // Width and height
453
+
454
+ // Auto Layout
455
+ layoutMode: 'NONE' | 'HORIZONTAL' | 'VERTICAL';
456
+ layoutWrap: 'NO_WRAP' | 'WRAP';
457
+ primaryAxisSizingMode: 'FIXED' | 'AUTO';
458
+ counterAxisSizingMode: 'FIXED' | 'AUTO';
459
+ primaryAxisAlignItems: 'MIN' | 'CENTER' | 'MAX' | 'SPACE_BETWEEN';
460
+ counterAxisAlignItems: 'MIN' | 'CENTER' | 'MAX' | 'BASELINE';
461
+ counterAxisAlignContent: 'AUTO' | 'SPACE_BETWEEN';
462
+ itemSpacing: number; // Gap between children
463
+ paddingLeft: number;
464
+ paddingRight: number;
465
+ paddingTop: number;
466
+ paddingBottom: number;
467
+
468
+ // Child sizing
469
+ layoutSizingHorizontal: 'FIXED' | 'HUG' | 'FILL';
470
+ layoutSizingVertical: 'FIXED' | 'HUG' | 'FILL';
471
+ layoutGrow: number; // 0 = fixed, 1 = fill (flex-grow)
472
+ layoutAlign: 'INHERIT' | 'STRETCH' | 'MIN' | 'CENTER' | 'MAX';
473
+
474
+ // Constraints (non-Auto-Layout frames)
475
+ constraints: {
476
+ vertical: 'TOP' | 'BOTTOM' | 'CENTER' | 'TOP_BOTTOM' | 'SCALE';
477
+ horizontal: 'LEFT' | 'RIGHT' | 'CENTER' | 'LEFT_RIGHT' | 'SCALE';
478
+ };
479
+
480
+ // Visual
481
+ fills: Paint[];
482
+ strokes: Paint[];
483
+ strokeWeight: number;
484
+ strokeAlign: 'INSIDE' | 'OUTSIDE' | 'CENTER';
485
+ cornerRadius: number;
486
+ rectangleCornerRadii: [number, number, number, number]; // TL, TR, BR, BL
487
+ effects: Effect[];
488
+ opacity: number;
489
+ blendMode: string;
490
+ clipsContent: boolean;
491
+ }
492
+ ```
493
+
494
+ #### Key Distinction: absoluteBoundingBox vs absoluteRenderBounds
495
+
496
+ - **`absoluteBoundingBox`** — The geometric bounding box of the node (position + dimensions). Does NOT account for strokes, shadows, or blur that extend beyond the shape.
497
+ - **`absoluteRenderBounds`** — The visual bounding box including all rendered effects (outside strokes, drop shadows, blur radius). Use this when determining actual visual footprint.
498
+
499
+ For design-to-code, use `absoluteBoundingBox` for sizing/positioning and `absoluteRenderBounds` when you need to ensure no visual clipping.
500
+
501
+ ---
502
+
503
+ ### Pagination
504
+
505
+ Library and team endpoints that return lists use **cursor-based pagination**.
506
+
507
+ **Response includes:**
508
+
509
+ ```ts
510
+ interface PaginatedResponse {
511
+ meta: {
512
+ cursor: {
513
+ before: number;
514
+ after: number;
515
+ };
516
+ };
517
+ // ... data
518
+ }
519
+ ```
520
+
521
+ **Query parameters:**
522
+
523
+ | Parameter | Type | Description |
524
+ |-----------|------|-------------|
525
+ | `page_size` | number | Number of items per page (default varies by endpoint) |
526
+ | `cursor` | string | Cursor value from previous response for next page |
527
+
528
+ Use `cursor.after` from the response as the `cursor` parameter for the next request.
529
+
530
+ ---
531
+
532
+ ### Rate Limits
533
+
534
+ Figma uses a **leaky bucket algorithm** for rate limiting. Limits are per-minute and vary by endpoint tier, Figma plan, and seat type.
535
+
536
+ #### Rate Limit Tiers (requests per minute)
537
+
538
+ | Tier | View/Collab Seats | Dev/Full (Starter) | Dev/Full (Pro) | Dev/Full (Org) | Dev/Full (Enterprise) |
539
+ |------|:-----------------:|:------------------:|:--------------:|:--------------:|:---------------------:|
540
+ | Tier 1 | Up to 6/month | 10/min | 15/min | 20/min | 20/min |
541
+ | Tier 2 | Up to 5/min | 25/min | 50/min | 100/min | 100/min |
542
+ | Tier 3 | Up to 10/min | 50/min | 100/min | 150/min | 150/min |
543
+
544
+ **Tracking:**
545
+
546
+ - **PATs:** Rate limits tracked per-user, per-plan
547
+ - **OAuth apps:** Rate limits tracked per-user, per-plan, per-app (each app gets separate budget)
548
+
549
+ #### Endpoint Tier Assignments
550
+
551
+ | Endpoint | Tier |
552
+ |----------|------|
553
+ | `GET /v1/files/:key` | 1 |
554
+ | `GET /v1/files/:key/nodes` | 1 |
555
+ | `GET /v1/images/:key` | 1 |
556
+ | `GET /v1/files/:key/images` | 1 |
557
+ | `GET /v1/files/:key/variables/local` | 2 |
558
+ | `GET /v1/files/:key/variables/published` | 2 |
559
+ | `POST /v1/files/:key/variables` | 3 |
560
+ | `GET /v1/files/:key/meta` | 3 |
561
+ | `GET /v1/files/:key/components` | 3 |
562
+ | `GET /v1/files/:key/component_sets` | 3 |
563
+ | `GET /v1/files/:key/styles` | 3 |
564
+
565
+ #### Handling 429 Errors
566
+
567
+ When rate-limited, the API returns `429 Too Many Requests` with these headers:
568
+
569
+ | Header | Description |
570
+ |--------|-------------|
571
+ | `Retry-After` | Seconds to wait before retrying |
572
+ | `X-Figma-Plan-Tier` | Resource's plan tier (`enterprise`, `org`, `pro`, `starter`) |
573
+ | `X-Figma-Rate-Limit-Type` | Seat classification (`low` for Collab/Viewer, `high` for Full/Dev) |
574
+
575
+ **Recommended backoff strategy:**
576
+
577
+ ```ts
578
+ async function fetchWithRetry(
579
+ url: string,
580
+ headers: Record<string, string>,
581
+ maxRetries = 3
582
+ ): Promise<Response> {
583
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
584
+ const response = await fetch(url, { headers });
585
+
586
+ if (response.status === 429) {
587
+ const retryAfter = parseInt(response.headers.get('Retry-After') || '60', 10);
588
+ console.warn(`Rate limited. Retrying after ${retryAfter}s (attempt ${attempt + 1})`);
589
+ await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
590
+ continue;
591
+ }
592
+
593
+ return response;
594
+ }
595
+ throw new Error(`Failed after ${maxRetries} retries`);
596
+ }
597
+ ```
598
+
599
+ **Mitigation strategies:**
600
+
601
+ 1. **Use `/nodes` instead of `/files`** — Fetch only the nodes you need
602
+ 2. **Cache aggressively** — Store file data and refresh selectively using version checks
603
+ 3. **Batch node IDs** — Combine multiple node IDs in a single `/nodes` or `/images` request
604
+ 4. **Respect `Retry-After`** — Always use the header value, not a fixed delay
605
+
606
+ ---
607
+
608
+ ### Parsing Figma URLs
609
+
610
+ Extract file key and node ID from Figma URLs:
611
+
612
+ ```ts
613
+ /**
614
+ * Parse a Figma URL to extract file key and optional node ID.
615
+ *
616
+ * Supported URL formats:
617
+ * https://www.figma.com/file/FILE_KEY/Title
618
+ * https://www.figma.com/design/FILE_KEY/Title
619
+ * https://www.figma.com/file/FILE_KEY/Title?node-id=1-2
620
+ * https://www.figma.com/design/FILE_KEY/Title?node-id=1-2
621
+ */
622
+ function parseFigmaUrl(url: string): { fileKey: string; nodeId?: string } | null {
623
+ const match = url.match(/figma\.com\/(?:file|design)\/([a-zA-Z0-9]+)/);
624
+ if (!match) return null;
625
+
626
+ const fileKey = match[1];
627
+ const nodeId = new URL(url).searchParams.get('node-id');
628
+
629
+ return {
630
+ fileKey,
631
+ // Figma URLs use hyphen (1-2) but API expects colon (1:2)
632
+ nodeId: nodeId?.replace(/-/g, ':') || undefined,
633
+ };
634
+ }
635
+ ```
636
+
637
+ #### Node ID Encoding
638
+
639
+ - **Internal format:** `1:2` (colon-separated)
640
+ - **URL format:** `1-2` (hyphen in `node-id` query param) or `1%3A2` (URL-encoded colon in API params)
641
+ - **API `ids` parameter:** Use colon format: `?ids=1:2,3:4` (colons are valid in query string values)
642
+
643
+ ---
644
+
645
+ ### Common Pitfalls
646
+
647
+ 1. **`/components` and `/styles` return published library content only.** To find all components in a file (including unpublished), traverse the document tree from `GET /v1/files/:key`.
648
+
649
+ 2. **Full file fetches are expensive.** A complex design file can return 10-50+ MB of JSON. Always prefer `GET /v1/files/:key/nodes?ids=...` for targeted access. Use `depth` parameter to limit tree traversal.
650
+
651
+ 3. **Node IDs use colon format but URLs use hyphens.** The API expects `1:2` in the `ids` parameter, but Figma UI URLs show `node-id=1-2`. Always convert hyphens to colons when passing node IDs to the API.
652
+
653
+ 4. **`absoluteBoundingBox` vs `absoluteRenderBounds`.** Use `absoluteBoundingBox` for layout positioning. Use `absoluteRenderBounds` when you need the full visual extent including strokes and shadows that extend beyond the shape.
654
+
655
+ 5. **Image URLs are temporary.** Rendered image URLs from `/images` expire after 30 days. Image fill URLs from `/files/:key/images` expire after 14 days. Cache the image data, not the URLs.
656
+
657
+ 6. **Vector data requires `geometry=paths`.** Without this query parameter, vector nodes will not include path data needed for SVG export.
658
+
659
+ 7. **Variables API requires Enterprise.** The `file_variables:read` and `file_variables:write` scopes require Enterprise organization full member access. See `figma-api-variables.md` for details.
660
+
661
+ 8. **Branch files have limitations.** You cannot publish from branches, so library endpoints will not work with branch file keys. The main file key is available via `mainFileKey` in the file response.
662
+
663
+ 9. **`null` values in node responses.** The `/nodes` endpoint may return `null` for a requested node ID if the node does not exist or is unrenderable. Always check for null.
664
+
665
+ 10. **PATs expire after 90 days maximum.** Non-expiring tokens can no longer be created. Plan for token rotation in automated workflows.
666
+
667
+ ---
668
+
669
+ ### TypeScript Helper: Figma API Client
670
+
671
+ ```ts
672
+ const FIGMA_BASE = 'https://api.figma.com/v1';
673
+
674
+ interface FigmaClientOptions {
675
+ token: string;
676
+ }
677
+
678
+ class FigmaClient {
679
+ private headers: Record<string, string>;
680
+
681
+ constructor(options: FigmaClientOptions) {
682
+ this.headers = { 'X-Figma-Token': options.token };
683
+ }
684
+
685
+ /** Fetch specific nodes from a file (preferred over full file fetch) */
686
+ async getNodes(fileKey: string, nodeIds: string[]): Promise<GetFileNodesResponse> {
687
+ const ids = nodeIds.join(',');
688
+ const res = await fetch(
689
+ `${FIGMA_BASE}/files/${fileKey}/nodes?ids=${ids}`,
690
+ { headers: this.headers }
691
+ );
692
+ if (!res.ok) throw new Error(`Figma API ${res.status}: ${res.statusText}`);
693
+ return res.json();
694
+ }
695
+
696
+ /** Export nodes as images */
697
+ async exportImages(
698
+ fileKey: string,
699
+ nodeIds: string[],
700
+ options: { format?: 'svg' | 'png' | 'jpg' | 'pdf'; scale?: number } = {}
701
+ ): Promise<Record<string, string | null>> {
702
+ const params = new URLSearchParams({
703
+ ids: nodeIds.join(','),
704
+ format: options.format || 'png',
705
+ scale: String(options.scale || 1),
706
+ });
707
+ const res = await fetch(
708
+ `${FIGMA_BASE}/images/${fileKey}?${params}`,
709
+ { headers: this.headers }
710
+ );
711
+ if (!res.ok) throw new Error(`Figma API ${res.status}: ${res.statusText}`);
712
+ const data = await res.json();
713
+ return data.images;
714
+ }
715
+
716
+ /** Fetch full file (use sparingly — prefer getNodes) */
717
+ async getFile(fileKey: string, depth?: number): Promise<GetFileResponse> {
718
+ const params = depth ? `?depth=${depth}` : '';
719
+ const res = await fetch(
720
+ `${FIGMA_BASE}/files/${fileKey}${params}`,
721
+ { headers: this.headers }
722
+ );
723
+ if (!res.ok) throw new Error(`Figma API ${res.status}: ${res.statusText}`);
724
+ return res.json();
725
+ }
726
+ }
727
+
728
+ // Usage:
729
+ const figma = new FigmaClient({ token: process.env.FIGMA_TOKEN! });
730
+ const nodes = await figma.getNodes('FILE_KEY', ['1:2', '3:4']);
731
+ ```
732
+
733
+ ---
734
+
735
+ ## Cross-References
736
+
737
+ - **`figma-api-variables.md`** — Variables API for design tokens (collections, modes, bound variables)
738
+ - **`figma-api-plugin.md`** — Plugin API for sandbox access, SceneNode types, and IPC patterns
739
+ - **`figma-api-webhooks.md`** — Webhooks v2 for real-time file change notifications
740
+ - **`figma-api-devmode.md`** — Dev Mode, codegen plugins, and Dev Resources API
741
+ - **`design-to-code-layout.md`** — Auto Layout to Flexbox mapping (consumes node layout properties)
742
+ - **`design-to-code-assets.md`** — Image/SVG export patterns (consumes image endpoints)