Watch our latest video available on Youtube.
Tutorials/Tutorial

How to Use the Airtable API: A Beginner's Guide

The Airtable REST API turns your base into a programmable database — anything you can do in the Airtable UI, you can do from code. This guide walks through it from scratch: getting a personal access token, making your first request, reading and writing records, handling pagination and rate limits, and the production patterns (upserts, batching, error handling) that separate a hobby script from something you can rely on.

Beginner18 min readAug 31, 2026

The Airtable REST API turns a base into a programmable database. Anything the UI can do — read, search, filter, create, update, delete, manage attachments — the API does too. Learning the API is the bridge from "Airtable is a tool I click in" to "Airtable is a backend my code can use."

This guide walks through the API from zero. By the end you'll have authenticated, made requests, handled pagination and rate limits, and written code that's safe to run in production. All examples use plain curl for clarity; production code will use a client library (covered at the end).

Step 1: Get a Personal Access Token

Airtable replaced legacy API keys with Personal Access Tokens (PATs) in 2024. PATs are scoped — they specify which bases they can access and what they can do.

  1. Go to airtable.com/create/tokens.
  2. Click Create new token.
  3. Name it (e.g. "My first API token").
  4. Add scopes:
    • data.records:read — read records.
    • data.records:write — create, update, delete records.
    • schema.bases:read — read base/table/field metadata.
  5. Add base access — pick the specific bases the token can access. Avoid all-workspace access unless you really need it.
  6. Click Create token.
  7. Copy the token immediately. Airtable shows it once and never again.

For a deeper walkthrough including OAuth alternatives, see our PAT guide.

Step 2: Find Your Base ID

Every Airtable base has a unique ID starting with app. Two ways to find it:

  1. Open the base in Airtable. The URL looks like https://airtable.com/appXXXXXXXXXXXXXX/tblYYYYYYYYYYYYYY/.... The app... portion is your Base ID.
  2. Go to airtable.com/api, pick the base, and the docs page shows the Base ID prominently.

The same docs page shows every table's ID and field IDs — useful reference material.

Step 3: Make Your First Request

The base URL for the Airtable API is:

https://api.airtable.com/v0/{baseId}/{tableIdOrName}

Substitute your Base ID and table name (URL-encoded if it contains spaces):

curl https://api.airtable.com/v0/appXXXXXXXXXXXXXX/Tasks \
  -H "Authorization: Bearer pat_xxxxxxxxxxxxx"

You'll get back JSON like:

{
  "records": [
    {
      "id": "recAAAAAAAAAAAAAA",
      "createdTime": "2026-04-15T10:30:00.000Z",
      "fields": {
        "Name": "Set up onboarding",
        "Status": "Done",
        "Due Date": "2026-04-20"
      }
    }
  ]
}

Each record has an id (the unique record ID, used for updates and links), a createdTime, and fields containing the actual field values.

Step 4: Filtering and Sorting

The API supports several query parameters:

ParameterPurpose
viewScope results to a specific view
filterByFormulaFilter with an Airtable formula
sort[0][field] and sort[0][direction]Order results
fields[]Return only specific fields
maxRecordsCap total records returned
pageSizeRecords per page (default 100, max 100)

Example: get only open high-priority tasks, sorted by due date:

curl -G "https://api.airtable.com/v0/appXXXX/Tasks" \
  -H "Authorization: Bearer pat_xxxx" \
  --data-urlencode "filterByFormula=AND({Status}='Open', {Priority}='High')" \
  --data-urlencode "sort[0][field]=Due Date" \
  --data-urlencode "sort[0][direction]=asc"

filterByFormula accepts any Airtable formula that returns true/false per record.

Step 5: Pagination

The API returns up to 100 records per request. When more exist, the response includes an offset cursor:

{
  "records": [...],
  "offset": "itrXXXX/recXXXX"
}

To get the next page, repeat the request with offset=...:

curl -G "https://api.airtable.com/v0/appXXXX/Tasks" \
  -H "Authorization: Bearer pat_xxxx" \
  --data-urlencode "offset=itrXXXX/recXXXX"

Loop until the response no longer includes offset. Always paginate — never assume one request returns the whole table.

Step 6: Creating Records

POST to /v0/{baseId}/{tableId} with a JSON body:

curl -X POST https://api.airtable.com/v0/appXXXX/Tasks \
  -H "Authorization: Bearer pat_xxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "records": [
      {"fields": {"Name": "New task", "Status": "Open", "Priority": "High"}}
    ]
  }'

Up to 10 records per request — batch when creating many.

Common field type formats:

