Airtable Schema Diff - Compare & Monitor Bases
Pricing
from $0.05 / base diffed
Airtable Schema Diff - Compare & Monitor Bases
Compare two Airtable bases field by field, or snapshot one base and see exactly what changed since last run. Renames are reported as renames, not as a delete plus an add. Read-only token, schema.bases:read only - no record access.
Pricing
from $0.05 / base diffed
Rating
0.0
(0)
Developer
Mediocre_Interest
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
a day ago
Last modified
Categories
Share
Find out exactly what changed in your Airtable base — which field was renamed, which formula broke, which link now points somewhere else. Compare two bases against each other to see how far copies of a template have drifted apart, or snapshot one base and diff it against its own previous run. Leave the token empty and press Start to see a worked example on two bundled sample bases; nothing to install and no credentials to try it.
- Renames are renames. A renamed field is one row, not a delete plus an add, because the diff matches on Airtable's stable field IDs rather than on names.
- Broken formulas are found. Airtable marks a computed field invalid in its schema and surfaces it nowhere in the UI. Every run catches the moment a formula, rollup, lookup or count stops evaluating.
- Read-only, one scope.
schema.bases:readand nothing else. The Actor never reads a single record. - Deterministic. No AI model, no scoring heuristic. The same two schemas always produce the same rows.
What does Airtable Schema Diff do?
Airtable has no schema history. There is no audit log of structural change on any plan below Enterprise, no way to ask what a base looked like last Tuesday, and no warning when someone deletes a field that three Make scenarios and a Zapier zap are reading by name. A base that started as a clean template accumulates a Notes 2 here and a retyped currency field there until the copies an agency runs for eight clients no longer share a shape — and the first anyone hears of it is an integration failing in production.
This Actor reads your base structure through Airtable's metadata API and compares two versions of it. In Compare two bases mode, every base you list is checked against one template base, which is how you find the client copy that has drifted. In Track one base over time mode, each base's structure is stored in a named key-value store and compared against the snapshot from the previous run, which is how you get a daily changelog. Snapshots are kept indefinitely and are not tied to a single run.
Each difference becomes one flat row in the dataset: what changed, where, the value before and after, how serious it is, and what to do about it. Alongside the rows you get one summary row per base and a self-contained web page per base you can send to whoever owns it — it needs no Airtable access and no Apify account to read. Everything runs on Apify, so you can schedule it daily, call it from the API, pipe it into Make, n8n, Zapier or Slack, and export the results as JSON, CSV or Excel.
Which schema changes does it detect?
Severity is about what breaks downstream, not how surprising the change is. Rows are written worst first.
| Severity | Change | What it catches |
|---|---|---|
critical | fieldBroke | A formula, rollup, lookup or count has stopped evaluating. Airtable reports this nowhere else. |
critical | fieldRemoved | A field is gone, so everything reading it now fails. |
critical | tableRemoved | A whole table is gone. |
critical | linkTargetChanged | A link field now points at a different table — joins that used to resolve now resolve somewhere else. |
critical | primaryFieldChanged | What every linked record displays as has changed, in every table that links here. |
high | fieldRenamed | A field was renamed. Field IDs are stable, so this is never confused with a delete plus an add. |
high | fieldTypeChanged | The field's JSON type in the API response has changed. |
high | selectOptionRemoved | A dropdown option was deleted, so records holding it lose their value. |
high | linkInverseChanged | A two-way link became one-way, or the reverse. |
medium | fieldAdded, tableAdded, selectOptionAdded | New structure appeared — a table, a field, or a dropdown option. |
medium | fieldRepaired | A previously broken computed field evaluates again. |
medium | tableRenamed, selectOptionRenamed | A rename that keeps its ID, so records keep their values. |
medium | fieldOptionsChanged, formulaChanged | Precision, currency symbol, date format, time zone, or the formula text itself. |
medium | viewAdded, viewRemoved | A view appeared or disappeared. |
low | fieldDescriptionChanged, tableDescriptionChanged | Documentation edits. |
low | viewRenamed, viewFieldsChanged | Cosmetic view changes. |
low | fieldOrderChanged, viewOrderChanged | Someone dragged a column or a view tab. |
The five kinds of churn that happen without anyone deciding anything — description edits, hidden columns, field order, view order and dropdown colours — are switched off by default, so a run on a schedule stays worth reading. Turn any of them back on in Changes to ignore.
Why use Airtable Schema Diff?
- It finds the break Airtable hides. A rollup that silently stopped evaluating looks normal in the grid. It is a
criticalrow here, on the run it happens. - Nothing to install, no credentials to start. Press Start with the token empty and the Actor compares two bundled sample bases: 12 changes across 4 tables and 17 fields, in about a second.
- Safe by construction. One read-only scope,
schema.bases:read, on a personal access token you grant base by base. No record data is read, so no record data can reach the dataset. - Built for agencies and ops teams. Point it at a template and eight client copies and it tells you which copy drifted and how far, in one run.
- Deterministic and auditable. No AI model and no weighted drift score — a dozen description edits can never outrank one broken formula. The dataset carries the counts; you decide what matters.
- Runs on the Apify platform. Schedule it daily, trigger it from the API, connect it to Make, n8n, Zapier or Slack, and export to JSON, CSV or Excel.
How to compare two Airtable bases
To see what the output looks like with nothing set up:
- Leave Airtable personal access token empty.
- Press Start.
- Open the Changes tab. You are looking at 12 differences between two sample bases, worst first, each marked
source: "sample".
To run it against your own bases:
- Create a personal access token at airtable.com/create/tokens.
- Give it the
schema.bases:readscope. Nothing else is needed. - Under Access, add each base you want compared. A personal access token lists its bases individually — a base you do not add here is invisible to the token.
- Paste the token into Airtable personal access token.
- Choose Compare two bases and list your bases, template first — or choose Track one base over time and list the bases you want a changelog for.
- Press Start, then open the Changes tab for the differences and Bases compared for the per-base totals.
In Track one base over time, the first run of any base stores its snapshot and reports no changes, because there is nothing yet to compare against. The second run is the one that produces a changelog.
Input
| Field | What it does |
|---|---|
| Airtable personal access token | A read-only token with the schema.bases:read scope. Leave it empty to compare two bundled sample bases instead of your own. |
| What to compare | Compare two bases checks every base against one template. Track one base over time compares each base against its own previous run. |
| Bases | Base IDs, one per line. Paste whole base URLs if that is easier — the ID is taken out of them. Leave empty to use every base the token can see. |
| Template base | The base everything else is compared against. Leave it empty to use the first base listed, so you can paste the template first and its copies after it. |
| Changes to ignore | Kinds of change that will not be reported. The default hides the churn a base produces on its own. |
| Minimum severity | Drop changes milder than this. |
| Snapshot store name | The named key-value store holding one snapshot per base, used when tracking over time. Change it only to keep two schedules from sharing snapshots. |
| Write a shareable report | Produce a self-contained web page per base, linked from that base's summary row. |
| Maximum bases | Stop after this many bases. |
See what the output looks like, with no Airtable account involved:
{"airtableToken": "","mode": "comparative"}
Check three client bases against a template, and report only what breaks something:
{"airtableToken": "<your Airtable personal access token>","mode": "comparative","compareToBaseId": "appTEMPLATE000000","baseIds": ["appCLIENTONE00000", "appCLIENTTWO00000", "appCLIENTTHREE000"],"minSeverity": "high"}
Keep a daily changelog of one base, including the description edits that are hidden by default:
{"airtableToken": "<your Airtable personal access token>","mode": "temporal","baseIds": ["https://airtable.com/appCLIENTONE00000/tblPjlsmyaLlNAekw"],"ignore": ["viewFieldVisibility", "fieldOrder", "viewOrder", "selectColors"]}
Output
Every run writes one row per change to the default dataset, one row per base to the Bases compared dataset, and — unless you switch reports off — one shareable web page per base plus a summary-run record to the key-value store. Export any of it as JSON, CSV or Excel, or read it from the API.
These two rows are from the first input example above, the one that needs no token. The broken rollup:
{"source": "sample","runAt": "2026-09-30T06:52:15.590Z","mode": "comparative","baseId": "appDRIFTEDCLONE01","baseName": "Northwind Ltd (drifted copy)","comparedToBaseId": "appm2rdjG6AkpyytA","comparedToBaseName": "Client Portal Template (demo)","comparedToAt": "2026-08-01T09:00:00.000Z","changeType": "fieldBroke","severity": "critical","scope": "field","tableId": "tblYf7TsieTYspz0b","tableName": "Projects","fieldId": "fldZQHqO9S9ooAZum","fieldName": "Hours Logged","fieldType": "rollup","viewId": "","viewName": "","path": "Projects.Hours Logged.isValid","before": "true","after": "false","message": "Field \"Hours Logged\" (rollup) in \"Projects\" is broken: Airtable reports it as no longer valid, so it has stopped evaluating.","remediation": "Open the field in Airtable and fix its configuration. A computed field usually breaks because a field or link it referenced was deleted or retyped."}
And the rename, reported as one row rather than as a removal and an addition:
{"changeType": "fieldRenamed","severity": "high","scope": "field","tableName": "Clients","fieldName": "Primary Email","fieldType": "email","path": "Clients.Primary Email","before": "Contact Email","after": "Primary Email","message": "Field \"Contact Email\" was renamed to \"Primary Email\" in \"Clients\".","remediation": "Update anything that addresses the field by name. Most no-code tools do - Airtable field IDs are stable across a rename, but names are what integrations usually store."}
What does each change row contain?
| Columns | What they mean |
|---|---|
changeType, severity, scope | What kind of change it is, how much it breaks, and whether it is about a table, a field or a view. |
path, tableName, fieldName, viewName, fieldType | Where in the base, in names you recognise — Projects.Hours Logged.isValid. |
before, after | The old and new values, as text. Empty on the side where the thing did not exist. |
message, remediation | The change in one sentence, and what to check or fix. remediation is empty where there is nothing to do. |
baseId, baseName, comparedToBaseId, comparedToBaseName, comparedToAt | Which base, and what it was compared against — the template, or the previous run and when that snapshot was taken. |
mode, source, runAt | How the comparison was made, whether the row came from your bases (live) or the bundled samples (sample), and when the run started. |
tableId, fieldId, viewId | Airtable's own IDs, which survive renames. Address these rather than names if you build on the output. |
What is in the "Bases compared" table?
One row per base, worst first: changesCritical, changesHigh, changesMedium, changesLow and changesTotal, the base's current tableCount and fieldCount, what it was compared against, and a link to its report. A row with isBaseline set is the first time this Actor saw that base, so it stored a snapshot and had nothing to compare against yet.
There is deliberately no single drift score. Any weighted sum lets a dozen description edits outrank one broken formula, so the table gives you the counts by severity and sorts on those instead.
What is in a drift report?
One self-contained web page per base, listing every change worst first, with the same before-and-after values and remediation text as the rows. It is a single HTML file with no external assets, so you can attach it to a ticket or send it to whoever owns the base — they need no Airtable access and no Apify account to read it. The link appears in the reportUrl column of that base's summary row.
How much does it cost to compare Airtable bases?
The Actor is pay per event, at the prices shown on this page.
| Event | Charged | When |
|---|---|---|
base-diffed | Once per base compared | After that base's rows have been written to the dataset |
A run that compares one template against eight client copies charges eight events — the template itself is not charged, because it is the thing being compared against. A run in Track one base over time charges once per base each run, including the first, because the base's structure is read and stored either way.
Nothing is charged for a base that could not be read: a base the token was not granted, a base ID that does not exist, or a base beyond the Maximum bases cut-off costs nothing. A run with no token compares the bundled samples and is free. A failed run charges nothing at all.
The Actor respects the maximum cost per run you set on the run. If your budget covers four bases and you listed nine, it compares four, writes their rows, and says so in the run's status message rather than quietly delivering work you have not paid for. If the budget does not cover a single base, the run fails and tells you why.
How to run an Airtable schema diff from the API or from Make, n8n and Zapier
Call it from anywhere with one request, and get the rows back in the response:
curl -X POST "https://api.apify.com/v2/acts/mediocre_interest~airtable-schema-drift/run-sync-get-dataset-items?token=<your Apify API token>" \-H "Content-Type: application/json" \-d '{"airtableToken": "<your Airtable personal access token>","mode": "comparative","compareToBaseId": "appTEMPLATE000000","baseIds": ["appCLIENTONE00000", "appCLIENTTWO00000"],"minSeverity": "high"}'
That returns the change rows as a JSON array once the run finishes. Add &format=csv for a spreadsheet, or &fields=severity,changeType,path,message to keep the response small enough to drop straight into a Slack message.
From there, the Apify integrations do the rest: the Apify app for Make, the Apify node for n8n and the Apify Zapier integration can all start this Actor and read its dataset, so a critical row can open a ticket or post to a channel the moment it appears. Apify's own Schedules run it daily without any of that — pair a daily schedule with Track one base over time and you have a changelog for every base you own.
Tips for better results
- Put the template first. In Compare two bases, leaving Template base empty makes the first base in the list the template. Pasting the template and then its copies is the fastest way to set up an agency check.
- Start at
highseverity, then loosen.criticalandhighare the rows that break integrations. Once those are clean, drop tolowto see the full picture. - Turn descriptions back on if you use them as documentation.
tableDescriptionsis reported by default butfieldDescriptionsis not, because most bases churn them. If yours are load-bearing, removefieldDescriptionsfrom Changes to ignore. - Give each schedule its own snapshot store. Two schedules tracking the same base with the same Snapshot store name overwrite each other's snapshots, and each run then diffs against the other's. One store name per schedule keeps them independent.
- Grant the token every base you list. A base missing from the token's Access list is refused by Airtable with the same error as a base that does not exist, and the run reports it as skipped.
FAQ
Is my Airtable token safe?
The token is a secret input: Apify encrypts it, the Console masks it, and it is never written to the dataset, the reports or the run log. The Actor only ever sends it to api.airtable.com. Give it the schema.bases:read scope alone and it cannot read a record even if it wanted to.
Does this change anything in my Airtable base?
No. Both endpoints it calls are read-only, and the only write scope it could use is one it never asks for. It reads your base structure and writes rows to Apify.
Does it use an AI model?
No. The diff is a deterministic comparison of two schemas: the same two schemas always produce the same rows, in the same order. Severity comes from a fixed table you can read in full above, not from a model's judgement.
Why did my run report no changes?
Three common reasons. In Track one base over time, the first run of a base only stores its snapshot — run it again after a change. In Compare two bases, the bases genuinely match. Or the changes are all in the categories switched off by default: description edits, hidden columns, field order, view order and dropdown colours. Clear Changes to ignore to see everything.
Why is a base reported as skipped?
Airtable answers with the same error for three different causes, and will not say which: the base ID is wrong, the token was not granted that base under Access, or the token is missing the schema.bases:read scope. The run names the base and lists all three, and carries on with the bases it could read. Nothing is charged for a base that was skipped.
Does it detect changes to view filters, sorts or grouping?
No, and nothing here claims otherwise. Airtable's metadata API returns only a view's ID, name, type and which fields are visible in it — there is no filter, sort, grouping or row-height information in the response at all. View configuration drift is not visible to any Airtable API client.
Does it need a paid Airtable plan?
No. schema.bases:read is available on every Airtable plan, including Free, and a base read-only role is enough. Airtable's own structural audit log is Enterprise-only, which is most of the reason this Actor exists.
How many bases can it handle in one run?
As many as your token can see, up to the Maximum bases limit, which defaults to 200. Bases are read ten at a time, so a fleet of 50 is a matter of seconds. A single base at Airtable's own ceiling — 1,000 tables of 500 fields — arrives as one large response, which is why the Actor's default memory is set high enough to parse it.
What other Actors work with this one?
- Airtable Base Schema Auditor & Link Checker — audits a single base for broken links, orphaned tables and configuration problems. It tells you what is wrong with a base right now; this Actor tells you what changed. Run the auditor when a
criticalrow appears here and you go straight from what changed to what it broke. - Airtable Base Documentation & ER Diagram Generator — turns a base into readable documentation and an entity-relationship diagram, which is the thing to regenerate once a diff shows the structure has moved.
- Make.com Scenario Backup & GitOps - Blueprints — versions the Make scenarios that read these bases, so a field rename found here can be traced to the scenarios that address it by name.
Support
Report a problem on the Issues tab of this Actor. Include the run ID and the smallest input that reproduces it — for a diff question, the two base IDs and the mode are usually enough. Custom work and changes to the output shape are available on request through the same tab.