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.
- package/README.md +133 -0
- package/bin/install.js +328 -0
- package/knowledge/README.md +62 -0
- package/knowledge/css-strategy.md +973 -0
- package/knowledge/design-to-code-assets.md +855 -0
- package/knowledge/design-to-code-layout.md +929 -0
- package/knowledge/design-to-code-semantic.md +1085 -0
- package/knowledge/design-to-code-typography.md +1003 -0
- package/knowledge/design-to-code-visual.md +1145 -0
- package/knowledge/design-tokens-variables.md +1261 -0
- package/knowledge/design-tokens.md +960 -0
- package/knowledge/figma-api-devmode.md +894 -0
- package/knowledge/figma-api-plugin.md +920 -0
- package/knowledge/figma-api-rest.md +742 -0
- package/knowledge/figma-api-variables.md +848 -0
- package/knowledge/figma-api-webhooks.md +876 -0
- package/knowledge/payload-blocks.md +1184 -0
- package/knowledge/payload-figma-mapping.md +1210 -0
- package/knowledge/payload-visual-builder.md +1004 -0
- package/knowledge/plugin-architecture.md +1176 -0
- package/knowledge/plugin-best-practices.md +1206 -0
- package/knowledge/plugin-codegen.md +1313 -0
- package/package.json +31 -0
- package/skills/README.md +103 -0
- package/skills/audit-plugin/SKILL.md +244 -0
- package/skills/build-codegen-plugin/SKILL.md +279 -0
- package/skills/build-importer/SKILL.md +320 -0
- package/skills/build-plugin/SKILL.md +199 -0
- package/skills/build-token-pipeline/SKILL.md +363 -0
- package/skills/ref-html/SKILL.md +290 -0
- package/skills/ref-layout/SKILL.md +150 -0
- package/skills/ref-payload-block/SKILL.md +415 -0
- package/skills/ref-react/SKILL.md +222 -0
- 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)
|