Field TypeJSON Format
Single line / Long text"text value"
Number / Currency / Percent123.45
Date"2026-06-15"
Date and time"2026-06-15T14:00:00.000Z"
Single select"Option Name"
Multi-select["Option 1", "Option 2"]
Linked record["recXXXX", "recYYYY"] (array of IDs)
Checkboxtrue or false
Attachments[{"url": "https://..."}]

Step 7: Updating Records

Two methods:

  • PATCH — update specified fields only (partial update).
  • PUT — replace all fields (anything not specified is cleared).

PATCH is what you want 99% of the time:

curl -X PATCH https://api.airtable.com/v0/appXXXX/Tasks \
  -H "Authorization: Bearer pat_xxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "records": [
      {"id": "recAAAA", "fields": {"Status": "Done"}}
    ]
  }'

Up to 10 records per request, same as create.

Step 8: Deleting Records

DELETE with record IDs as query parameters:

curl -X DELETE "https://api.airtable.com/v0/appXXXX/Tasks?records[]=recAAAA&records[]=recBBBB" \
  -H "Authorization: Bearer pat_xxxx"

Up to 10 deletions per request.

Step 9: Upsert (Create or Update)

The API supports upserts via a single POST request:

curl -X POST https://api.airtable.com/v0/appXXXX/Tasks \
  -H "Authorization: Bearer pat_xxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "performUpsert": {
      "fieldsToMergeOn": ["External ID"]
    },
    "records": [
      {"fields": {"External ID": "ABC123", "Name": "Hello"}}
    ]
  }'

If a record exists with External ID = "ABC123", it's updated. If not, it's created. Use this pattern for sync scripts where you don't want to write your own "search then create or update" logic.

Step 10: Rate Limits and Production Patterns

The hard limits:

  • 5 requests per second per base. Exceed and get a 429 with a 30-second cooldown.
  • 10 records per request for create/update/delete.
  • 100 records per request for list/search.

For batch operations:

import time

for batch in chunks(records, 10):
    response = create_records(batch)
    time.sleep(0.25)  # 4 req/sec safe margin

For high-volume sync, consider:

  • Webhooks API for change-data-capture instead of polling.
  • Caching — keep a local copy of slowly-changing reference data.
  • Enterprise tier — higher rate limits available on request.

Step 11: Use a Client Library

Curl works for learning. For production, use an official or community client library:

LanguageLibrary
JavaScript / TypeScriptairtable (official)
Pythonpyairtable (community, well-maintained)
Gomehanizm/airtable
Rubyairrecord

Example with pyairtable:

from pyairtable import Api

api = Api('pat_xxxx')
table = api.table('appXXXX', 'Tasks')

# List, with auto-pagination
records = table.all(formula="{Status}='Open'")

# Create
table.create({'Name': 'New task', 'Status': 'Open'})

# Update
table.update('recAAAA', {'Status': 'Done'})

# Upsert
table.batch_upsert(records, key_fields=['External ID'])

For Python specifically, see our Airtable with Python guide.

Common Mistakes

Mistake 1: Hardcoding PATs in code. Use environment variables. Commit a token to GitHub and it ends up in attack scripts within hours.

Mistake 2: Single requests for batch operations. 100 individual creates take 100 requests + 25 seconds of rate-limit delay. One batched call per 10 records is 90% faster.

Mistake 3: Not handling 429 errors. Production code must catch 429 and back off. Sleep 30 seconds, retry, escalate if it persists.

Mistake 4: Filtering client-side. Pulling 50,000 records to filter them in code wastes bandwidth and burns rate limit. Use filterByFormula server-side.

Mistake 5: Using table names instead of IDs. Names break if anyone renames the table. Use the tbl... ID for stable references.

Troubleshooting

401 Unauthorized. PAT is wrong, expired, or doesn't have access to the base. Confirm via the Airtable API docs page for the base.

403 Forbidden. PAT is valid but lacks the required scope. Add data.records:write if you're getting this on a POST/PATCH.

404 Not Found. Base ID or table ID/name is wrong. Double-check both.

422 Unprocessable Entity. Field values don't match the schema. Most often: passing a string where a number is expected, or an unknown single-select option.

429 Too Many Requests. Rate limited. Back off 30 seconds, then retry. Reduce request frequency or batch more aggressively.

Next Steps

The API is the foundation for any serious Airtable integration. Once you're comfortable with CRUD operations and pagination, the natural next steps are: building a sync script between Airtable and another system, integrating Airtable into your product backend, and writing scripts that automate data cleanup and reporting tasks.

For deeper dives, see our finding Airtable IDs guide, Python guide, scripting guide, and webhooks guide. For production integrations that need to be reliable at scale, get in touch.

Frequently Asked Questions

Common questions about this tutorial.

Ready to Transform Your Business Operations?

Join 100+ companies that have automated their way to success. Get started today and see the difference.