airtable について
Hermes ユーザー向けに、terminal ツールから curl で Airtable REST API を操作する方法を案内するスキルです。トークン権限、レコード操作、ページネーション、一括処理、安全な変更の注意点を扱います。
主な機能
- 01curl による Airtable REST API 呼び出し
- 02ベースとテーブルスキーマの確認
- 03レコードの検索、絞り込み、並べ替え
- 04レコードの作成、更新、削除、アップサート
- 05ページネーション、レート制限、権限の管理
活用シーン
- 01変更前にテーブルスキーマを確認
- 02数式でレコードを絞り込んで更新
- 03レコードの一括作成や冪等な同期
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ nousresearch/ hermes-agent --skill airtableインストール後、Claude Code で/airtableを入力して読み込まれたか確認します。
詳細
Airtable — ベース、テーブル、レコード
terminal ツールを使って、curl から Airtable の REST API を直接利用します。MCP サーバーも OAuth フローも Python SDK も不要です。必要なのは curl と個人用アクセストークンだけです。
前提条件
- https://airtable.com/create/tokens で 個人用アクセストークン(PAT) を作成します(トークンは
pat...で始まります)。 - 次のスコープを付与します(最小限):
data.records:read— 行の読み取りdata.records:write— 行の作成/更新/削除schema.bases:read— ベースとテーブルの一覧表示
- 重要: 同じトークンの UI で、アクセスする各ベースをトークンの Access リストに追加します。PAT のアクセス範囲はベースごとです。対象と異なるベースに対して有効なトークンを使うと、
403が返されます。 - トークンを
${HERMES_HOME:-~/.hermes}/.envに保存します(またはhermes setupを実行します):AIRTABLE_API_KEY=pat_your_token_here
注:従来の
key...API キーは 2024 年 2 月に非推奨になりました。現在は PAT と OAuth トークンのみ使用できます。
API の基本
- エンドポイント:
https://api.airtable.com/v0 - 認証ヘッダー:
Authorization: Bearer $AIRTABLE_API_KEY - すべてのリクエストで JSON を使用します(POST/PATCH/PUT の本文には
Content-Type: application/jsonを指定します)。 - オブジェクト ID: ベースは
app...、テーブルはtbl...、レコードはrec...、フィールドはfld...です。ID は変わりませんが、名前は変わる可能性があります。自動化では ID を優先してください。 - レート制限: ベースごとに毎秒 5 リクエストです。
429が返されたら、リクエストの間隔を空けてください。単一のベースに対するリクエストが集中すると、スロットリングされます。
ベースへの curl の基本パターン:
curl -s "https://api.airtable.com/v0/$BASE_ID/$TABLE?maxRecords=5" \
-H "Authorization: Bearer $AIRTABLE_API_KEY" | python -m json.tool
-s は curl の進行状況バーを抑制します。Hermes のツール出力をきれいに保つため、すべての呼び出しで設定してください。読みやすい JSON にするには、python -m json.tool(常に利用可能)または jq(インストール済みの場合)にパイプします。
フィールドの種類(リクエスト本文の形式)
| フィールドの種類 | 書き込み形式 |
|---|---|
| 単一行テキスト | "Name": "hello" |
| 長文テキスト | "Notes": "multi\nline" |
| 数値 | "Score": 42 |
| チェックボックス | "Done": true |
| 単一選択 | "Status": "Todo"(typecast: true を指定しない限り、名前がすでに存在している必要があります) |
| 複数選択 | "Tags": ["urgent", "bug"] |
| 日付 | "Due": "2026-04-01" |
| 日時(UTC) | "At": "2026-04-01T14:30:00.000Z" |
| URL/メールアドレス/電話番号 | "Link": "https://…" |
| 添付ファイル | "Files": [{"url": "https://…"}](Airtable が取得して再ホストします) |
| リンクされたレコード | "Owner": ["recXXXXXXXXXXXXXX"](レコード ID の配列) |
| ユーザー | "AssignedTo": {"id": "usrXXXXXXXXXXXXXX"} |
作成/更新の本文の最上位に "typecast": true を指定すると、Airtable が値を自動変換します(例:新しい選択肢をその場で作成する、"42" を 42 に変換する)。
よく使うクエリ
トークンから参照できるベースの一覧
curl -s "https://api.airtable.com/v0/meta/bases" \
-H "Authorization: Bearer $AIRTABLE_API_KEY" | python -m json.tool
ベースのテーブルとスキーマを一覧表示
curl -s "https://api.airtable.com/v0/meta/bases/$BASE_ID/tables" \
-H "Authorization: Bearer $AIRTABLE_API_KEY" | python -m json.tool
変更を加える前に必ず実行してください。正確なフィールド名と ID を確認でき、選択フィールドの options.choices や主フィールド名も表示されます。
レコード一覧(最初の 10 件)
curl -s "https://api.airtable.com/v0/$BASE_ID/$TABLE?maxRecords=10" \
-H "Authorization: Bearer $AIRTABLE_API_KEY" | python -m json.tool
単一レコードを取得
curl -s "https://api.airtable.com/v0/$BASE_ID/$TABLE/$RECORD_ID" \
-H "Authorization: Bearer $AIRTABLE_API_KEY" | python -m json.tool
レコードを絞り込む(filterByFormula)
Airtable の数式は URL エンコードする必要があります。Python 標準ライブラリを使い、手動でエンコードしないでください:
FORMULA="{Status}='Todo'"
ENC=$(python -c 'import sys, urllib.parse; print(urllib.parse.quote(sys.argv[1], safe=""))' "$FORMULA")
curl -s "https://api.airtable.com/v0/$BASE_ID/$TABLE?filterByFormula=$ENC&maxRecords=20" \
-H "Authorization: Bearer $AIRTABLE_API_KEY" | python -m json.tool
便利な数式のパターン:
- 完全一致:
{Email}='[email protected]' - 含む:
FIND('bug', LOWER({Title})) - 複数条件:
AND({Status}='Todo', {Priority}='High') - または:
OR({Owner}='alice', {Owner}='bob') - 空でない:
NOT({Assignee}='') - 日付の比較:
IS_AFTER({Due}, TODAY())
並べ替えと特定フィールドの選択
curl -s "https://api.airtable.com/v0/$BASE_ID/$TABLE?sort%5B0%5D%5Bfield%5D=Priority&sort%5B0%5D%5Bdirection%5D=asc&fields%5B%5D=Name&fields%5B%5D=Status" \
-H "Authorization: Bearer $AIRTABLE_API_KEY" | python -m json.tool
クエリパラメーター内の角括弧は必ず URL エンコードしてください(%5B/%5D)。
名前付きビューを使用
curl -s "https://api.airtable.com/v0/$BASE_ID/$TABLE?view=Grid%20view&maxRecords=50" \
-H "Authorization: Bearer $AIRTABLE_API_KEY" | python -m json.tool
ビューに保存されたフィルターと並べ替えが、サーバー側で適用されます。
よく使う変更操作
レコードを作成
curl -s -X POST "https://api.airtable.com/v0/$BASE_ID/$TABLE" \
-H "Authorization: Bearer $AIRTABLE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"fields":{"Name":"New task","Status":"Todo","Priority":"High"}}' | python -m json.tool
1 回の呼び出しで最大 10 件のレコードを作成
curl -s -X POST "https://api.airtable.com/v0/$BASE_ID/$TABLE" \
-H "Authorization: Bearer $AIRTABLE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"typecast": true,
"records": [
{"fields": {"Name": "Task A", "Status": "Todo"}},
{"fields": {"Name": "Task B", "Status": "In progress"}}
]
}' | python -m json.tool
バッチエンドポイントで処理できるのは、1 リクエストあたり 10 件までです。さらに多くのレコードを挿入する場合は、ベースごとに毎秒 5 リクエストの制限を守るため、10 件ずつ短いスリープを挟んで繰り返します。
レコードを更新(PATCH — マージし、変更のないフィールドを保持)
curl -s -X PATCH "https://api.airtable.com/v0/$BASE_ID/$TABLE/$RECORD_ID" \
-H "Authorization: Bearer $AIRTABLE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"fields":{"Status":"Done"}}' | python -m json.tool
マージフィールドでアップサート(ID 不要)
curl -s -X PATCH "https://api.airtable.com/v0/$BASE_ID/$TABLE" \
-H "Authorization: Bearer $AIRTABLE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"performUpsert": {"fieldsToMergeOn": ["Email"]},
"records": [
{"fields": {"Email": "[email protected]", "Status": "Active"}}
]
}' | python -m json.tool
performUpsert は、マージフィールドの値が新しいレコードを作成し、その値がすでに存在するレコードを PATCH します。冪等な同期に最適です。
レコードを削除
curl -s -X DELETE "https://api.airtable.com/v0/$BASE_ID/$TABLE/$RECORD_ID" \
-H "Authorization: Bearer $AIRTABLE_API_KEY" | python -m json.tool
1 回の呼び出しで最大 10 件のレコードを削除
curl -s -X DELETE "https://api.airtable.com/v0/$BASE_ID/$TABLE?records%5B%5D=rec1&records%5B%5D=rec2" \
-H "Authorization: Bearer $AIRTABLE_API_KEY" | python -m json.tool
ページネーション
一覧エンドポイントは、1ページあたり最大 100 件のレコードを返します。レスポンスに "offset": "..." が含まれている場合は、次の呼び出しでその値を渡してください。このフィールドがなくなるまで繰り返します。
OFFSET=""
while :; do
URL="https://api.airtable.com/v0/$BASE_ID/$TABLE?pageSize=100"
[ -n "$OFFSET" ] && URL="$URL&offset=$OFFSET"
RESP=$(curl -s "$URL" -H "Authorization: Bearer $AIRTABLE_API_KEY")
echo "$RESP" | python -c 'import json,sys; d=json.load(sys.stdin); [print(r["id"], r["fields"].get("Name","")) for r in d["records"]]'
OFFSET=$(echo "$RESP" | python -c 'import json,sys; d=json.load(sys.stdin); print(d.get("offset",""))')
[ -z "$OFFSET" ] && break
done
Hermes の一般的なワークフロー
- 認証を確認します。
curl -s -o /dev/null -w "%{http_code}\n" https://api.airtable.com/v0/meta/bases -H "Authorization: Bearer $AIRTABLE_API_KEY"—200が返ることを確認します。 - ベースを見つけます。 ベースの一覧を取得する(上記の手順)か、トークンに
schema.bases:readがない場合は、ユーザーにapp...ID を直接尋ねます。 - スキーマを確認します。
GET /v0/meta/bases/$BASE_ID/tables— 変更を加える前に、正確なフィールド名とプライマリフィールド名をセッション内にキャッシュします。 - 書き込む前に読み取ります。 「Y に該当する X を更新する」場合は、まず
filterByFormulaでrec...ID を特定し、その後PATCH /v0/$BASE_ID/$TABLE/$RECORD_IDを実行します。レコード ID を推測してはいけません。 - 書き込みをまとめます。 関連する作成処理を 10 レコードの POST 1 回にまとめ、5 リクエスト/秒の上限を超えないようにします。
- 破壊的な操作。 API 経由で削除を取り消すことはできません。ユーザーが「X をすべて削除して」と指示した場合は、実行前にフィルターとレコード件数を伝え、確認を取ります。
注意点
filterByFormulaは必ず URL エンコードしてください。 スペースや非 ASCII 文字を含むフィールド名もエンコードが必要です({My Field}→%7BMy%20Field%7D)。上記のパターンのように Python 標準ライブラリを使用し、手作業でエスケープしてはいけません。- 空のフィールドはレスポンスから省略されます。
"Assignee"キーがないからといって、そのフィールドが存在しないとは限りません。このレコードの値が空であることを意味します。フィールドが存在しないと判断する前に、スキーマ(手順 3)を確認してください。 - PATCH と PUT。
PATCHは指定したフィールドをレコードにマージします。PUTはレコード全体を置き換え、含めなかったフィールドをすべてクリアします。デフォルトではPATCHを使用してください。 - 単一選択のオプションは存在している必要があります。 フィールドのオプション一覧に
Shippingがない状態で"Status": "Shipping"を書き込むと、"typecast": trueを渡さない限りINVALID_MULTIPLE_CHOICE_OPTIONSエラーになります(typecastを指定すると、オプションが自動作成されます)。 - トークンのアクセス範囲はベースごとに設定されます。 あるベースでは
403が返り、別のベースでは正常に動作する場合、トークンの Access リストにそのベースが含まれていないことを意味します。スコープや認証の問題ではありません。ユーザーに https://airtable.com/create/tokens でアクセス権を付与してもらってください。 - レート制限はトークン単位ではなく、ベース単位です。
baseAで 5 リクエスト/秒、baseBで 5 リクエスト/秒なら問題ありませんが、baseAだけで 6 リクエスト/秒を実行すると制限されます。429のRetry-Afterヘッダーを確認してください。
Hermes に関する重要な注意事項
- 必ず
curlとterminalツールを使用してください。web_extract(認証ヘッダーを送信できません)やbrowser_exec(UI での認証が必要で、時間もかかります)は使用しないでください。 AIRTABLE_API_KEYは、このスキルが読み込まれると${HERMES_HOME:-~/.hermes}/.envからサブプロセスに自動的に渡されます。curlを呼び出すたびに再エクスポートする必要はありません。- 数式内の波括弧は慎重に扱ってください。 ヒアドキュメントの本文では
{Status}はそのままの文字列です。シェル引数では、波括弧展開のコンテキスト外であれば{Status}は安全です。ただし、動的な文字列を URL に埋め込む前に、python urllib.parse.quoteを通してください。 - 整形には
jq(オプション)ではなく、常に利用できるpython -m json.toolを使用してください。 フィルタリングや射影が必要な場合に限り、jqを使用してください。 - ページネーションは全体ではなく、ページごとに行います。 Airtable の 100 レコード上限は固定で、引き上げる方法はありません。
offsetフィールドがなくなるまでループしてください。 - 2xx 以外のレスポンスでは
errors配列を確認してください。 Airtable はAUTHENTICATION_REQUIRED、INVALID_PERMISSIONS、MODEL_ID_NOT_FOUND、INVALID_MULTIPLE_CHOICE_OPTIONSなどの構造化されたエラーコードを返すため、問題を正確に把握できます。