Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Make your first Figma REST API call to fetch a file and inspect its node tree. Use when starting a new Figma integration, testing API connectivity, or learning the Figma document structure. Trigger with phrases like "figma hello world", "figma first call", "figma quick start", "fetch figma file".
.claude/skills/jeremylongshore-figma-hello-world/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-02 | ✗→✓ | ▲ Improved | 56% | 0% |
| case-14 | ✓→✗ | ▼ Worse | 38% | 0% |
| case-01 | ✓→✓ | = Same ✓ | 116% | 0% |
| case-03 | ✓→✓ | = Same ✓ | 30% | 0% |
| case-04 | ✓→✓ | = Same ✓ | 104% | 0% |
Make your first Figma REST API call. Fetch a file's metadata and document tree, then inspect the node structure that represents every layer and object in a Figma design.
figma-install-auth setupfigma.com/design/<FILE_KEY>/...)FIGMA_PAT environment variable setbash# Get the full document JSON for a file curl -s -H "X-Figma-Token: ${FIGMA_PAT}" \ "https://api.figma.com/v1/files/${FIGMA_FILE_KEY}" | jq '{ name: .name, lastModified: .lastModified, version: .version, pages: [.document.children[] | .name] }'
Expected output:
json{ "name": "My Design File", "lastModified": "2025-03-15T10:30:00Z", "version": "1234567890", "pages": ["Page 1", "Components", "Tokens"] }
Every Figma file is a tree of typed nodes:
DOCUMENT (root)
├── CANVAS (page)
│ ├── FRAME (container / auto-layout)
│ │ ├── TEXT
│ │ ├── RECTANGLE
│ │ └── INSTANCE (component instance)
│ ├── GROUP
│ │ └── VECTOR
│ ├── COMPONENT (reusable master)
│ └── SECTIONKey node types: DOCUMENT, CANVAS, FRAME, GROUP, RECTANGLE, ELLIPSE, TEXT, VECTOR, COMPONENT, COMPONENT_SET, INSTANCE, LINE, SECTION, BOOLEAN_OPERATION.
typescript// hello-figma.ts const PAT = process.env.FIGMA_PAT!; const FILE_KEY = process.env.FIGMA_FILE_KEY!; interface FigmaNode { id: string; name: string; type: string; children?: FigmaNode[]; } interface FigmaFileResponse { name: string; lastModified: string; version: string; document: FigmaNode; components: Record<string, { key: string; name: string; description: string }>; styles: Record<string, { key: string; name: string; style_type: string }>; } async function main() { const res = await fetch( `https://api.figma.com/v1/files/${FILE_KEY}`, { headers: { 'X-Figma-Token': PAT } } ); if (!res.ok) { throw new Error(`Figma API error: ${res.status} ${res.statusText}`); } const file: FigmaFileResponse = await res.json(); console.log(`File: ${file.name}`); console.log(`Last modified: ${file.lastModified}`); console.log(`Components: ${Object.keys(file.components).length}`); console.log(`Styles: ${Object.keys(file.styles).length}`); // Walk the first page and list top-level frames const firstPage = file.document.children?.[0]; if (firstPage) { console.log(`\nPage: ${firstPage.name}`); for (const child of firstPage.children ?? []) { console.log(` ${child.type}: ${child.name} (${child.id})`); } } } main().catch(console.error);
typescript// Fetch only specific nodes by ID (faster for large files) async function fetchNodes(fileKey: string, nodeIds: string[]) { const ids = nodeIds.join(','); const res = await fetch( `https://api.figma.com/v1/files/${fileKey}/nodes?ids=${ids}`, { headers: { 'X-Figma-Token': PAT } } ); const data = await res.json(); // data.nodes is a map: { "nodeId": { document: {...}, components: {...} } } return data.nodes; } // Node IDs use the format "pageId:frameId" (e.g., "0:1", "123:456") const nodes = await fetchNodes(FILE_KEY, ['0:1', '2:3']);
| Error | Status | Cause | Solution | |-------|--------|-------|----------| | Not found | 404 | Invalid file key | Verify the key from the Figma URL | | Forbidden | 403 | No access to file | Check token scopes and file permissions | | Rate limited | 429 | Too many requests | Honor Retry-After header | | Empty document | 200 | File has no pages | Check if file was recently created |
bash# Count total nodes in a file curl -s -H "X-Figma-Token: ${FIGMA_PAT}" \ "https://api.figma.com/v1/files/${FIGMA_FILE_KEY}" \ | jq '[.. | .id? // empty] | length'
bashcurl -s -H "X-Figma-Token: ${FIGMA_PAT}" \ "https://api.figma.com/v1/files/${FIGMA_FILE_KEY}" \ | jq -r '.thumbnailUrl'
Proceed to figma-local-dev-loop for setting up a development workflow.
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | pass→pass | 5,110 | 3,790 | -26% | 1 | 1 | 0% | 1,081 | 2,336 | +116% | 0 | 0 | — |
case-02 | fail→pass | 16,960 | 13,611 | -20% | 1 | 1 | 0% | 2,856 | 4,455 | +56% | 0 | 0 | — |
case-03 | pass→pass | 23,016 | 31,458 | +37% | 1 | 1 | 0% | 4,853 | 6,320 | +30% | 0 | 0 | — |
case-04 | pass→pass | 7,797 | 11,569 | +48% | 1 | 1 | 0% | 1,554 | 3,168 | +104% | 0 | 0 | — |
case-05 | pass→pass | 11,504 | 7,253 | -37% | 1 | 1 | 0% | 1,748 | 2,763 | +58% | 0 | 0 | — |
case-06 | pass→pass | 7,407 | 6,996 | -6% | 1 | 1 | 0% | 1,460 | 2,861 | +96% | 0 | 0 | — |
case-07 | pass→pass | 11,478 | 9,286 | -19% | 1 | 1 | 0% | 1,611 | 2,953 | +83% | 0 | 0 | — |
case-08 | pass→pass | 13,463 | 11,244 | -16% | 1 | 1 | 0% | 2,369 | 3,204 | +35% | 0 | 0 | — |
case-09 | pass→pass | 7,215 | 3,756 | -48% | 1 | 1 | 0% | 1,100 | 2,201 | +100% | 0 | 0 | — |
case-10 | pass→pass | 4,091 | 2,619 | -36% | 1 | 1 | 0% | 773 | 1,963 | +154% | 0 | 0 | — |
case-11 | pass→pass | 3,771 | 3,620 | -4% | 1 | 1 | 0% | 646 | 2,138 | +231% | 0 | 0 | — |
case-12 | pass→pass | 15,784 | 10,511 | -33% | 1 | 1 | 0% | 2,295 | 3,329 | +45% | 0 | 0 | — |
case-13 | pass→pass | 12,509 | 4,438 | -65% | 1 | 1 | 0% | 1,939 | 2,294 | +18% | 0 | 0 | — |
case-14 | pass→fail | 16,620 | 14,582 | -12% | 1 | 1 | 0% | 2,886 | 3,993 | +38% | 0 | 0 | — |
case-15 | pass→pass | 13,660 | 12,563 | -8% | 1 | 1 | 0% | 2,579 | 3,823 | +48% | 0 | 0 | — |
case-16 | pass→pass | 13,118 | 8,958 | -32% | 1 | 1 | 0% | 2,538 | 3,308 | +30% | 0 | 0 | — |
case-17 | pass→pass | 12,183 | 10,504 | -14% | 1 | 1 | 0% | 2,297 | 3,499 | +52% | 0 | 0 | — |
case-18 | pass→pass | 10,303 | 9,099 | -12% | 1 | 1 | 0% | 1,597 | 2,936 | +84% | 0 | 0 | — |
case-19 | pass→pass | 3,305 | 2,128 | -36% | 1 | 1 | 0% | 484 | 1,779 | +268% | 0 | 0 | — |
case-20 | pass→pass | 4,594 | 4,487 | -2% | 1 | 1 | 0% | 839 | 2,107 | +151% | 0 | 0 | — |
case-21 | pass→pass | 4,825 | 2,904 | -40% | 1 | 1 | 0% | 985 | 2,132 | +116% | 0 | 0 | — |
case-22 | pass→pass | 2,847 | 2,582 | -9% | 1 | 1 | 0% | 409 | 2,030 | +396% | 0 | 0 | — |
case-23 | pass→pass | 2,448 | 3,158 | +29% | 1 | 1 | 0% | 432 | 2,050 | +375% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 23 cases were attempted. The headline lift of 0 percentage points is the difference between those two pass rates over the 23 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.