- Trigger
- Schedule Trigger, every 15 minutes
- Nodes
- 9
- Requires
- Airtable, Notion
- Category
- Sync
- From the course
- Notion, Airtable and Google Workspace
- Published
- 2026-09-10
Node types used
- Schedule Trigger
- Airtable
- If
- Loop Over Items
- Notion
- Wait
What it does
Two teams, two tools, and the same records typed into both. The obvious fix is a workflow that creates a Notion page for every Airtable record it sees, which produces a fresh duplicate on every run. The next fix is a second workflow syncing back, which produces a loop. Neither of those is a sync.
A Schedule Trigger runs every 15 minutes. An Airtable search returns only records whose Last modified time is after NOW() minus 20 minutes, so the window overlaps the schedule and a record changed near a run boundary is seen twice rather than missed. An If node checks there is a record id at all before going further. A Loop Over Items node with a batch size of one takes each record in turn. For each one, a Notion node queries the target database for a page whose Airtable ID rich text property equals the record id, returning at most one result and with Always Output Data on so an empty search still produces an item. A second If node checks whether that search returned a page id: if it did, a Notion update writes the Name and the Airtable ID onto the existing page; if it did not, a Notion create makes a new page with the same two properties. Either way a Wait node pauses 250 milliseconds before the loop takes the next record, which keeps the run under the Airtable and Notion request limits.
It is one direction only. Nothing reads Notion back into Airtable, and a record deleted in Airtable leaves its Notion page in place because the Airtable search only returns records that still exist. Only the Name field and the Airtable ID are mapped: add your own property pairs to both Notion nodes if you need more.
Node by node
- 1
Schedule Trigger Fires every 15 minutes.
- 2
Airtable: changed records Searches the configured base and table with the filter formula IS_AFTER({Last modified time}, DATEADD(NOW(), -20, 'minutes')), so each run sees a 20 minute window against a 15 minute schedule. The overlap is deliberate.
- 3
Any records to process? An If node that checks the item has a non-empty id. The false output is empty, so a quiet window ends the run here.
- 4
Loop Over Items Splits the records into batches of one so each record is matched and written individually. The done output is empty; the loop output feeds the Notion lookup.
- 5
Notion: find matching page Queries the Notion database with a JSON filter on the Airtable ID rich text property equalling the current Airtable record id, limit 1. Always Output Data is on, so a miss still produces an item for the next If node to test.
- 6
Page found? An If node checking whether the search result has a non-empty id. True means update, false means create.
- 7
Notion: update page Updates the found page, setting the Name title from the Airtable record Name field and rewriting the Airtable ID rich text property.
- 8
Notion: create page Creates a page in the same database with the Airtable Name as the title and the Airtable record id stored in the Airtable ID rich text property. That stored id is what makes the next run an update instead of a duplicate.
- 9
Wait 250ms Pauses a quarter of a second, then returns to Loop Over Items for the next record. This is the pacing that keeps the run under the Airtable limit of five requests per second.
Setup after import
- 1
Create an Airtable personal access token credential and select it on Airtable: changed records.
- 2
Create a Notion internal integration credential and select it on all three Notion nodes.
- 3
Replace the base id appPLACEHOLDER01 and the table id tblPLACEHOLDER01 on the Airtable node with your own.
- 4
Replace the Notion database URL on both the find and the create node. The export ships with https://www.notion.so/YOUR_NOTION_DATABASE_ID?v=YOUR_VIEW_ID.
- 5
Share the Notion database with your integration. A database that has not been shared returns nothing and the workflow silently creates duplicates instead of finding pages.
- 6
Add a rich text property called Airtable ID to the Notion database, and a Last modified time field to the Airtable table. Both names are referenced literally in the filters.
Limits
One direction. Airtable is the source of truth and Notion is overwritten.
Deletes do not propagate. A record removed from Airtable leaves its Notion page behind.
Only Name and Airtable ID are mapped. Every other field needs adding to both Notion nodes by hand.
The lookup is one Notion query per record, so a large batch of changes is slow by design.
The 20 minute window assumes runs succeed. A workflow that was off for an hour will not catch up on its own.
Download the workflow
The 9 node export as n8n reads it. Credential ids, personal values and real endpoints are replaced with placeholders, so nothing here can reach an account that is not yours. No email, no account.
Download airtable-to-notion-one-way-sync.jsonGet help with this in the community
If the import fails, if a node errors on the first run, or if you want to point this at a service it does not cover yet, post it in the free House of Loops community. Say which template it is and paste the error. Shannon Atkinson answers, and the answer stays there for the next person who hits the same thing.
Ask in the communityMore templates
Notion tasks to Google Calendar events in n8n
Every 15 minutes, turns Notion tasks with a due date into all-day Google Calendar events, writes the event id back, and deletes the event when the task is done.
- Trigger
- Schedule Trigger, every 15 minutes
- Nodes
- 9
Stripe webhook signature verification in n8n
A Webhook node with Raw Body on, a Code node that checks the Stripe-Signature HMAC, and a Postgres insert that ignores duplicate event ids.
- Trigger
- Stripe webhook, POST on the path /stripe
- Nodes
- 3
Stripe subscription lifecycle router in n8n
Verifies the Stripe signature, then routes customer.subscription events, invoice.paid and invoice.payment_failed to one branch per subscription status.
- Trigger
- Stripe webhook, POST on the path /stripe-subscription-lifecycle
- Nodes
- 15