Skip to main content
Business & GrowthCrm Services176 lines

Airtable

Integrate with Airtable API for managing bases, tables, records, views,

Quick Summary26 lines
You are an expert in Airtable API integration using the official `airtable` npm package and Airtable's REST API. You build data-driven workflows that leverage Airtable as a flexible structured database with rich field types, views, and formula support.

## Key Points

- **Writing to computed fields**: Formula, rollup, count, and lookup fields reject writes silently or error. Always verify field types via the metadata endpoint.
- **Exceeding 10 records per batch**: The API hard-caps at 10 records per create/update/delete. Sending more causes a 422 error.
- **Not URL-encoding formula strings**: Special characters in `filterByFormula` must be encoded. Use the SDK's built-in query builder instead of raw URLs.
- **Storing large blobs in attachment fields**: Attachment URLs expire after a few hours. Never cache attachment URLs; always re-fetch them before use.
- Building lightweight CRM or project tracker backends without a dedicated database
- Syncing structured data between Airtable and external systems bidirectionally
- Automating record creation from form submissions, emails, or webhook events
- Generating reports by aggregating Airtable data with formula and rollup fields
- Prototyping data models that business users can edit directly in the Airtable UI

## Quick Example

```bash
npm install airtable
```

```
AIRTABLE_API_KEY=patXXXXXXXXXXXXXX
AIRTABLE_BASE_ID=appXXXXXXXXXXXXXX
```
skilldb get crm-services-skills/AirtableFull skill: 176 lines
Paste into your CLAUDE.md or agent config

Airtable API Integration

You are an expert in Airtable API integration using the official airtable npm package and Airtable's REST API. You build data-driven workflows that leverage Airtable as a flexible structured database with rich field types, views, and formula support.

Core Philosophy

Record-Oriented Thinking

Airtable records have a 100KB size limit and tables cap at 100,000 records on paid plans. Design your schema to keep records lean. Use linked records instead of duplicating data across tables.

Batch Everything

The Airtable API processes a maximum of 10 records per create/update/delete call. Always chunk your operations into batches of 10 and respect the 5 requests/second rate limit with appropriate throttling.

Formula Fields Are Read-Only

Formula, rollup, count, and lookup fields are computed server-side. Never attempt to write to them. Always check field type via the metadata API before performing writes.

Setup

npm install airtable
AIRTABLE_API_KEY=patXXXXXXXXXXXXXX
AIRTABLE_BASE_ID=appXXXXXXXXXXXXXX

Initialize the client:

import Airtable from "airtable";

const base = new Airtable({ apiKey: process.env.AIRTABLE_API_KEY }).base(
  process.env.AIRTABLE_BASE_ID!
);

Key Patterns

Do: Batch creates in groups of 10 with rate limiting

async function batchCreate(
  tableName: string,
  records: Record<string, unknown>[]
) {
  const results = [];
  for (let i = 0; i < records.length; i += 10) {
    const chunk = records.slice(i, i + 10).map((fields) => ({ fields }));
    const created = await base(tableName).create(chunk);
    results.push(...created);
    if (i + 10 < records.length) await sleep(200);
  }
  return results;
}
const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));

Not: Creating records one at a time

// WRONG - 1 API call per record, hits rate limits immediately
for (const record of records) {
  await base("Tasks").create(record);
}

Do: Use filterByFormula for server-side filtering

const activeRecords = await base("Projects")
  .select({
    filterByFormula: `AND({Status} = 'Active', {Priority} >= 3)`,
    fields: ["Name", "Status", "Priority", "Owner"],
    sort: [{ field: "Priority", direction: "desc" }],
  })
  .all();

Common Patterns

Paginated Record Fetch

async function fetchAllRecords(
  tableName: string,
  fields?: string[],
  formula?: string
) {
  const allRecords: Airtable.Record<any>[] = [];
  const query: Record<string, unknown> = {};
  if (fields) query.fields = fields;
  if (formula) query.filterByFormula = formula;

  await base(tableName)
    .select(query)
    .eachPage((records, fetchNextPage) => {
      allRecords.push(...records);
      fetchNextPage();
    });
  return allRecords;
}

Upsert by Unique Field

async function upsertRecord(
  tableName: string,
  uniqueField: string,
  uniqueValue: string,
  fields: Record<string, unknown>
) {
  const existing = await base(tableName)
    .select({
      filterByFormula: `{${uniqueField}} = '${uniqueValue}'`,
      maxRecords: 1,
    })
    .firstPage();

  if (existing.length > 0) {
    return base(tableName).update(existing[0].id, fields);
  }
  return base(tableName).create({ [uniqueField]: uniqueValue, ...fields });
}

Batch Update Records

async function batchUpdate(
  tableName: string,
  updates: { id: string; fields: Record<string, unknown> }[]
) {
  const results = [];
  for (let i = 0; i < updates.length; i += 10) {
    const chunk = updates.slice(i, i + 10);
    const updated = await base(tableName).update(chunk);
    results.push(...updated);
    if (i + 10 < updates.length) await sleep(200);
  }
  return results;
}

Fetch Table Metadata

async function getTableSchema(baseId: string, tableIdOrName: string) {
  const response = await fetch(
    `https://api.airtable.com/v0/meta/bases/${baseId}/tables`,
    { headers: { Authorization: `Bearer ${process.env.AIRTABLE_API_KEY}` } }
  );
  const { tables } = await response.json();
  return tables.find(
    (t: { id: string; name: string }) =>
      t.id === tableIdOrName || t.name === tableIdOrName
  );
}

Anti-Patterns

  • Writing to computed fields: Formula, rollup, count, and lookup fields reject writes silently or error. Always verify field types via the metadata endpoint.
  • Exceeding 10 records per batch: The API hard-caps at 10 records per create/update/delete. Sending more causes a 422 error.
  • Not URL-encoding formula strings: Special characters in filterByFormula must be encoded. Use the SDK's built-in query builder instead of raw URLs.
  • Storing large blobs in attachment fields: Attachment URLs expire after a few hours. Never cache attachment URLs; always re-fetch them before use.

When to Use

  • Building lightweight CRM or project tracker backends without a dedicated database
  • Syncing structured data between Airtable and external systems bidirectionally
  • Automating record creation from form submissions, emails, or webhook events
  • Generating reports by aggregating Airtable data with formula and rollup fields
  • Prototyping data models that business users can edit directly in the Airtable UI

Install this skill directly: skilldb add crm-services-skills

Get CLI access →