airtable
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseAirtable — Bases, Tables & Records
Airtable — Bases、Tables与Records
Work with Airtable's REST API directly via using the tool. No MCP server, no OAuth flow, no Python SDK — just and a personal access token.
curlterminalcurl使用工具,通过直接调用Airtable的REST API。无需MCP服务器、OAuth流程或Python SDK —— 只需和个人访问令牌。
terminalcurlcurlPrerequisites
前置条件
- Create a Personal Access Token (PAT) at https://airtable.com/create/tokens (tokens start with ).
pat... - Grant these scopes (minimum):
- — read rows
data.records:read - — create / update / delete rows
data.records:write - — list bases and tables
schema.bases:read
- Important: in the same token UI, add each base you want to access to the token's Access list. PATs are scoped per-base — a valid token on the wrong base returns .
403 - Store the token in (or via
~/.hermes/.env):hermes setupAIRTABLE_API_KEY=pat_your_token_here
Note: legacyAPI keys were deprecated Feb 2024. Only PATs and OAuth tokens work now.key...
- 在https://airtable.com/create/tokens创建**个人访问令牌(Personal Access Token, PAT)**(令牌以开头)。
pat... - 授予以下最低权限范围:
- — 读取行数据
data.records:read - — 创建/更新/删除行数据
data.records:write - — 列出Bases和Tables
schema.bases:read
- 重要提示:在同一个令牌界面中,将你想要访问的每个Base添加到令牌的**访问(Access)**列表中。PAT是按Base划分权限范围的——如果令牌在错误的Base上使用,会返回错误。
403 - 将令牌存储在中(或通过
~/.hermes/.env设置):hermes setupAIRTABLE_API_KEY=pat_your_token_here
注意:旧版格式的API密钥已于2024年2月弃用。现在仅支持PAT和OAuth令牌。key...
API Basics
API基础
- Endpoint:
https://api.airtable.com/v0 - Auth header:
Authorization: Bearer $AIRTABLE_API_KEY - All requests use JSON (for any POST/PATCH/PUT body).
Content-Type: application/json - Object IDs: bases , tables
app..., recordstbl..., fieldsrec.... IDs never change; names can. Prefer IDs in automations.fld... - Rate limit: 5 requests/sec/base. → back off. Burst on a single base will be throttled.
429
Base curl pattern:
bash
curl -s "https://api.airtable.com/v0/$BASE_ID/$TABLE?maxRecords=5" \
-H "Authorization: Bearer $AIRTABLE_API_KEY" | python3 -m json.tool-spython3 -m json.tooljq- 端点:
https://api.airtable.com/v0 - 认证头:
Authorization: Bearer $AIRTABLE_API_KEY - 所有请求均使用JSON格式(POST/PATCH/PUT请求体需设置)。
Content-Type: application/json - 对象ID:Bases为、Tables为
app...、Records为tbl...、Fields为rec...。ID永不改变;名称可能变更。在自动化操作中优先使用ID。fld... - 速率限制:每个Base每秒最多5次请求。收到响应时请暂停请求。针对单个Base的突发请求会被限流。
429
基础curl调用模板:
bash
curl -s "https://api.airtable.com/v0/$BASE_ID/$TABLE?maxRecords=5" \
-H "Authorization: Bearer $AIRTABLE_API_KEY" | python3 -m json.tool-spython3 -m json.tooljqField Types (request body shapes)
字段类型(请求体格式)
| Field type | Write shape |
|---|---|
| Single line text | |
| Long text | |
| Number | |
| Checkbox | |
| Single select | |
| Multi-select | |
| Date | |
| DateTime (UTC) | |
| URL / Email / Phone | |
| Attachment | |
| Linked record | |
| User | |
Pass at the top level of a create/update body to let Airtable auto-coerce values (e.g. create a new select option on the fly, convert → ).
"typecast": true"42"42| 字段类型 | 写入格式 |
|---|---|
| 单行文本 | |
| 多行文本 | |
| 数字 | |
| 复选框 | |
| 单选 | |
| 多选 | |
| 日期 | |
| 日期时间(UTC) | |
| URL / 邮箱 / 电话 | |
| 附件 | |
| 关联记录 | |
| 用户 | |
在创建/更新请求体的顶层设置,可让Airtable自动转换值(例如动态创建新的单选选项、将转换为)。
"typecast": true"42"42Common Queries
常见查询
List bases the token can see
列出令牌可访问的Bases
bash
curl -s "https://api.airtable.com/v0/meta/bases" \
-H "Authorization: Bearer $AIRTABLE_API_KEY" | python3 -m json.toolbash
curl -s "https://api.airtable.com/v0/meta/bases" \
-H "Authorization: Bearer $AIRTABLE_API_KEY" | python3 -m json.toolList tables + schema for a base
列出某个Base的Tables及 schema
bash
curl -s "https://api.airtable.com/v0/meta/bases/$BASE_ID/tables" \
-H "Authorization: Bearer $AIRTABLE_API_KEY" | python3 -m json.toolUse this BEFORE mutating — confirms exact field names and IDs, surfaces for select fields, and shows primary-field names.
options.choicesbash
curl -s "https://api.airtable.com/v0/meta/bases/$BASE_ID/tables" \
-H "Authorization: Bearer $AIRTABLE_API_KEY" | python3 -m json.tool在执行修改操作前请先调用此接口——确认字段的准确名称和ID,查看单选字段的选项,以及主键字段名称。
options.choicesList records (first 10)
列出记录(前10条)
bash
curl -s "https://api.airtable.com/v0/$BASE_ID/$TABLE?maxRecords=10" \
-H "Authorization: Bearer $AIRTABLE_API_KEY" | python3 -m json.toolbash
curl -s "https://api.airtable.com/v0/$BASE_ID/$TABLE?maxRecords=10" \
-H "Authorization: Bearer $AIRTABLE_API_KEY" | python3 -m json.toolGet a single record
获取单条记录
bash
curl -s "https://api.airtable.com/v0/$BASE_ID/$TABLE/$RECORD_ID" \
-H "Authorization: Bearer $AIRTABLE_API_KEY" | python3 -m json.toolbash
curl -s "https://api.airtable.com/v0/$BASE_ID/$TABLE/$RECORD_ID" \
-H "Authorization: Bearer $AIRTABLE_API_KEY" | python3 -m json.toolFilter records (filterByFormula)
筛选记录(filterByFormula)
Airtable formulas must be URL-encoded. Let Python stdlib do it — never hand-encode:
bash
FORMULA="{Status}='Todo'"
ENC=$(python3 -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" | python3 -m json.toolUseful formula patterns:
- Exact match:
{Email}='user@example.com' - Contains:
FIND('bug', LOWER({Title})) - Multiple conditions:
AND({Status}='Todo', {Priority}='High') - Or:
OR({Owner}='alice', {Owner}='bob') - Not empty:
NOT({Assignee}='') - Date comparison:
IS_AFTER({Due}, TODAY())
Airtable公式必须进行URL编码。请使用Python标准库完成编码——切勿手动编码:
bash
FORMULA="{Status}='Todo'"
ENC=$(python3 -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" | python3 -m json.tool实用公式模板:
- 精确匹配:
{Email}='user@example.com' - 包含匹配:
FIND('bug', LOWER({Title})) - 多条件:
AND({Status}='Todo', {Priority}='High') - 或条件:
OR({Owner}='alice', {Owner}='bob') - 非空:
NOT({Assignee}='') - 日期比较:
IS_AFTER({Due}, TODAY())
Sort + select specific fields
排序并选择特定字段
bash
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" | python3 -m json.toolSquare brackets in query params MUST be URL-encoded ( / ).
%5B%5Dbash
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" | python3 -m json.tool查询参数中的方括号必须进行URL编码( / )。
%5B%5DUse a named view
使用命名视图
bash
curl -s "https://api.airtable.com/v0/$BASE_ID/$TABLE?view=Grid%20view&maxRecords=50" \
-H "Authorization: Bearer $AIRTABLE_API_KEY" | python3 -m json.toolViews apply their saved filter + sort server-side.
bash
curl -s "https://api.airtable.com/v0/$BASE_ID/$TABLE?view=Grid%20view&maxRecords=50" \
-H "Authorization: Bearer $AIRTABLE_API_KEY" | python3 -m json.tool视图会在服务器端应用已保存的筛选和排序规则。
Common Mutations
常见修改操作
Create a record
创建记录
bash
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"}}' | python3 -m json.toolbash
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"}}' | python3 -m json.toolCreate up to 10 records in one call
单次调用创建最多10条记录
bash
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"}}
]
}' | python3 -m json.toolBatch endpoints are capped at 10 records per request. For larger inserts, loop in batches of 10 with a short sleep to respect 5 req/sec/base.
bash
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"}}
]
}' | python3 -m json.tool批量接口的上限为每次请求10条记录。如需插入更多记录,请按10条为一批进行循环,并在每批之间短暂休眠,以遵守每个Base每秒5次请求的限制。
Update a record (PATCH — merges, preserves unchanged fields)
更新记录(PATCH —— 合并更新,保留未修改字段)
bash
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"}}' | python3 -m json.toolbash
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"}}' | python3 -m json.toolUpsert by a merge field (no ID needed)
通过合并字段进行Upsert(无需记录ID)
bash
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": "user@example.com", "Status": "Active"}}
]
}' | python3 -m json.toolperformUpsertbash
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": "user@example.com", "Status": "Active"}}
]
}' | python3 -m json.toolperformUpsertDelete a record
删除记录
bash
curl -s -X DELETE "https://api.airtable.com/v0/$BASE_ID/$TABLE/$RECORD_ID" \
-H "Authorization: Bearer $AIRTABLE_API_KEY" | python3 -m json.toolbash
curl -s -X DELETE "https://api.airtable.com/v0/$BASE_ID/$TABLE/$RECORD_ID" \
-H "Authorization: Bearer $AIRTABLE_API_KEY" | python3 -m json.toolDelete up to 10 records in one call
单次调用删除最多10条记录
bash
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" | python3 -m json.toolbash
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" | python3 -m json.toolPagination
分页
List endpoints return at most 100 records per page. If the response includes , pass it back on the next call. Loop until the field is absent:
"offset": "..."bash
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" | python3 -c 'import json,sys; d=json.load(sys.stdin); [print(r["id"], r["fields"].get("Name","")) for r in d["records"]]'
OFFSET=$(echo "$RESP" | python3 -c 'import json,sys; d=json.load(sys.stdin); print(d.get("offset",""))')
[ -z "$OFFSET" ] && break
done列表接口每页最多返回100条记录。如果响应中包含,请在下一次调用时传入该参数。循环调用直到该字段不存在:
"offset": "..."bash
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" | python3 -c 'import json,sys; d=json.load(sys.stdin); [print(r["id"], r["fields"].get("Name","")) for r in d["records"]]'
OFFSET=$(echo "$RESP" | python3 -c 'import json,sys; d=json.load(sys.stdin); print(d.get("offset",""))')
[ -z "$OFFSET" ] && break
doneTypical Hermes Workflow
典型Hermes工作流
- Confirm auth. — expect
curl -s -o /dev/null -w "%{http_code}\n" https://api.airtable.com/v0/meta/bases -H "Authorization: Bearer $AIRTABLE_API_KEY".200 - Find the base. List bases (step above) OR ask the user for the ID directly if the token lacks
app....schema.bases:read - Inspect the schema. — cache the exact field names and primary-field name locally in the session before mutating anything.
GET /v0/meta/bases/$BASE_ID/tables - Read before you write. For "update X where Y", first to resolve the
filterByFormulaID, thenrec.... Never guess record IDs.PATCH /v0/$BASE_ID/$TABLE/$RECORD_ID - Batch writes. Combine related creates into one 10-record POST to stay under the 5 req/sec budget.
- Destructive ops. Deletions can't be undone via API. If the user says "delete all Xs", echo back the filter + record count and confirm before firing.
- 确认认证有效性。执行—— 预期返回
curl -s -o /dev/null -w "%{http_code}\n" https://api.airtable.com/v0/meta/bases -H "Authorization: Bearer $AIRTABLE_API_KEY"。200 - 找到目标Base。列出Bases(如上一步),或者如果令牌缺少权限,直接向用户询问
schema.bases:read格式的Base ID。app... - 检查Schema。调用—— 在执行任何修改操作前,在会话本地缓存字段的准确名称和主键字段名称。
GET /v0/meta/bases/$BASE_ID/tables - 先读取再写入。对于“更新满足Y条件的X记录”操作,先通过解析出
filterByFormula格式的记录ID,再调用rec...。切勿猜测记录ID。PATCH /v0/$BASE_ID/$TABLE/$RECORD_ID - 批量写入。将相关的创建操作合并为单次10条记录的POST请求,以控制在每秒5次请求的限制内。
- 破坏性操作。通过API删除的记录无法恢复。如果用户要求“删除所有X记录”,请先返回筛选条件和记录数量,确认后再执行操作。
Pitfalls
常见陷阱
- MUST be URL-encoded. Field names with spaces or non-ASCII also need encoding (
filterByFormula→{My Field}). Use Python stdlib (pattern above) — never hand-escape.%7BMy%20Field%7D - Empty fields are omitted from responses. A missing key doesn't mean the field doesn't exist — it means this record's value is empty. Check the schema (step 3) before concluding a field is missing.
"Assignee" - PATCH vs PUT. merges supplied fields into the record.
PATCHreplaces the record entirely and clears any field you didn't include. Default toPUT.PATCH - Single-select options must exist. Writing when
"Status": "Shipping"isn't in the field's option list errors withShippingunless you passINVALID_MULTIPLE_CHOICE_OPTIONS(which auto-creates the option)."typecast": true - Per-base token scoping. A on one base while another works means the token's Access list doesn't include that base — not a scope or auth issue. Send the user to https://airtable.com/create/tokens to grant it.
403 - Rate limits are per base, not per token. 5 req/sec on and 5 req/sec on
baseAis fine; 6 req/sec onbaseBalone will throttle. Monitor thebaseAheader onRetry-After.429
- 必须进行URL编码。包含空格或非ASCII字符的字段名称也需要编码(例如
filterByFormula→{My Field})。请使用Python标准库(如上模板)——切勿手动转义。%7BMy%20Field%7D - 空字段会从响应中省略。响应中缺少键并不意味着该字段不存在——而是表示该记录的此字段值为空。在判断字段是否存在前,请先检查Schema(步骤3)。
"Assignee" - PATCH与PUT的区别。会将提供的字段合并到记录中。
PATCH会完全替换记录,并清空所有未包含的字段。默认优先使用PUT。PATCH - 单选选项必须已存在。如果字段选项列表中没有,写入
Shipping会返回"Status": "Shipping"错误,除非设置INVALID_MULTIPLE_CHOICE_OPTIONS(会自动创建该选项)。"typecast": true - 令牌按Base划分权限。在一个Base上返回错误但在另一个Base上正常,说明令牌的访问列表未包含该Base——并非权限范围或认证问题。请引导用户前往https://airtable.com/create/tokens授予访问权限。
403 - 速率限制是按Base而非令牌计算。在每秒5次请求、
baseA每秒5次请求是允许的;但仅在baseB上每秒6次请求会被限流。收到baseA响应时,请查看429头信息。Retry-After
Important Notes for Hermes
Hermes相关重要提示
- Always use the tool with
terminal. Do NOT usecurl(it can't send auth headers) orweb_extract(needs UI auth and is slow).browser_navigate - flows from
AIRTABLE_API_KEYinto the subprocess automatically when this skill is loaded — no need to re-export it before each~/.hermes/.envcall.curl - Escape curly braces in formulas carefully. In a heredoc body, is literal. In a shell argument,
{Status}is safe outside{Status}brace-expansion context — but pass dynamic strings through{...}before splicing into a URL.python3 urllib.parse.quote - Pretty-print with (always present) rather than
python3 -m json.tool(optional). Only reach forjqwhen you need filtering/projection.jq - Pagination is per-page, not global. Airtable's 100-record cap is a hard limit; there is no way to bump it. Loop with until the field is absent.
offset - Read the array on non-2xx responses — Airtable returns structured error codes like
errors,AUTHENTICATION_REQUIRED,INVALID_PERMISSIONS,MODEL_ID_NOT_FOUNDthat tell you exactly what's wrong.INVALID_MULTIPLE_CHOICE_OPTIONS
- 始终使用工具配合
terminal。请勿使用curl(无法发送认证头)或web_extract(需要UI认证且速度慢)。browser_navigate - 加载此技能时,会自动从
AIRTABLE_API_KEY传入子进程——无需在每次~/.hermes/.env调用前重新导出该变量。curl - 注意公式中花括号的转义。在 heredoc 体中,是字面量。在Shell参数中,
{Status}在{Status}括号扩展上下文之外是安全的——但在将动态字符串拼接到URL前,请通过{...}处理。python3 urllib.parse.quote - 使用格式化输出(系统默认自带),而非
python3 -m json.tool(可选安装)。仅在需要筛选/投影时使用jq。jq - 分页是按页而非全局限制。Airtable的100条记录上限是硬性限制;无法调整。请循环传入直到该字段不存在。
offset - 查看非2xx响应中的数组。Airtable会返回结构化错误码,例如
errors、AUTHENTICATION_REQUIRED、INVALID_PERMISSIONS、MODEL_ID_NOT_FOUND,可明确告知问题所在。INVALID_MULTIPLE_CHOICE_OPTIONS