Migrate from Zapier to n8n: map, rebuild, run in parallel
There's no import button that turns Zaps into n8n workflows. A migration is a rebuild, and that's a good thing, because it's a chance to merge duplicate Zaps and drop the ones nobody uses. This guide covers the method:
outline {
- list everything;
- translate each Zapier feature to its n8n equivalent;
- rebuild;
- run both side by side;
- switch over with a way back.
If you haven't decided yet, read Should you switch to n8n? first. You'll also need a running n8n instance: Self-host n8n covers that.
What you need
- Admin access to your Zapier account.
- A production n8n instance with HTTPS and a public URL, so apps can reach its webhooks.
- A spreadsheet for the inventory.
- Test accounts or test records in the apps you connect, where you can get them.
Step 1: List every Zap
In Zapier, go through each Zap and add a row to your spreadsheet:
| Zap name | On? | Trigger app and event | Steps (in order) | Filters or Paths | Runs per month | Owner | Keep, merge or drop |
|---|
Get run counts from Zap history. While you're there, sort out the obvious:
- Zaps that are off, or haven't run in months: drop.
- Several Zaps with the same trigger: merge into one n8n workflow with a Switch node.
- Zaps that touch money, customers or legal records: mark them high risk, and migrate them last.
Check it worked: every Zap has a row and a decision, and you know which three to migrate first (frequent, low risk, simple).
Step 2: Learn the mapping
Most Zapier building blocks have a direct n8n equivalent:
| Zapier | n8n | Notes |
|---|---|---|
| Trigger (app event) | App trigger node, or Webhook | Prefer webhooks where the app supports them. Polling triggers check on a schedule. |
| Action | App node, or HTTP Request | HTTP Request covers any app with an API. |
| Filter by Zapier | Filter or If | Filter drops items. If splits them into true and false branches. |
| Paths by Zapier | Switch | One output per rule, plus an optional fallback output. |
| Formatter by Zapier | Edit Fields (Set), Date & Time, or Code | Expressions handle most text and number formatting inline. |
| Delay by Zapier | Wait | Wait for a set time, until a date, or until a webhook call. |
| Webhooks by Zapier | Webhook and Respond to Webhook | |
| Looping by Zapier | Built in, or Loop Over Items | Most n8n nodes already run once per item. |
| Storage by Zapier | A database node, or Data tables where available | Postgres, Google Sheets, or n8n's built-in data tables, depending on version. |
| Schedule by Zapier | Schedule Trigger | Cron syntax is supported. |
| Sub-Zap | Execute Workflow | Called workflow starts with the Execute Workflow Trigger. |
| Zap error notifications | Error Trigger workflow | See Make n8n workflows reliable. |
The biggest shift is items. A Zapier trigger that returns 10 rows usually runs the Zap 10 times. An n8n trigger can pass 10 items into one execution, and each following node processes all 10. That's why you rarely need a loop node in n8n.
Check it worked: for each of your first three Zaps, write the n8n node for every step next to the Zapier step in your spreadsheet.
Step 3: Rebuild the first workflow
Take your first Zap, for example "New form entry > create CRM contact > post to Slack".
- Create credentials in n8n for each app. Self-hosted n8n sometimes needs your own OAuth app with the provider. The node's docs page explains how.
- Add the trigger. If the form tool supports webhooks, add a Webhook node, set it to
POST, and copy its Test URL. - Point the source app at the test URL, then click Listen for test event and submit a test entry. The data appears in the node's output.
- Add the steps. Map fields by dragging them from the input panel. A typical expression:
{{ $json.body.email.trim().toLowerCase() }}That replaces a Formatter step: expressions accept JavaScript, so most small transformations happen inline.
- Recreate filters and paths. A Zapier filter "Only continue if Country is US" becomes a Filter node with the condition
{{ $json.body.country }}*is equal to*US. - Add error handling. Turn on Retry On Fail for the CRM and Slack nodes, and set the workflow's error workflow.
Check it worked: a test entry produces the same CRM record and Slack message as the Zap does.
Step 4: Run both side by side
Don't switch off the Zap yet. For a week or two, run both:
- Keep the Zap live. It still does the real work.
- Activate the n8n workflow in shadow mode. Point its final "write" steps at a test destination: a test Slack channel, a sandbox CRM, or a Google Sheet that logs what it would have done.
- Send the same events to both. Many apps allow more than one webhook URL. If yours allows only one, put the n8n workflow on a schedule that reads the same source instead, or have the Zap forward the payload to n8n with a Webhooks by Zapier action.
At the end of each day, compare the outputs. Count runs on both sides, and check a few records field by field.
Check it worked: a full day, or several, where every event the Zap handled also shows up correctly in n8n's test destination.
Step 5: Cut over, with a way back
When the outputs match:
- Pick a quiet time.
- Switch the n8n workflow's write steps from the test destinations to the real ones.
- Point the source app's webhook at n8n's Production URL (the one with
/webhook/, not/webhook-test/). - Turn the Zap off, but don't delete it.
- Watch n8n's Executions for the first few hours.
To roll back: turn the Zap back on and point the webhook back at Zapier. Write these two steps down before you start, so anyone can do them.
Keep the old Zap switched off, but in place, for a few weeks, until you're sure nothing depends on it. Then delete it and mark it done in your spreadsheet.
Check it worked: the first real events go through n8n, and the Zap's history shows no new runs.
Step 6: Repeat, then retire Zapier
Work down your list: low-risk Zaps first, high-risk ones last, each with its own parallel run. When the last Zap is off and its notice period has passed, downgrade or cancel the plan. Check your billing cycle first, so you don't pay for a month you won't use.
Troubleshooting
| Problem | Likely cause | Fix |
|---|---|---|
| The workflow runs once, but the Zap ran several times | n8n processed all items in one execution | Expected. Check the item count in the output, not the execution count. |
| Fields are empty in n8n but filled in Zapier | Different field paths. Webhook data sits under body | Look at the real input data and fix the expression path. |
| The source app rejects the webhook URL | The URL isn't public HTTPS | Use your production domain, not localhost. |
| OAuth credential fails on self-hosted n8n | Missing redirect URL at the provider | Add the redirect URL shown in the n8n credential dialog to your OAuth app. |
| Duplicate records after cutover | Both the Zap and n8n ran | Make sure the Zap is off, and add the duplicate check from the reliability guide. |
References
- n8n Webhook node: https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.webhook/
- If, Filter and Switch nodes: https://docs.n8n.io/integrations/builtin/core-nodes/
- Expressions: https://docs.n8n.io/code/expressions/
- Looping in n8n: https://docs.n8n.io/flow-logic/looping/
- Error handling: https://docs.n8n.io/flow-logic/error-handling/
- Zapier help center (Zap history, Paths, Filters): https://help.zapier.com/