Figmaのコンポーネントやインスタンスを一覧にするには、REST APIでファイルのノードを取得し、Google Apps Script(GAS)で行データへ変換します。名前、node ID、部品のキー、文字情報を並べれば、デザインの棚卸しに使えます。
最初は小さなファイルか、特定のセクションを対象にしてください。ファイル全体の取得はデータ量とAPIの制限に影響されます。この記事では、外部ライブラリを使わずに取得する例と、従来のFigmaSheetライブラリを使う例を分けます。
用意するものと取得できる範囲
| 用意するもの | 用途 |
|---|---|
| Googleスプレッドシートと編集権限 | 一覧の出力先 |
| スプレッドシートに紐づくGAS | APIの呼び出しと行の生成 |
| FigmaのPAT | file_content:readでファイル内容を取得 |
| ファイルキー | /design/ファイルキー/の部分 |
| セクション等のnode ID(任意) | 取得範囲を絞る |
REST APIのノード情報は、デザインの見た目をそのまま表へ移す機能ではありません。ローカルのPlugin APIと取得できる情報が異なり、参照先部品の情報が常にすべて入るとも限りません。REST APIとPlugin APIの違い
次の画像は旧FigmaSheet実装による2025年12月の出力例です。後述の独立したGAS例を新たに実行した画面ではありません。

PATとファイルキーを設定する
- スプレッドシートの拡張機能 → Apps Scriptを開きます。
- プロジェクトの設定でスクリプトプロパティを追加します。
FIGMA_TOKENにPAT、FIGMA_FILE_KEYにファイルキーを保存します。- 範囲を絞る場合は
FIGMA_NODE_IDSに123:456,789:1011のようなnode IDを保存します。
PATの作成は個人アクセストークンの取得とスコープを参照してください。プロパティにはトークンを置き、ソースコードや実行ログへ値を記載しません。スクリプトプロパティは同じプロジェクトのコードから読み取れるため、プロジェクトを編集できる人も確認してください。
node IDはAPI用の123:456形式を使います。画面のリンクにnode-id=123-456とある場合は区切りを確認してください。シートで数字の自動変換を防ぐための先頭アポストロフィは、APIへ渡すIDには付けません。ファイルとノードの取得仕様

外部ライブラリを使わないGASの最小例
新しいシート名Figma取得結果へ、コンポーネント・コンポーネントセット・インスタンス・テキストを出力します。同名シートがある場合は、取得と解析が終わってから内容を更新します。既存の業務表とは別のスプレッドシートで試してください。
function exportFigmaNodes() {
const props = PropertiesService.getScriptProperties();
const token = props.getProperty('FIGMA_TOKEN');
const fileKey = props.getProperty('FIGMA_FILE_KEY');
const ids = (props.getProperty('FIGMA_NODE_IDS') || '')
.split(',').map(s => s.trim()).filter(Boolean);
if (!token || !fileKey) throw new Error('トークンとファイルキーを設定してください');
if (ids.some(id => !/^\d+:\d+$/.test(id))) {
throw new Error('node IDは123:456形式で指定してください');
}
const base = 'https://api.figma.com/v1/files/' + encodeURIComponent(fileKey);
const url = ids.length ? base + '/nodes?ids=' +
encodeURIComponent(ids.join(',')) : base;
const response = UrlFetchApp.fetch(url, {
headers: { 'X-Figma-Token': token }, muteHttpExceptions: true
});
const status = response.getResponseCode();
if (status !== 200) {
throw new Error('Figma API HTTP ' + status + '。権限・期限・制限を確認してください');
}
const data = JSON.parse(response.getContentText());
const types = new Set(['INSTANCE', 'COMPONENT', 'COMPONENT_SET', 'TEXT']);
const rows = [];
const seen = new Set();
const safeCell = value => {
const text = value == null ? '' : String(value);
return /^[=+@\-\t\r\n]/.test(text) ? "'" + text : text;
};
function walk(node, path, components) {
if (!node || seen.has(node.id)) return;
seen.add(node.id);
const nextPath = path.concat(node.name || node.id);
if (types.has(node.type)) {
const component = components[node.componentId || node.id] || {};
const bounds = node.absoluteBoundingBox || {};
rows.push([node.id, node.type, node.name, nextPath.join(' / '),
node.componentId || '', component.key || '', node.characters || '',
bounds.width == null ? '' : bounds.width,
bounds.height == null ? '' : bounds.height].map(safeCell));
}
(node.children || []).forEach(child => walk(child, nextPath, components));
}
if (ids.length) {
ids.forEach(id => {
const item = data.nodes && data.nodes[id];
if (!item || !item.document) throw new Error('対象node IDを取得できませんでした');
walk(item.document, [], item.components || {});
});
} else {
if (!data.document) throw new Error('ファイルのdocumentがありません');
walk(data.document, [], data.components || {});
}
const headers = ['nodeId', 'type', 'name', 'layerPath', 'componentId',
'componentKey', 'characters', 'width', 'height'];
const sheetName = 'Figma取得結果';
const book = SpreadsheetApp.getActiveSpreadsheet();
const sheet = book.getSheetByName(sheetName) || book.insertSheet(sheetName);
const values = [headers].concat(rows);
if (sheet.getMaxRows() < values.length) {
sheet.insertRowsAfter(sheet.getMaxRows(), values.length - sheet.getMaxRows());
}
if (sheet.getMaxColumns() < headers.length) {
sheet.insertColumnsAfter(sheet.getMaxColumns(), headers.length - sheet.getMaxColumns());
}
sheet.clearContents();
sheet.getRange(1, 1, values.length, headers.length).setValues(values);
sheet.setFrozenRows(1);
console.log('出力行数: ' + rows.length);
}exportFigmaNodesを実行し、初回のGoogle側の承認でアクセス内容を確認します。出力先、node ID、対象数をFigma側と照合してください。このコードは公式の応答構造に基づく例です。2026年10月3日のNode.js上の模擬データで、部品のキーと列の生成、数式として扱われる文字列への対処、429時にシートを書き換えない処理を確認しました。実PATを使う接続やGAS上の実行は未検証です。
特定セクションを取得した旧実装の例です。範囲指定は、ファイル全体の取得よりデータ量を抑えるためにも使えます。

