Connect Bubble to Airtable by adding the free API Connector plugin and pointing it at the Airtable REST API with a Personal Access Token in a Private header. Airtable's 5 requests-per-second rate limit applies across all your app's simultaneous users — cache data in Bubble's database for any page with more than a handful of concurrent visitors rather than fetching live per user.
| Fact | Value |
|---|---|
| Tool | Airtable |
| Category | Database & Backend |
| Method | Bubble API Connector |
| Difficulty | Beginner |
| Time required | 1–2 hours |
| Last updated | July 2026 |
Bubble + Airtable: a polished frontend on top of your team's data
Airtable and Bubble are complementary tools that serve opposite ends of a common workflow. Your operations team, marketing team, or product team maintains records in Airtable because the grid interface is familiar and requires no technical knowledge — they can add rows, edit fields, attach files, and manage views without involving a developer. Bubble, on the other hand, renders polished, interactive user interfaces that your customers or internal users interact with. Connecting the two means your non-technical team controls the data, and Bubble controls the experience. The integration is simpler than almost any other Bubble backend connection: Airtable has a public REST API that needs only a Personal Access Token in an Authorization header. There is no plugin to buy, no server to deploy, no SDK to initialize. You configure the API Connector once with your base ID and PAT, define API calls for each table operation, and start binding Airtable data to repeating groups and form submissions. The one critical operational concern is rate limiting: Airtable allows only 5 API requests per second per base, counting requests from all your Bubble app's users combined. A page that fires one Airtable API call when it loads will hit the rate limit if 6 or more users open that page at the same second. The solution is data caching: use a Bubble scheduled workflow to sync Airtable data into Bubble's own database every few minutes, and serve most users from Bubble's fast native database rather than live Airtable API calls. Reserve live Airtable calls for actions where fresh data is essential — form submissions, confirmations, write-backs.
Integration method
The Bubble API Connector (free plugin by Bubble) calls the Airtable REST API at `https://api.airtable.com/v0/{baseId}` with a Personal Access Token in a Private `Authorization` header — no separate plugin or proxy needed.
Prerequisites
- An Airtable account with at least one base containing the data you want to display or write to in Bubble
- A Bubble app (any plan — read-only API calls work on the free plan; write operations via Backend Workflows require Starter plan or above)
- The free API Connector plugin by Bubble installed in your app (Plugins → Add plugins → search 'API Connector')
- Your Airtable Base ID from the base URL (e.g. `airtable.com/appXXXXXXXXXXXXXX/...` — the segment starting with 'app')
- An Airtable Personal Access Token scoped to the relevant base with at least `data.records:read` scope (add `data.records:write` if Bubble will create or update records)
Step-by-step guide
Step 1 — Create an Airtable Personal Access Token
Log in to your Airtable account at airtable.com. In the top-right corner, click your profile avatar and select 'Developer hub,' or navigate directly to airtable.com/create/tokens. Click 'Create token.' Give it a descriptive name like 'Bubble Integration — [Your App Name].' Under 'Scopes,' add `data.records:read` if you only need to display Airtable data in Bubble. If Bubble will also create or update records (for example, a form that writes leads to Airtable), also add `data.records:write`. Under 'Access,' click 'Add a base' and select the specific Airtable base you want to connect — scoping a PAT to only the bases it needs follows the principle of least privilege. Click 'Create token.' Copy the token immediately and save it somewhere secure (a password manager). Airtable shows the token only once — if you lose it, you will need to create a new one. Also note your Base ID: open your Airtable base in the browser and look at the URL, which looks like `airtable.com/appXXXXXXXXXXXXXX/tblXXXXXXXXXXXXXX/viwXXXXXXXXXXXXXX`. The segment starting with 'app' is your Base ID.
1// Airtable Personal Access Token scope reference2// Navigate to: airtable.com/create/tokens34// Minimum scope for read-only Bubble integration:5// data.records:read67// Add these for write operations from Bubble:8// data.records:write910// Your Base ID is in the URL when viewing your base:11// https://airtable.com/appXXXXXXXXXXXXXX/tblYYYYYYYYYYYYYY/viwZZZZZZZZZZZZZZ12// ^^^^^^^^^^^^^^^^^^^13// This is your baseId1415// API base URL pattern:16// https://api.airtable.com/v0/{baseId}/{tableName}Pro tip: Create separate tokens for read and write operations. The read-only token (data.records:read only) can be used in client-side Bubble workflows with lower risk if it is ever exposed, because it cannot create or modify records. The write token (data.records:write) should be used only in Bubble Backend Workflows with a Private header. This two-token approach limits blast radius if either token is compromised.
Expected result: You have an Airtable Personal Access Token copied and saved. You have noted your Base ID (starts with 'app') from the Airtable URL. The token appears as a long string beginning with 'pat'.
Step 2 — Install the API Connector plugin and configure the Airtable API group
Open your Bubble app editor and click 'Plugins' in the left sidebar. If the API Connector plugin by Bubble does not appear in your installed plugins list, click 'Add plugins,' search for 'API Connector,' and install it (it is free). Return to the Plugins tab and click 'API Connector.' You will see an 'Add another API' button — click it. Name this API group 'Airtable.' In the 'Root URL' field, enter your Airtable base's root endpoint: `https://api.airtable.com/v0/appXXXXXXXXXXXXXX` (replace with your actual Base ID). Under 'Shared headers across all calls,' click 'Add a shared header.' For the key, type `Authorization`. For the value, type `Bearer ` followed by your Personal Access Token (for example: `Bearer patXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX`). Check the 'Private' checkbox next to this header — this is critical. It ensures the PAT is used only server-side on Bubble's servers and never sent to a visitor's browser. Add a second shared header: key `Content-Type`, value `application/json` (no Private checkbox needed). Your API group is now configured. All API calls you add under this group will automatically use these headers for every request.
1// Bubble API Connector — Airtable group configuration2{3 "api_group_name": "Airtable",4 "root_url": "https://api.airtable.com/v0/appXXXXXXXXXXXXXX",5 "shared_headers": [6 {7 "key": "Authorization",8 "value": "Bearer patXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",9 "private": true10 },11 {12 "key": "Content-Type",13 "value": "application/json"14 }15 ]16}Pro tip: The root URL includes your Base ID — this means all calls in this API group target the same Airtable base. If you need to connect to multiple Airtable bases, create separate API groups in the API Connector, each with its own root URL and PAT. You can name them 'Airtable — [Base Name]' to distinguish them.
Expected result: The Airtable API group appears in your API Connector with the correct root URL and two shared headers (Authorization marked Private, Content-Type). No individual API calls have been added yet.
Step 3 — Add the 'List Records' API call and initialize it
Inside the Airtable API group you just created, click 'Add call.' Name this call 'List [TableName] Records' (replace TableName with your actual Airtable table name, for example 'List Products Records'). Set the method to GET. In the endpoint URL field, type `/{tableName}` — replacing tableName with the exact name of your Airtable table as it appears in Airtable (case-sensitive, spaces are URL-encoded automatically by Bubble, for example `/Products` or `/Blog Posts`). Add URL parameters: click 'Add parameter' and add `maxRecords` with an example value of `100` (the API maximum per page; leave the value flexible so you can pass it from workflows). Optionally add `filterByFormula` (example: `{Status}='Published'`) and `sort[0][field]` with `sort[0][direction]` for sorting. Leave 'Use as' set to 'Data' — this tells Bubble the response should be mappable to a list data type. Now, critically: before clicking 'Initialize call,' make sure your Airtable table has at least one record with real data. Airtable returns an empty `records: []` array for empty tables, and Bubble cannot detect field names from an empty array — it will show no fields to map. With a record in the table, click 'Initialize call.' Bubble fires a real GET request to Airtable using your PAT. If the PAT is valid and the base ID is correct, Airtable returns the first page of records and Bubble displays the detected fields (id, createdTime, fields.FieldName, etc.). Click 'Save.'
1// GET call: List records from an Airtable table2// Endpoint (appended to root URL): /{tableName}3// Full URL example: GET https://api.airtable.com/v0/appXXXXX/Products45// Query parameters:6{7 "maxRecords": "100",8 "filterByFormula": "{Status}='Published'",9 "sort[0][field]": "Name",10 "sort[0][direction]": "asc",11 "fields[]": "Name", // optional: request only specific fields12 "offset": "<dynamic>" // for pagination (from previous page's response)13}1415// Airtable response structure:16{17 "records": [18 {19 "id": "recXXXXXXXXXXXXXX",20 "createdTime": "2024-01-15T10:30:00.000Z",21 "fields": {22 "Name": "Product A",23 "Status": "Published",24 "Price": 49.99,25 "Category": "Electronics"26 }27 }28 ],29 "offset": "recYYYYYYYYYYYYYY" // present only if more pages exist30}Pro tip: Airtable column names are case-sensitive and must match exactly in `filterByFormula` expressions. If your column is named 'Product Name' (with a space), use `{Product Name}` in the formula. Spaces in column names are fine — Airtable handles them in curly-brace filter syntax. In Bubble's API response mapping, fields with spaces in their names appear as-is and Bubble handles them correctly.
Expected result: The 'List [TableName] Records' call is initialized with a green checkmark. Bubble shows detected fields including `records body list of record`, and within each record the `fields` object with your Airtable column names. The call is set to 'Use as: Data.'
Step 4 — Add Create and Update API calls for writing data to Airtable
If your Bubble app needs to write data back to Airtable (from form submissions, button clicks, or workflow triggers), add two more API calls in the Airtable group. First, click 'Add call' and name it 'Create [TableName] Record.' Set method to POST, endpoint to `/{tableName}`. In the Body section, set type to JSON and add the body structure: `{"fields": {"FieldName": "{{value}}", "AnotherField": "{{otherValue}}"}}`. Replace FieldName and AnotherField with your actual Airtable column names — they must match exactly. The values in double curly braces are dynamic placeholders that Bubble will fill in from workflow inputs. Set 'Use as' to 'Action' (since this call creates a record, not fetches data). Second, add another call named 'Update [TableName] Record.' Set method to PATCH, endpoint to `/{tableName}/{recordId}` (add `recordId` as a URL parameter). Body: `{"fields": {"FieldName": "{{newValue}}"}}`. PATCH in Airtable updates only the fields you specify — unspecified fields keep their current values. Set 'Use as' to 'Action.' For the Initialize call on both POST and PATCH, you will need to provide real example values in the body parameters that match your field types — Airtable returns the created or updated record on success. Click 'Initialize call' to verify the response shape, then save.
1// POST call: Create a new record in Airtable2// Endpoint: /{tableName}3// Method: POST4// Use as: Action56// Request body:7{8 "fields": {9 "Name": "{{name}}",10 "Email": "{{email}}",11 "Status": "New",12 "Created Date": "{{created_date}}"13 }14}1516// Airtable response on success (200):17{18 "id": "recXXXXXXXXXXXXXX",19 "createdTime": "2024-01-15T10:30:00.000Z",20 "fields": {21 "Name": "John Doe",22 "Email": "john@example.com",23 "Status": "New",24 "Created Date": "2024-01-15"25 }26}2728// PATCH call: Update specific fields on an existing record29// Endpoint: /{tableName}/{recordId}30// Method: PATCH31// Use as: Action3233// Request body (only include fields to update):34{35 "fields": {36 "Status": "{{new_status}}",37 "Updated Date": "{{updated_date}}"38 }39}Pro tip: Airtable date fields expect ISO 8601 format (YYYY-MM-DD or YYYY-MM-DDTHH:mm:ss.sssZ). In Bubble workflows, use the ':formatted as' operator on date fields to format them as ISO 8601 before passing to the API call. Number fields must be actual numbers, not strings — Airtable will return a 422 Unprocessable Entity error if you pass '49.99' (string) instead of 49.99 (number).
Expected result: The Airtable API group now has three calls: List Records (GET, Use as Data), Create Record (POST, Use as Action), and Update Record (PATCH, Use as Action). All three are initialized with green checkmarks.
Step 5 — Bind Airtable data to a Bubble repeating group
With your API calls initialized, you can now display Airtable records in Bubble's UI. In the Bubble editor, drag a Repeating Group element onto your page. In the Repeating Group's settings panel, set 'Type of content' to 'Airtable List [TableName] Records record' (or similar — Bubble names the type based on the API call name). Set 'Data source' to 'Get data from an external API' → select 'Airtable - List [TableName] Records.' Pass any parameters Bubble asks for (like maxRecords or filterByFormula). Inside the Repeating Group's first cell, add text elements, image elements, and other UI components. To bind an element's value to an Airtable field: click the text element → click 'Insert dynamic data' → 'Current cell's Airtable List [TableName] Records record's fields Name' (or whatever field you need). Airtable field names with spaces appear in the dropdown as-is. If a field does not appear in the dropdown, re-initialize the API call after ensuring that field has a value in at least one record. Repeat this for each field you want to display. To implement a filtered view, add a dropdown or search input on the page. Set the Repeating Group's data source to include the filter: in the 'filterByFormula' parameter, reference the dropdown's value using Bubble's dynamic expression builder. The Repeating Group will re-query Airtable when the filter value changes.
1// Bubble Repeating Group setup (conceptual, configured in Bubble editor)2// Type of content: Airtable - List Products Records - record3// Data source: Get data from an external API4// API: Airtable5// Call: List Products Records6// Parameters:7// maxRecords: 1008// filterByFormula: AND({Status}='Published', {Category}=Input Category Filter's value)9// sort[0][field]: Name10// sort[0][direction]: asc1112// Inside repeating group first cell:13// Text element: Current cell's record's fields Name14// Text element: Current cell's record's fields Price :formatted as $#,###.##15// Image element: Current cell's record's fields Image URL16// Button: On click → Call Airtable 'Update Products Record'17// recordId: Current cell's record's id18// new_status: 'Archived'Pro tip: The Airtable API returns a maximum of 100 records per page. If your table has more than 100 records, you need to implement pagination using the `offset` field returned in each response. The offset is an opaque string — pass it as the `offset` parameter in the next API call to get the next page. Bubble does not handle pagination automatically; you need to store the offset value in an app state and trigger subsequent API calls from a 'Load more' button.
Expected result: The Repeating Group on your Bubble page displays data from your Airtable base. Each row in the repeating group corresponds to an Airtable record. Bubble's UI previewer shows the records in real time when you run the app.
Step 6 — Implement a scheduled sync to handle rate limits at scale
Airtable's 5 requests-per-second rate limit applies across all users of your Bubble app simultaneously, not per user. If your page makes one Airtable API call on load and 6+ users open it at the same moment, you will hit the limit and receive HTTP 429 errors, which appear as empty repeating groups or silent failures in Bubble. The production-safe pattern is to cache Airtable data in Bubble's own database. Create a Bubble data type that mirrors your Airtable table structure (for example, a 'Product' data type with fields matching your Airtable columns, plus an 'airtable_record_id' text field to track the Airtable record ID). Enable Backend Workflows in your Bubble app (Starter plan or above required). Create a Backend Workflow named 'Sync Airtable Products.' Inside it, add an API call step: Airtable - List Products Records (with maxRecords=100 and filterByFormula for active records). For each record in the response, use 'Search for Products where airtable_record_id = [record id]': if found, update the existing Bubble Product; if not found, create a new one. Finally, schedule this Backend Workflow to run on a recurring schedule (every 15 or 30 minutes) using Bubble's 'Schedule API workflow (recurring)' action, triggered on page load or from an admin button. Once the sync runs, change your UI repeating groups to use 'Search for Products' from Bubble's database instead of the live Airtable API call. Users now get fast, database-speed responses, and Airtable is only called by the scheduled sync workflow — typically once every 15 minutes regardless of user count. If RapidDev's team can help you design this sync architecture for your specific Airtable schema, reach out for a free scoping call at rapidevelopers.com/contact.
1// Bubble Backend Workflow: 'Sync Airtable Products'2// Triggered: Recurring schedule (every 15 minutes)3// Requires: Starter plan (Backend Workflows)45// Step 1: Call Airtable API6// API call: Airtable - List Products Records7// Parameters: maxRecords=100, filterByFormula={Status}='Published'89// Step 2: For each record in Step 1's response records list10// (Use 'Schedule API workflow on a list' → 'Upsert Single Product' sub-workflow)1112// Sub-workflow 'Upsert Single Product' (parameter: record = one Airtable record):13// Search for Products where airtable_record_id = input record id14// If found (count > 0):15// Make changes to thing: update Product fields from input record fields16// If not found (count = 0):17// Create new thing: Product with airtable_record_id = input record id18// and all fields from input record fields1920// Result: Bubble database stays in sync with Airtable21// UI repeating groups: use 'Search for Products' (Bubble DB, fast, no rate limits)22// not 'Get data from external API' (live Airtable, rate-limited)Pro tip: For tables with more than 100 records, chain multiple sync calls using Airtable's `offset` pagination. After the first sync step, check if the response includes an `offset` field. If it does, schedule a second 'Sync Airtable Products - Page 2' workflow immediately with the offset value as a parameter to fetch the next 100 records. Repeat until no offset is returned. This daisy-chain approach handles unlimited record counts within Bubble's workflow system.
Expected result: A recurring Backend Workflow syncs your Airtable data into Bubble's database every 15 minutes. Your UI pages use Bubble's native database for display (fast, no rate limits) and call Airtable directly only for write-back actions (form submissions, status updates) where fresh data is required.
Common use cases
Ops-managed content directory rendered in Bubble
Your team maintains a directory of resources, products, or listings in Airtable — adding new rows, updating statuses, and tagging records without any developer involvement. A Bubble scheduled workflow syncs Airtable records into Bubble's database every 15 minutes. Bubble renders a searchable, filterable directory from its native database, serving users quickly without hitting Airtable's rate limit. When a new record is manually added in Airtable, it appears in Bubble at the next sync cycle.
Every 15 minutes, run Backend Workflow: search Airtable for all records in 'Resources' table where Status is 'Published', create or update matching Bubble Resource records, and delete Bubble Resource records not found in the Airtable sync result
Copy this prompt to try it in Bubble
Bubble form writes back to Airtable as a CRM intake
A Bubble landing page captures lead information via a form. On submission, a Bubble workflow fires an API Connector POST call to create a new record in an Airtable 'Leads' table. The sales team immediately sees the new lead in Airtable's grid view, can assign owners, change status, and add notes — all without touching Bubble. The Bubble form serves as the data entry front end while Airtable is the CRM.
When Submit button is clicked and Form is valid, call Airtable API 'Create Lead Record' with fields: Name = Input Name's value, Email = Input Email's value, Source = 'Website Form', Created = Current date/time
Copy this prompt to try it in Bubble
Airtable-powered event or course catalog
A non-technical team manages an events or courses catalog in Airtable, including dates, descriptions, image links, pricing, and capacity. Bubble fetches this data and renders a card-based catalog with filtering by category, date range, and price. Users can register for events via Bubble, which writes a registration record back to Airtable and updates remaining capacity using a PATCH call.
On page load, call Airtable 'Get Events' with filterByFormula: AND(IS_AFTER({Date}, TODAY()), {Status}='Published'), sort by Date ascending, display results in repeating group, filter by Category dropdown selection
Copy this prompt to try it in Bubble
Troubleshooting
Bubble's 'Initialize call' returns no fields or shows an empty response from Airtable
Cause: The Airtable table has no records, or the `filterByFormula` example value you provided during initialization matches zero records. Airtable returns `{"records": []}` for an empty result, and Bubble cannot detect field names from an empty array.
Solution: Before initializing, ensure your Airtable table has at least one real record with values in the fields you want to use. Temporarily remove or relax the `filterByFormula` parameter during initialization. After Bubble detects the field schema, you can re-add the filter to the call's parameters. Re-initialize by clicking 'Initialize call' again after adding a record.
Airtable returns HTTP 429 errors and Bubble repeating groups appear empty or show an error state
Cause: Your app's users are collectively exceeding Airtable's 5 requests-per-second rate limit for your base. This happens when multiple users load a page simultaneously and each page load triggers an Airtable API call.
Solution: Implement the scheduled sync pattern from Step 6: cache Airtable data in Bubble's database every 15–30 minutes and serve UI from Bubble's database instead of live Airtable calls. For pages that must show live Airtable data, add Bubble's 'When API Call returns an error' event to show a user-friendly message and retry after a 1-second pause using a scheduled workflow.
Bubble API Connector returns 401 Unauthorized when calling Airtable
Cause: The Personal Access Token is invalid, expired, or was not entered correctly in the Authorization header. Common mistakes: missing the 'Bearer ' prefix (with a space), the token was truncated when copied, or the token was revoked in Airtable's developer hub.
Solution: Open Airtable's developer hub at airtable.com/create/tokens and verify the token still exists and has not been revoked. If in doubt, create a new token. In Bubble's API Connector, click the Airtable group, find the Authorization shared header, and re-paste the full token value: `Bearer patXXXXXXXXXXXXXX...` (with 'Bearer ' including the space before the token string). Click Initialize call to verify the fix.
1// Correct Authorization header format:2// Key: Authorization3// Value: Bearer patXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX4// ^ ^5// | The actual token (starts with 'pat')6// 'Bearer ' with a space after itA POST call to create an Airtable record returns 422 Unprocessable Entity
Cause: One or more field values in the request body do not match the expected type in Airtable. Common causes: sending a number field as a quoted string, sending a date in the wrong format, or using a field name that does not exactly match the Airtable column name (case-sensitive).
Solution: Check the error response body in Bubble's Logs tab — it will specify which field failed and why. Verify that number fields receive numeric values (not quoted strings), date fields receive ISO 8601 format (YYYY-MM-DD), and that field names in your POST body exactly match the Airtable column names including capitalization and spaces. Field names are case-sensitive: 'Product Name' is different from 'product name'.
1// Common type fixes for Airtable POST body:2// Date field: use Bubble's ':formatted as' → Custom → 'YYYY-MM-DD'3// Number field: ensure value is not wrapped in quotes in the JSON body4// Linked record field: pass an array of record IDs ["recXXXXXXXX"]5// Multi-select: pass an array of option strings ["Option A", "Option B"]Linked record fields in Airtable show record IDs like 'recXXXXXXXX' instead of the linked record's actual content
Cause: Airtable's API returns linked record fields as arrays of record IDs (internal Airtable identifiers), not the content of the linked records. This is expected API behavior — the linked record data requires a separate API call to retrieve.
Solution: For each linked record you need to display, make a second API call: GET `/{LinkedTableName}/{recordId}` to retrieve the linked record's fields. In Bubble workflows, use the linked record IDs to trigger additional API calls and store the fetched data. Alternatively, use Airtable's 'Lookup' field type to pull specific values from linked records into the current table as a flat field — these appear directly in the records response without a second API call.
Best practices
- Use a read-only PAT (`data.records:read` scope only) for Bubble workflows that display Airtable data, and a separate write-scoped PAT (`data.records:write`) for workflows that create or update records — store both in the API Connector with the Private checkbox enabled
- Cache Airtable data in Bubble's database with a scheduled sync workflow (every 15–30 minutes) for any page with more than a handful of concurrent users — Airtable's 5 req/sec per base rate limit is reached quickly at real user volumes
- Use Airtable's `filterByFormula` parameter to filter records server-side before they reach Bubble, rather than fetching all records and filtering in the Bubble workflow — this reduces API call size and lowers rate limit pressure
- Add an `airtable_record_id` text field to Bubble data types that mirror Airtable tables — this enables your sync workflow to distinguish between records to create versus update, preventing duplicates
- Apply Bubble's Data tab → Privacy rules to any data type that stores synced Airtable records, ensuring that users can only see records they are permitted to access — the Airtable PAT controls Airtable access, but Bubble's privacy rules control what each user sees in your app
- Handle Airtable pagination explicitly: the API returns a maximum of 100 records per request, with an `offset` field in the response when more pages exist — your sync workflow must chain calls using the offset until no more pages remain
- Monitor Airtable API call WU consumption in Bubble's Logs tab → Workflow logs when using Backend Workflows for sync operations — each API call in a workflow consumes Workload Units, and syncing large Airtable tables frequently can become expensive on Bubble's paid plans
- Create dedicated Airtable views (using Airtable's filter and sort settings) and reference them by view ID in your API calls using the `view` parameter — this lets your non-technical team control which records appear in Bubble by managing Airtable view filters, without requiring any Bubble code changes
Alternatives
Supabase exposes PostgreSQL via a REST API with built-in Row Level Security, has no per-request rate limit comparable to Airtable's 5 req/sec ceiling, and supports millions of records with full SQL join power. Choose Supabase when your data volume is large, your team is comfortable with structured schemas, or you need relational queries Airtable cannot express. Choose Airtable when your non-technical ops or marketing team is already managing the data in Airtable and the grid UI is part of their workflow.
Notion's database API offers similar no-code data management with richer content blocks (text, embeds, kanban) alongside the database rows. Airtable is stronger for pure tabular data with calculated fields, linked records, and Gantt/calendar views; Notion excels when the data involves long-form documentation. Notion's API rate limit is also more restrictive than Airtable's. If your team uses Notion for project management and wikis, the Notion API may suit content-heavy Bubble pages.
Firebase Firestore offers real-time data sync via the Zeroqode plugin, scales to millions of documents, and has no record caps. It requires a paid Zeroqode plugin and has a steeper setup curve compared to Airtable's simple REST API. Choose Firestore when your Bubble app needs real-time updates (live chat, collaborative editing, live dashboards) or when your data volume exceeds Airtable's plan limits. Choose Airtable when your team needs to manage the data visually in a spreadsheet-like interface.
Frequently asked questions
Can I use the Airtable integration on Bubble's free plan?
Yes, for reading data. Bubble's free plan supports client-side API Connector calls, so you can display Airtable records in repeating groups and data elements. The limitation is for write operations: POST and PATCH calls should go through Backend Workflows (which require a Starter plan or above) to avoid exposing your write-scoped PAT in browser DevTools. Read-only integrations using a read-scoped PAT with the Private header flag work on the free plan.
Airtable's rate limit is 5 requests per second — does that mean 5 users per second?
Not quite. The 5 req/sec limit applies to the total number of API requests your application makes to that base per second — across all users. If 5 users each trigger one Airtable API call at the same moment, that is 5 simultaneous requests and you are at the limit. If 6 users do the same, the 6th request fails with HTTP 429. This is why caching Airtable data in Bubble's database (updated by a scheduled sync) is essential for any app with meaningful user traffic — the sync workflow makes one API call per sync cycle regardless of how many users are viewing the data.
How do I display Airtable linked record values instead of just the record IDs?
Airtable's API returns linked record fields as arrays of record IDs (for example, `["recXXXXXXXXXXXXXX"]`). To display the linked record's actual field values, you need to make a second API call to `GET /{linkedTableName}/{recordId}` for each linked record. An easier alternative is to add a 'Lookup' field in Airtable that pulls specific field values from the linked table into the current table as a plain text or number field — lookup values appear directly in the records response without any additional API calls from Bubble.
Do I need to handle Airtable pagination in Bubble?
Yes, if your table has more than 100 records. Airtable returns a maximum of 100 records per API call. If there are more records, the response includes an `offset` field (a string). To fetch the next page, make the same API call with `offset` set to that value as a query parameter. In Bubble, implement pagination by storing the offset in an app state and triggering a new API call (via a 'Load more' button or automatically) that passes the stored offset. Continue until the response has no `offset` field, indicating you have reached the last page.
Can Airtable send real-time updates to Bubble when records change?
Yes, using Airtable Webhooks. Create a webhook subscription via `POST /v0/bases/{baseId}/webhooks` with the URL of a Bubble Backend Workflow endpoint. Airtable will POST a payload to that endpoint whenever records in your base are created, updated, or deleted. Set up the Backend Workflow in Bubble under Settings → API → enable Workflow API, then create a Backend Workflow with 'Detect request data.' Use the Backend Workflow endpoint URL (without the `/initialize` suffix for production) as the webhook destination in Airtable. Note that Airtable webhook subscriptions expire after 7 days of inactivity and require renewal — plan for automatic renewal in your Bubble workflow. Airtable Webhooks require an Airtable Team plan or above.
My Airtable field names have spaces and special characters — will that cause problems in Bubble?
No, Bubble handles field names with spaces correctly. When Bubble's API Connector initializes an Airtable call, it maps all detected field names exactly as returned by the API, including names with spaces (for example, 'Product Name', 'Created Date'). These appear in Bubble's dynamic data dropdown with their original names. The only place to be careful is in `filterByFormula` expressions: always wrap field names in curly braces, even if they have no spaces — `{Name}`, `{Product Name}`, `{Created Date}` — and the Airtable API will handle them correctly.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation