Figmaのファイルに付いたコメントは、REST APIのGET /v1/files/:key/commentsで取得できます。レビュー内容を一覧化する場合は、コメント本文だけでなく、返信先のparent_idと解決日時を残すと、会話のつながりを確認しやすくなります。
この記事では、必要な権限、PythonによるCSV出力、返信・削除、エラーの確認順を説明します。2026年10月3日に確認した公式仕様に基づく例です。CSV処理は模擬データで確認していますが、実際のFigmaファイルへのAPI接続は未検証です。公式コメントAPI
必要なものと権限
- コメントを取得したいFigmaファイルへのアクセス権を確認します。
- 個人アクセストークンへ
file_comments:readスコープを付けます。 - 対象ファイルのURLからファイルキーを確認します。
- Pythonとrequestsを用意し、環境変数
FIGMA_TOKEN・FIGMA_FILE_KEYへ設定します。
取得だけなら書き込みスコープは不要です。投稿・返信・自分のコメントの削除も行う場合に、file_comments:writeを追加します。トークンの取得方法
ファイルキーはURLの/design/や/file/の直後にある文字列です。node-idはレイヤーのIDなので、コメント取得先のファイルキーとして使わないでください。ブランチのコメントを扱う場合は、ブランチキーを指定する方法もあります。REST APIとPlugin APIの違い
コメントと返信を区別する
取得結果のcomments配列には、ルートのコメントと返信が含まれます。親子関係はparent_idで追います。画面のコメント番号であるorder_idはトップレベルのコメントだけに設定されるため、識別にはidを使います。
| 項目 | 用途 |
|---|---|
| id | API上のコメント識別子 |
| parent_id | 返信先のコメントID |
| user.handle | 投稿者の表示名 |
| created_at | 投稿日時。UTCのISO 8601形式 |
| resolved_at | 解決済みの日時。返信自身にない場合も親を確認 |
| client_meta | ピンの位置・フレームへの相対位置など |
返信のresolved_atだけを見て、スレッド全体が未解決と判断しないようにします。次のCSV例は、親コメントの解決日時も参照したthread_resolved_at列を作ります。コメントの型と位置情報
PythonでCSVへ出力する
次をexport_comments.pyとして保存します。as_md=trueは、対応するコメントをMarkdown形式で取得する指定です。CSVにはその文字列を保存し、HTMLとして実行しません。
import csv
import json
import os
import re
import tempfile
from pathlib import Path
import requests
def csv_text(value):
text = "" if value is None else str(value)
if text.lstrip().startswith(("=", "+", "-", "@")):
return "'" + text
return text
def write_comments(comments, destination):
by_id = {str(c["id"]): c for c in comments}
destination = Path(destination)
columns = ["id", "parent_id", "user", "created_at",
"resolved_at", "thread_resolved_at", "message", "client_meta"]
temporary = None
try:
with tempfile.NamedTemporaryFile(
mode="w", newline="", encoding="utf-8-sig",
dir=destination.parent, delete=False
) as handle:
temporary = Path(handle.name)
writer = csv.writer(handle)
writer.writerow(columns)
for comment in comments:
parent_id = str(comment.get("parent_id") or "")
root = by_id.get(parent_id, comment)
values = [comment["id"], parent_id,
(comment.get("user") or {}).get("handle", ""),
comment.get("created_at", ""),
comment.get("resolved_at", ""),
root.get("resolved_at", ""),
comment.get("message", ""),
json.dumps(comment.get("client_meta"), ensure_ascii=False)]
writer.writerow([csv_text(v) for v in values])
temporary.replace(destination)
finally:
if temporary is not None:
temporary.unlink(missing_ok=True)
def main():
key = os.environ["FIGMA_FILE_KEY"]
if not re.fullmatch(r"[A-Za-z0-9_-]+", key):
raise SystemExit("FIGMA_FILE_KEYにはURL全体ではなくファイルキーを設定してください")
response = requests.get(
f"https://api.figma.com/v1/files/{key}/comments",
headers={"X-Figma-Token": os.environ["FIGMA_TOKEN"]},
params={"as_md": "true"}, timeout=60
)
if response.status_code == 429:
raise SystemExit("API制限に達しました。Retry-Afterを確認して再実行してください")
response.raise_for_status()
comments = response.json()["comments"]
if not isinstance(comments, list):
raise ValueError("commentsの形式が想定と異なります")
write_comments(comments, "comments.csv")
print(f"{len(comments)}件をcomments.csvへ保存しました")
if __name__ == "__main__":
main()python -m pip install requests
python export_comments.py実行が成功すると、作業フォルダーにcomments.csvを保存します。UTF-8 BOM付きで日本語を扱い、カンマ・改行・引用符はCSVライブラリでエスケープします。数式として解釈されやすい先頭文字にはアポストロフィを付けるため、該当セルの内容は元の本文と完全には同一ではありません。
一時ファイルへ書いてから置き換えるので、取得や変換が途中で失敗した場合は既存のCSVを更新しません。成功時には同名のCSVを置き換えます。出力に投稿者名や非公開のコメントが含まれる場合は、元ファイルの共有範囲に合わせて扱ってください。
投稿・返信・削除のAPI
| 操作 | エンドポイントと条件 |
|---|---|
| 新規投稿 | POST /v1/files/:file_key/comments。messageと必要な位置情報を指定 |
| 返信 | 同じPOST先でcomment_idにルートコメントのIDを指定 |
| 削除 | DELETE /v1/files/:file_key/comments/:comment_id。投稿した本人だけ |
| リアクション | comments/:comment_id/reactionsへGET・POST・DELETE |
返信先のcomment_idには、返信コメント自身のIDは指定できません。返信のparent_idでルートを探します。また、取得できたコメントをすべて削除できるわけではありません。位置を指定するclient_metaは、キャンバス座標やフレーム相対位置など、目的に合う型を選びます。投稿・返信の仕様
403・404・429の確認順
403ならトークンの有効期限と読み取りスコープ、対象ファイルへの権限を確認します。404ならファイルキー、ブランチ、削除・アクセス権を確認します。429なら応答のRetry-Afterに従い、短い間隔での再取得を止めます。
コメントAPIはTier 2です。上限は席と対象リソースのプランに関係するので、MCPの回数表と混同しないでください。REST APIの利用制限
新しいコメントの発生を契機に処理したい場合は、毎回全件取得する代わりにWebhookのFILE_COMMENTを検討します。Webhookの通知本文はこのGETの返り値とは形式が異なります。