既存のFigmaSheetライブラリを利用する場合
従来の公開ライブラリを使う場合は、Apps Scriptのライブラリ追加で次のスクリプトIDを指定し、識別子をFigmaSheetにします。FIGMA_TOKENは認証情報のプロパティ名であり、このライブラリIDとは別です。
1KjcblxtHtWAzr20-1Voztr0RTh-blaHx_BdLxsFuO0nT9CDdpGgsYCxF追加画面で利用できる版と関数を確認してください。現在の公開権限・利用可能な版・実装全体の挙動は今回再検証できていません。ライブラリが表示されない場合は、上記の独立したGAS例で試せます。Googleのライブラリ追加手順

従来の呼び出しでは、全体取得をFigmaSheet.exportToSheet、範囲指定をFigmaSheet.exportSectionToSheetへ渡します。以下は範囲指定の形です。ファイルキーとIDは利用するファイルの値へ変更します。
function runExportSection() {
const props = PropertiesService.getScriptProperties();
const token = props.getProperty('FIGMA_TOKEN');
const fileKey = props.getProperty('FIGMA_FILE_KEY');
const sectionIds = ['123:456'];
const result = FigmaSheet.exportSectionToSheet(fileKey, sectionIds, {
token: token,
sheetName: 'Figma情報一覧',
fields: ['nodeId', 'layerPath', 'componentKey', 'pageName',
'instanceName', 'componentSetName', 'characters'],
nodeTypes: ['INSTANCE', 'COMPONENT', 'COMPONENT_SET', 'TEXT']
});
console.log(result);
}旧実装ではmainComponent、バリアント、位置・寸法、塗り、線、文字スタイルなどの列も扱っていました。これはFigmaSheet側で解析している項目で、REST APIの同名プロパティが必ず直接返るという意味ではありません。使う版の実装と対象ファイルで確認します。

大きなファイル・エラー・列の不足への対応
| 状況 | 対応 |
|---|---|
| 401・403 | PATの期限、Required scopes、ファイル権限を確認 |
| 429 | 再実行を止め、Retry-AfterとFigmaの制限を確認 |
| 応答が大きい・実行時間を超える | セクションを指定し、複数回に分ける |
| コンポーネントのキーが空欄 | 応答のcomponents情報と参照先の条件を確認 |
| 一部の文字情報が不足 | RESTで取得した文字とスタイルの表現を確認 |
| Libraryの関数が見つからない | 識別子、利用版、公開権限を確認 |
GASのURL Fetchには応答サイズ、実行時間、日次呼び出し数などの制限があります。現在の公式表では応答サイズは1回50MB、通常のスクリプト実行は1回6分です。Figma側のレート制限も併せて設計し、再取得前にキャッシュや変更有無を検討してください。GASの制限、Figmaの制限
旧ライブラリの設計は、公開関数で認証・取得方法・キャッシュを管理し、API通信、JSON解析、シート書き込みを分ける構成でした。既存の呼び出し例も残しています。GitHubのフォルダーは呼び出し例を掲載したもので、公開ライブラリの実装全体が含まれているとは限りません。
Figma上で外部スタイルを調べる場合はPlugin APIによるCSV抽出を参照してください。
