What transfers, what doesn't, how long it takes, and how to undo it. Read the second half before you start — the limitations are the part worth your time.
An Airtable account you can sign into. The migration reads your Airtable base over the public API and writes into a new base on our side. It never writes to Airtable.
schema.bases:read and data.records:read — and nothing else. No token to create, no base ID to find.Your Airtable base is not modified, moved, or deleted. This is a copy. Both bases exist afterwards and you can keep working in Airtable while you evaluate. Nothing here is a one-way door — the section on leaving Base is at the bottom of this page for the same reason.
Prefer not to connect an account? A personal access token with those two read scopes still works — the migration screen has the field. Delete the token when you're done.
In Base: Home → + New base → Migrate from Airtable → Connect Airtable. Airtable shows you what we're asking for; you approve it there.
We list the bases the connection can read. Choose one and start. No IDs to copy out of a URL.
The schema first, then every record in every table, a hundred at a time until there are none left. There is no row cap — whatever is in the base comes across.
Tables first, so that linked records have somewhere to point. Then columns, then rows, then a second pass that turns Airtable's record links into real links between your new rows.
Your primary field keeps its name and stays the primary column. Your tables arrive with the columns Airtable had and no others.
One difference you may notice: multi-select values are stored in the order the choices are defined, not the order they were entered. The values are the same set.
Agents can run this too. The MCP server exposes list_airtable_bases and migrate_airtable, and the REST API takes the same arguments at POST /v1/bases. Both take a personal access token — the connect flow is a browser one.
These arrive as real, typed columns — not text — with their options intact. Select choices, currency symbols, decimal precision and rating scales all come across.
| Airtable | Base | Notes |
|---|---|---|
| Text | ||
| Single line text | text | — |
| Long text | text | — |
| Rich text | rich_text | Sanitised to a safe HTML subset |
| — | ||
| URL | url | — |
| Phone | phone | — |
| Numbers | ||
| Number | number | Decimal precision preserved |
| Currency | currency | Symbol and precision preserved |
| Percent | percent | Airtable stores 0.123 for 12.3%; we convert, so the number you see is the number you saw |
| Rating | rating | Scale preserved when it's 1–10 |
| Dates and flags | ||
| Date | date | — |
| Date and time | date | Time of day is dropped. Base stores dates, not timestamps — 2026-03-14T09:30Z becomes 2026-03-14 |
| Checkbox | checkbox | — |
| Choices, files and relationships | ||
| Single select | select | Choice list preserved. A select with no choices defined falls back to text. |
| Multiple select | multi_select | Same |
| Attachments | attachment | Links, not copies — see below |
| Link to another record | link | Rewritten into real links between the new rows, provided the linked table is part of the same migration |
Attachments come across as links to the files in Airtable, not as copies of them. The cell keeps the file's URL, name and type. If you are leaving Airtable permanently, download anything you need to keep before you close the account — an attachment cell whose Airtable URL stops resolving is an empty cell.
Airtable computes formulas, rollups, lookups and counts on its side. There is no way to read the expression through the API, only the result — so that's what we take: a text snapshot of what the cell said at the moment we read it. It will not recalculate.
This affects formula, rollup, lookup, count, autonumber, created and modified time, created and modified by, collaborator, duration, barcode, button, and synced-source fields.
Base has all of these field types natively, with cross-table formulas, link, lookup, rollup, count and reverse-link. So the fix is real, and it's a one-time job: add the column you want, write the expression, delete the snapshot. The data underneath it made the trip.
Two other cases end up as text. A single or multiple select field with no choices configured, and a link field pointing at a table that isn't part of the migration — for that one, the cell keeps Airtable's record IDs so nothing is silently lost, and you can rebuild the relationship.
Empty cells stay empty. A blank value in Airtable doesn't become the string "null" or a zero on this side.
Tables, fields, records and your collaborator list make the trip. Everything you built around them does not. Plan for rebuilding these.
Your views are recreated by name: grid, kanban, calendar, gallery, list, and Airtable's timeline as a gantt. Form views aren't — Base forms are a separate thing with their own designer — and a view Base can't build on a given table (a timeline on a table with no date field) is skipped rather than failing the migration. What arrives is the right set of views, empty of their filters and sorts.
A migration that fails cleans up after itself: the half-built base is removed on the way out, so there is nothing to delete and retrying never stacks copies in your workspace. Nothing in Airtable is touched either way, which is why retrying is always safe.
| What you see | Usually means | Fix |
|---|---|---|
| airtable_fetch_failed | The connection can't read that base — with a token, wrong scopes, base not added to the token, or a mistyped base ID | Reconnect Airtable. On the token path, confirm both read scopes and that the token lists this base. |
| airtable_not_connected | The hour-long connection expired, or you approved it in another browser | Click Connect Airtable again. Nothing was created, so nothing needs cleaning up. |
| A large base takes a while | Airtable rate-limits per base, and reading a whole one is a request per hundred records per table | Nothing to do — rate limits are retried with backoff, so a big base is slower rather than broken. If it does give up, the base is removed and you can start again. |
| A column arrived as text | Expected — it's a computed or unsupported type | See fields that arrive as text. Add the real column and delete the snapshot. |
| Links are empty or show record IDs | The linked table wasn't in the migration | Migrate the whole base at once rather than table by table. |
Stuck on something not listed here? Send us the base ID and what you saw. A migration that fails is a bug on our side, not a support ticket on yours.
Worth knowing before you move in, not after. There are two ways out and none of them require asking us.
/v1.We think switching costs are a bug, including ours. The import you just ran took a minute because of that, and the exit should cost you about the same.