Coda to Notion Migration: Step-by-Step Guide, Examples & Tools
A complete technical guide to Coda to Notion migration. Learn how to map databases, translate formulas, and safely migrate relational data using API pipelines.
Planning a migration?
Get a free 30-min call with our engineers. We'll review your setup and map out a custom migration plan — no obligation.
Schedule a free call- 1,500+ migrations completed
- Zero downtime guaranteed
- Transparent, fixed pricing
- Project success responsibility
- Post-migration support included
1. Quick Answer
Migrating from Coda to Notion requires translating a rigid, row-based database architecture into a flexible, block-based document structure. While standalone tables can be exported as CSV files and imported directly, migrating relational schemas, cross-doc formulas, and nested hierarchies requires API-based mapping. Complex enterprise transitions rely on custom middleware or engineered data pipelines to successfully navigate Notion's ~3 requests-per-second API rate limit and guarantee perfect relational data fidelity.
2. Comparing Migration Destinations
Before mapping data, it is crucial to understand if Notion is the correct architectural fit for the data leaving Coda.
- Coda to Notion: Best for teams shifting focus from computational internal apps to document-first knowledge bases and wikis.
- Coda to Airtable: Best if the workspace is purely relational data (e.g., complex inventory or CRM) and the team does not need text-based documentation.
- Coda to ClickUp: Best if the Coda workspace was used strictly for sprint tracking and task management rather than free-form data manipulation.
3. Understanding Structural Differences
A successful migration requires mapping Coda's rigid hierarchy to Notion's infinite canvas. If this schema is not translated properly, relational data will break upon import.
| Coda Architecture (Database-First) | Notion Architecture (Document-First) |
|---|---|
| Workspace | Workspace |
| Doc | Top-Level Page |
| Section / Page | Sub-Page |
| Table | Database |
| Row | Page (Inside a Database) |
| Column | Property |
| Canvas Column | Page Body (Blocks) |
4. What Transfers and What Breaks (Compatibility Matrix)
Standard CSV exports are fundamentally limited. When engineering a migration, you must account for data degradation.
| Feature | Migrates via CSV? | Technical Notes |
|---|---|---|
| Basic Tables (Text, Numbers) | Yes | Imports smoothly into Notion properties. |
| Relations & Lookups | Partial | Exports as flat text. Must be programmatically re-linked via Page IDs. |
| Attachments & Images | No | Coda exports temporary image URLs that eventually expire. |
| Formulas | No | Syntax is incompatible. Coda's imperative formulas (ModifyRows()) must be completely rewritten into Notion's Formula 2.0 language. |
| Automations & Buttons | No | Must be rebuilt using Notion's native triggers or external webhooks. |
| Scale/Slider Columns | No | No equivalent property type exists in Notion. Must be converted to a Number property or Select. |
| Conditional Formatting / Row Coloring | No | Notion databases do not support conditional formatting. Visual logic must be replaced with filtered views or status properties. |
| Cross-Doc Tables | No | Must be individually exported and re-linked. See Section 9 for details. |
| Pack-Sourced Data | Partial | Data already written to Coda tables migrates normally; the live Pack connection does not. External integrations must be rebuilt in Notion or via middleware. |
5. API Migration Architecture (The Engineering Approach)
For enterprise workspaces, CSV exports are not viable. Migrating relations and maintaining formatting requires building a pipeline between the Coda API and the Notion API.
The Endpoint Logic:
Data is extracted using Coda's row endpoint:
GET https://coda.io/apis/v1/docs/{docId}/tables/{tableId}/rowsThe JSON payload must then be mapped to Notion's block and property structures, pushing data via:
POST https://api.notion.com/v1/pagesThe Rate Limit Bottleneck: Notion's API enforces a strict rate limit of ~3 requests per second. If a Coda to Notion migration loops through a 10,000-row Coda database simultaneously, the Notion API will return a 429 Too Many Requests error. Migration scripts must include pagination and exponential backoff logic to ensure payloads are accepted.
API Constraints to Know:
- 100-block limit per request: When creating page content (e.g., migrating Coda canvas columns with rich text), the Notion API accepts a maximum of 100 blocks per
PATCH /v1/blocks/{block_id}/childrenrequest. Canvas columns with extensive content must be chunked into sequential API calls. - 2MB file upload limit: The Notion API enforces a 2MB limit for file uploads via external URL. Attachments exceeding this threshold must be hosted externally (e.g., S3) and linked rather than embedded.
6. Choosing Your Migration Method
Not every migration needs a custom pipeline. The right approach depends on the complexity of your workspace.
| Factor | CSV Export | API Pipeline | Hybrid (CSV + API) |
|---|---|---|---|
| Row count | < 1,000 rows | Any size | > 1,000 rows with mixed complexity |
| Relations | None | Any relational schema | Relations handled via API after CSV import |
| Attachments | None or few (manual download) | Many or large files | Attachments handled via API |
| Formula complexity | No imperative formulas | Imperative formulas requiring automation rebuilds | Simple formulas via CSV, complex logic via API |
| Best for | Quick moves of flat, standalone tables | Enterprise workspaces with relational integrity requirements | Mid-complexity workspaces where speed matters but some data needs programmatic handling |
Rule of thumb: If your workspace has zero relations, zero imperative formulas, and fewer than 1,000 rows, CSV is fine. If any of those conditions are false, you need at least a partial API approach.
7. Formula Translation: Coda vs. Notion Formula 2.0
One of the most complex migration phases is translating computational logic, as the two platforms treat formulas entirely differently.
Translating Data Formatting:
Simple text and math formulas can be mapped directly to Notion's Formula 2.0.
- Coda:
Concatenate([First Name], " ", [Last Name]) - Notion 2.0:
prop("First Name") + " " + prop("Last Name")
Handling formulas and automations (rebuild patterns)
The Imperative Formula Problem:
Coda formulas can take action (e.g., ModifyRows(), AddRow()). Notion formulas are strictly read-only; they can display data but cannot change the state of another database.
- Coda Logic: A button uses
ModifyRows([Inventory], [Status], "Shipped"). - Notion Translation: This cannot be solved with Formula 2.0. It requires building a Notion Database Automation that triggers an external webhook to execute the API patch.
8. Handling Relations & Linked Tables
When migrating relational databases (e.g., [Tasks] linked to [Projects]), the API execution order is critical.
- Migrate the Parent: Push the
[Projects]database to Notion first. - Migrate the Child: Push the
[Tasks]database second. - Map the IDs: Query the newly generated Notion Page IDs for the Parent database, and pass those exact IDs into the relation property of the Child payload during the
POSTrequest.
Overview and when to use the two-pass approach
A two-pass approach is mandatory when your schema includes bi-directional relations or complex circular parent-child dependencies. In pass one, you create all records as standalone pages without relations. In pass two, you update the pages to link them together using the captured IDs.
Prerequisites and API credentials
To execute this pipeline, you need a Coda API token (coda.io) and a Notion internal integration secret (developers.notion.com). Keep both tokens secure. Give the Notion integration only the capabilities it needs, and share only the target pages and databases with it.
Step 1: Read data from Coda (sample API calls & pagination)
Extract data using the GET /v1/docs/{docId}/tables/{tableId}/rows endpoint (coda.io). Because Coda paginates results, your script must pass the nextPageToken from each response into the pageToken parameter of the next request until no nextPageToken is returned (coda.io).
Step 2: Create Notion databases/pages (sample payloads)
Push the extracted data to Notion using the POST /v1/pages endpoint (developers.notion.com). During this first pass, omit all relation properties.
{
"parent": { "database_id": "your_notion_database_id" },
"properties": {
"Name": { "title": [ { "text": { "content": "Task 1" } } ] },
"Coda_Row_ID": { "rich_text": [ { "text": { "content": "i-abc123def" } } ] }
}
}Step 3: Capture and store ID mappings
When the Notion API responds with a 200 OK, it returns the new Notion Page ID. Store a key-value mapping of the original Coda Row ID to the new Notion Page ID (e.g., in a local JSON file or a Redis cache).
Step 4: Re-link relations using mapping
Iterate through your mapping. For every record that has a relation, look up the corresponding Notion Page IDs for the linked records. Send a PATCH /v1/pages/{page_id} request (developers.notion.com) containing the relation property array (developers.notion.com). A write replaces the whole relation, and one request can set at most 100 related pages (developers.notion.com).
Sample scripts (Node/Python)
Here is a minimal Node.js example for Step 1 (reading with pagination):
// Node.js: Fetch Coda rows with pagination
async function fetchCodaRows(docId, tableId, codaToken) {
let rows = [];
let pageToken = "";
do {
const url = `https://coda.io/apis/v1/docs/${docId}/tables/${tableId}/rows?limit=100${pageToken ? `&pageToken=${pageToken}` : ""}`;
const res = await fetch(url, { headers: { "Authorization": `Bearer ${codaToken}` } });
const data = await res.json();
rows = rows.concat(data.items);
pageToken = data.nextPageToken;
} while (pageToken);
return rows;
}Here is a Python example for Step 4 (patching relations with backoff):
# Python: Patch Notion relations
import requests
import time
def update_notion_relation(page_id, relation_prop, related_ids, token):
url = f"https://api.notion.com/v1/pages/{page_id}"
headers = {
"Authorization": f"Bearer {token}",
"Notion-Version": "2022-06-28",
"Content-Type": "application/json"
}
payload = {
"properties": {
relation_prop: { "relation": [{"id": r_id} for r_id in related_ids] }
}
}
while True:
res = requests.patch(url, json=payload, headers=headers)
if res.status_code == 429:
time.sleep(int(res.headers.get("Retry-After", 1))) # honour Retry-After
continue
return res.json()9. Handling Cross-Doc Tables and Packs
Enterprise Coda workspaces frequently use cross-doc tables — tables synced across multiple docs — and Packs, which pull live data from external services like Salesforce, GitHub, or Slack.
Cross-Doc Tables: These are effectively read-only synced copies. During migration, each source table must be identified and exported independently. Synced copies in other docs should be ignored to avoid duplicating data. In Notion, the closest equivalent is a linked view of a shared database, but the sync behavior is different: Notion's linked views always reference a single source database rather than creating independent copies.
Packs: Any data a Pack has already written into Coda table rows migrates like normal table data. However, the live connection to the external service does not transfer. After migration, these integrations must be rebuilt using Notion's native integrations, Zapier/Make workflows, or custom API connections.
10. Handling Attachments & Large Databases
- Attachment Expiry: Coda hosts its own attachments. When exported, these become temporary URLs. A proper migration script must programmatically download the file from Coda, store it in an AWS S3 bucket (or similar), and upload it to the Notion API as a distinct file object. Keep in mind the 2MB per-file API upload limit noted in Section 5.
- Validation Keys (Coda_Row_ID): When migrating thousands of rows, add a temporary text property to your Notion database called Coda_Row_ID. Map the original Coda row identifier here. This allows engineers to run automated data-integrity checks and execute upserts if a batch fails, preventing duplicate entries.
11. Rollback and Failure Recovery
Migrations fail partway through more often than most teams expect. A rate-limit cascade, a malformed payload, or a network timeout can leave your Notion workspace in a half-populated state.
Error handling and rate-limit strategies
Error handling and rate-limit strategies dictate the stability of your pipeline. The Notion API gives each connection 180 requests per minute (an average of 3 per second), or 600 per minute on Business and Enterprise plans (developers.notion.com). If you exceed this, the API returns a 429 response with a rate_limited error code. Your scripts must catch this status code, wait for the number of seconds in the Retry-After header, and fall back to exponential backoff if the header is missing.
Build for recovery from the start:
- Use the Coda_Row_ID property (described in Section 10) as your reconciliation key. After a failure, query your Notion database for existing Coda_Row_IDs, diff against the source, and resume only the missing rows.
- Log every API response. Store the HTTP status code and Notion page ID for each row pushed. When a batch fails at row 3,000 of 10,000, the log tells you exactly where to restart.
- Idempotent writes: Design your migration script so that re-running it does not create duplicates. Before creating a new page, check whether a page with that Coda_Row_ID already exists. If it does, update it instead.
- Lock the source. Prevent edits to the Coda doc during migration. If someone modifies a row after it has been exported but before the migration completes, the data will silently diverge. Load the bulk of the doc during the week, then freeze edits and run a short final delta before the team starts work on Monday.
12. Pre-Migration Audit Checklist
Before exporting a single byte of data, audit your Coda workspace:
- Audit Tables: Delete unused columns, hidden rows, and archived sections to reduce payload size.
- Review Formulas: Document all complex logic (e.g., lead scoring) that will need to be rebuilt.
- Identify Automations: Map out which native Coda buttons need to be replaced by Make or Zapier workflows.
- Identify Cross-Doc Tables: Trace every cross-doc sync back to its source table. Only migrate source tables.
- Inventory Packs: List every Pack in use and the data it sources. Decide whether each integration will be rebuilt in Notion or replaced.
- Export Attachments: Download all critical files stored in Coda rows locally.
- Data Backup: Lock the Coda doc to prevent changes during the migration window.
Testing and QA checklist
Validating relation integrity before cutover requires a structured test plan.
| Test Phase | Validation Check | Pass Criteria |
|---|---|---|
| Record Parity | Compare total row count in Coda to total page count in Notion. | Counts match exactly per database. |
| Relation Integrity | Sample 50 records with known parent-child links. | Notion relation properties point to the correct target pages. |
| Data Types | Verify dates, numbers, and select fields. | No fields degraded to plain text. |
| Delta Sync | Run the final 30-minute delta load. | Only modified/new records are updated; no duplicates created. |
To create a migration checklist, read our blog "How to Create a Data Migration Checklist (Copy-Paste Template Included)" for a ready-to-use template and step-by-step guidance.
13. Sources & References
- Notion API Endpoints & Rate Limits: Developer documentation detailing the POST /v1/pages architecture and the 3 request/second throttling limit. (developers.notion.com/reference)
- Coda API Documentation: Specifications for extracting row data and canvas payloads. (coda.io/developers/apis)
- Notion Formula 2.0 Syntax: Technical overview of Notion's updated read-only property logic. (notion.so/help/formulas)
Frequently Asked Questions
- How do I migrate from Coda to Notion?
- Simple tables can be exported as CSVs and imported. Complex workspaces with relations and formulas require querying the Coda API and mapping the JSON payloads to Notion's POST /v1/pages endpoint to prevent data degradation.
- Can relational databases be preserved during migration?
- Yes, but not via standard CSV exports. Relations must be mapped programmatically by extracting the Coda Row ID and matching it to the newly generated Notion Page ID before pushing the data.
- Do Coda Packs migrate to Notion?
- No. Coda Packs are native API plugins specific to Coda's architecture. Notion uses 'Connections' and native integrations. Workflows previously powered by Coda Packs (like Jira or Salesforce updates) must be completely rebuilt using Notion’s native automations or external middleware like Truto.
- What is the Notion equivalent of Coda's Action Formulas?
- Notion’s Formula 2.0 is strictly read-only and cannot replicate Coda's imperative actions like ModifyRows() or AddRow(). To achieve similar functionality in Notion, you must set up a Notion Database Automation that triggers an external webhook to execute an API patch.
- Will I lose my document formatting during the migration?
- A standard CSV export will strip out canvas formatting, nested sections, and interactive buttons, leaving only flat text. Preserving the visual hierarchy requires extracting the Coda Canvas JSON and mapping it programmatically to Notion's Block architecture.
- How are attachments and files handled during the transition?
- Exporting Coda tables to CSV generates temporary URLs for attachments that will eventually expire. A permanent migration requires scripts to programmatically download these assets from Coda's servers and upload them directly to the Notion API as new file objects.
- How long does a complex migration take?
- A manual migration of enterprise architectures with thousands of rows and cross-doc relations can take internal teams weeks of trial and error, often hampered by API rate limits. Utilizing an optimized data mapping pipeline drastically reduces this timeline. By applying a fast turnaround time, these complex workspace migrations are routinely completed in a matter of days.
- Is there a direct Coda to Notion migration tool?
- There is no native "one-click" import built into either platform. Middleware tools like Truto can bridge the APIs, but they require strict technical configuration and pagination logic to handle relational mapping correctly.