Notion to SharePoint Migration: The Complete Technical Guide
No native path exists from Notion to SharePoint. This guide covers what breaks, which methods work, and how to migrate databases, pages, files, and permissions correctly.
There is no native migration path from Notion to SharePoint. No connector, no importer, no "Move to SharePoint" button. Notion stores content as a recursive tree of JSON blocks; SharePoint stores content as modern pages with CanvasContent1 JSON, structured lists, and document libraries. The two architectures are fundamentally incompatible at the data-model level, which means every migration requires an extraction layer, a transformation layer, and a loading strategy — all custom.
This guide covers what actually breaks, which methods exist, and how to get your Notion workspace into SharePoint with the least data loss.
For a side-by-side comparison of the platforms before committing, see our Notion vs. SharePoint: The Definitive Guide for 2026.
Notion to SharePoint Migration: Why It Breaks
Notion's data model is blocks all the way down. Every paragraph, heading, image, toggle, callout, and database row is an individual block object with a unique ID. Blocks nest inside other blocks, and pages themselves are blocks. Databases are collections of pages where each row is a page. Relations link pages across databases. Rollups aggregate values from those linked pages. The Notion API requires recursive traversal to reconstruct any page — you call GET /v1/blocks/{block_id}/children, get up to 100 children per response, check has_children on each, and recurse. (developers.notion.com)
SharePoint's data model is sites, lists, libraries, and pages. A SharePoint site contains document libraries (for files), lists (structured data with typed columns), and site pages (modern .aspx pages rendered via CanvasContent1 JSON and web parts). There is no concept of infinite nesting — pages live in the Site Pages library, and hierarchy comes from site navigation and hub associations, not parent-child block relationships.
When you create modern pages through Microsoft Graph, only a fixed set of standard web parts is supported for programmatic create and edit operations. Attempts to use unsupported web parts cause API failures. (learn.microsoft.com)
That mismatch creates several categories of problems:
| Problem | Notion Behavior | SharePoint Behavior | Result |
|---|---|---|---|
| Page structure | Nested blocks, infinite depth | Flat page with sections and web parts | Hierarchy flattened or lost |
| Database → List | Relations, rollups, formulas per row | Typed columns (Choice, Lookup, Managed Metadata) | Relations break, formulas don't transfer |
| Permissions | Page-level sharing (Full Access, Can Edit, Can View) | Inherited permission model (Site → Library → Item) | Manual permission remapping required |
| Metadata | Created By, Created Time per page/block | Created By, Modified columns per list item | Native export strips authorship |
| Files | Embedded in blocks, hosted on Notion CDN | Stored in document libraries with metadata columns | File URLs expire, require re-download |
The Limitations of Native Notion Exports
Notion offers three export formats: Markdown & CSV, HTML, and PDF (Business/Enterprise only). None are designed for platform migration.
What the export produces
When you export a full workspace via Settings → General → Export all workspace content, Notion generates a ZIP file containing:
- Markdown files for pages (or HTML, depending on your choice)
- CSV files for each database (one per view — only the current or default view)
- Attached files and images
- An
index.htmlsitemap for navigation
Notion's documentation notes that workspace exports may exclude private pages the exporter cannot access, may omit content based on teamspace settings, and can take up to 30 hours to process. The resulting ZIP cannot be reuploaded to recreate the workspace. (notion.com)
For a deeper walkthrough of every export method and its quirks, see our Complete Guide to Exporting Notion Data.
What the export drops
Relations lose their programmatic links. CSV exports of relation properties sever the actual database connection. You get a flat text representation, not a usable relational reference. If you have a "Projects" database linked to a "Tasks" database, the exported CSV contains nothing SharePoint can use to rebuild that link. You can create a formula column like prop("Name") to extract the display text before exporting, but this is a lossy workaround that destroys the relational link itself.
Rollups and formulas vanish. These are computed server-side in Notion. CSV export may capture the current value of a formula column as static text, but not the formula logic. Rollups that aggregate across related databases are absent entirely. Notion's own docs recommend the page property item endpoint for formulas, rollups, and relations to get accurate, up-to-date values. (developers.notion.com)
Created By and Created Date are not preserved. The Notion API exposes created_by, created_time, last_edited_by, and last_edited_time as page properties, but the native export does not guarantee these fields carry over in a SharePoint-usable format. Once you import flat files into SharePoint, every item shows the migration account as the creator unless you explicitly script metadata preservation via the API.
Board and timeline views flatten. Kanban boards and timeline views revert to standard flat tables in CSV. The visual layout is gone.
Large workspaces may fail entirely. Notion's help center acknowledges the export function can have trouble completing for large workspaces. The recommended workaround is to export in smaller batches, page by page.
Notion workspace exports create deeply nested folder structures. On Windows, path lengths exceeding 260 characters will prevent the ZIP from extracting. Notion appends hexadecimal hashes to every file path, making this a common issue for large workspaces.
Why HTML export doesn't solve the SharePoint page problem
SharePoint modern pages do not accept arbitrary HTML. Page content is stored as CanvasContent1 — a specific JSON structure that defines sections, columns, and web parts. You can inject HTML inside a Text Web Part (innerHtml property), but SharePoint sanitizes it aggressively. Inline styles, custom classes, and non-standard elements get stripped.
The Microsoft Graph API for SharePoint Pages (GA since 2024) supports programmatic page creation, but only 14 Microsoft-provided web parts for create/edit operations. Collapsible sections are not supported via the API. You cannot replicate a Notion toggle list, a synced block, or an inline database view using the Pages API alone.
Notion also notes that callout blocks export as HTML because Markdown has no equivalent, and that Form view cannot be exported at all. (notion.com)
Take the native export anyway. It gives you rollback insurance, raw assets, and a defensible backup point before you start transforming data through the API.
DIY Workarounds: Power Automate, Zapier, and Embeds
Three workarounds appear frequently in search results. All three have hard limits that make them unsuitable for a full workspace migration.
Power Automate and Zapier
Both tools connect Notion and SharePoint through event-based triggers — "New Database Item," "Updated Page," "New Comment." They are designed for ongoing sync of new events, not bulk historical migration.
The Power Automate Notion connector (Independent Publisher) supports actions like Create a Page, Query a Database, Retrieve a Page, and Retrieve Block Children, with a documented throttling limit of 100 API calls per connection per 60 seconds. That is enough for light workflow automation, not for recursively walking large page trees. There is no "Get all pages from Notion workspace" trigger. (learn.microsoft.com)
On the Zapier side, specific limitations for the Notion integration include:
- Cannot retrieve files or media attachments through the standard integration
- Cannot access formula values in Notion databases
- Cannot update Relation properties — a documented limitation requiring a Code by Zapier workaround with raw API calls
- Each block is limited to 2,000 characters due to Notion's API restrictions
- Rate limited to an average of 3 requests per second per integration
- Multi-select updates overwrite existing values instead of appending
On the SharePoint side, Zapier's Create List Item and Update List Item actions do not support writing to Person/Group, URL/Hyperlink, or Managed Metadata column types. File triggers cap at 100 files per poll. (zapier.com)
Zapier and Power Automate work for keeping a small, ongoing sync alive between one Notion database and one SharePoint list. They cannot migrate an existing workspace with thousands of pages, nested content, and relational databases.
Embedding Notion in SharePoint
You can embed a live Notion page inside a SharePoint page using the Embed Web Part with the page's public share URL. SharePoint's Embed web part requires iframe-based embed code and HTTPS, and site collection admins can restrict which external domains are allowed. (support.microsoft.com)
This is not a migration — the data still lives in Notion. You get no SharePoint search indexing, no metadata, no permissions integration, no Microsoft Purview compliance coverage, and you still pay for both platforms. Use this only as a temporary bridge during a phased migration, not as permanent architecture.
Basic API scripts at scale
Writing a custom Python or Node.js script to pull data via the Notion API often fails at scale. The API paginates at 100 results per call and limits property references to 25 per call. A deeply nested page can hit block limits:
{
"object": "error",
"status": 400,
"code": "validation_error",
"message": "Cannot append more than 100 children in a single request."
}Rebuilding internal linking structure programmatically requires maintaining a mapping table of old Notion IDs to new SharePoint URLs — a task that breaks most basic scripts. You also have to handle file URL expiration, rate limiting, pagination cursors, and SharePoint's throttling responses on the write side.
Use Power Automate or Zapier for post-migration deltas and lightweight syncs. Use embeds when you intentionally want coexistence. Use a scripted API-to-API migration when the goal is to actually move the knowledge base.
Mapping Notion Databases to SharePoint Lists
This is where most of the migration complexity lives. You are moving from a flexible, schema-on-the-fly system to a strictly typed database. For a comprehensive field-by-field mapping reference, see our Guide to Mapping Notion Databases to SharePoint Lists.
Property-to-column translation
| Notion Property Type | SharePoint Column Type | Notes |
|---|---|---|
| Title | Single line of text (Title column) | Direct mapping |
| Text | Single/Multiple lines of text | Multi-line loses rich formatting |
| Number | Number | Direct mapping |
| Select / Status | Choice | Map option values 1:1 |
| Multi-select | Choice (allow multiple) or Managed Metadata | Managed Metadata is better for enterprise governance |
| Date | Date/Time | Notion uses ISO 8601; SharePoint accepts it |
| Person | Person or Group | Requires identity mapping (Notion email → Entra ID). If a user has left the company, the import fails unless you map them to a generic account |
| Checkbox | Yes/No | Direct mapping |
| URL | Hyperlink | Direct mapping |
| Email / Phone | Single line of text | No native email or phone column type in SharePoint |
| Relation | Lookup column | Requires the related list to exist first; limited to 12 lookup-type columns per view |
| Rollup | Calculated column (limited) | Cannot cross-reference other lists |
| Formula | Calculated column | Must be manually recreated; Notion formula syntax ≠ SharePoint formula syntax |
| Files & Media | Document Library link or Attachment | Files must be downloaded from Notion and re-uploaded. Libraries allow up to 250 GB per file; list attachments cap at 250 MB (learn.microsoft.com) |
| Created time | Created (built-in) | Only preserved if scripted via API; native export drops it |
| Created by | Created By (built-in) | Same — requires API-level metadata injection |
The two-pass loading approach
The extraction pattern matters as much as the field mapping. Use a stable external key in every target list — NotionPageId, NotionParentId, SourceURL, and LastSeenAt — then load in two passes:
- First pass: Create base items in all SharePoint lists with source IDs. Upload files. Do not resolve lookups or internal links yet.
- Second pass: Populate lookup fields, rewrite internal page links, and stamp any precomputed rollup or formula values once every target item has a durable SharePoint ID.
This two-pass approach is what prevents relations from collapsing into broken text. It also respects the dependency order — independent lists (e.g., "Clients") must exist and be populated before dependent lists (e.g., "Projects" linked to "Clients") can have their Lookup columns resolved.
lists:
Projects:
key: NotionPageId
source: Projects database
columns:
Title: title
Status: choice
Tags: multi_select
Owner: person_or_group
LegacyOwnerName: text
SourceURL: hyperlink
relations:
ProjectToClient:
mode: second_pass_lookup
files:
library: Knowledge AssetsThe lookup column ceiling
Notion databases can have unlimited relation properties. SharePoint views are limited to 12 lookup-type columns (which includes Lookup, Person/Group, and Managed Metadata columns). If your Notion database has 15 relation properties, you cannot display them all in a single SharePoint list view. (learn.microsoft.com)
Audit your Notion databases before migration. Identify which relations are actively used versus legacy. Consolidate where possible, and consider Managed Metadata with a shared Term Store instead of Lookup columns for categorical data.
Rollups don't cross lists
This is the single biggest structural loss. In Notion, a rollup on Database A can count, sum, or aggregate values from related rows in Database B. SharePoint has no native equivalent that works across lists. Your options:
- Flatten rollup values at export time — capture the computed value as a static number or text column. You lose the live calculation.
- Use Power Automate to recalculate values on a schedule — works for simple counts but adds maintenance overhead.
- Use a SharePoint Calculated Column within a single list for same-list computations only.
SharePoint lists can hold up to 30 million items, but the List View Threshold kicks in at 5,000 items per view query. Index your most-filtered columns before importing data. You cannot create indexes on lists that already exceed 20,000 items without workarounds. (learn.microsoft.com)
Handling Nested Pages, Files, and Permissions
Nested pages → SharePoint site structure
Notion's infinite nesting (Page → Sub-page → Sub-sub-page) does not map to SharePoint's flat page model. In the Notion API, a child page appears as a child_page block, and complete extraction requires following that tree recursively. (developers.notion.com)
You have three options for the target structure:
- Flatten into the Site Pages library with metadata columns indicating the original hierarchy (e.g., a "Parent Page" text column or a Managed Metadata term set that mirrors the Notion tree). Most common for knowledge bases under ~500 pages.
- Map top-level Notion pages to separate SharePoint sites, connected via a Hub Site. Better for large organizations where different Notion teamspaces had different audiences. For the trade-offs between hubs and subsites, see our SharePoint architecture guide.
- Use SharePoint page sections and navigation to replicate the visual hierarchy, even though the pages are technically flat.
Preserve source page IDs and parent IDs during migration so you can rebuild hierarchy deliberately instead of trusting folder names alone.
Extracting and re-hosting files
Files embedded in Notion blocks are hosted on Notion's CDN with signed, time-limited URLs — they expire, typically within one hour of the API call. You cannot copy Notion file URLs into SharePoint and expect them to work long-term. (developers.notion.com)
The migration process for files:
- Extract file URLs via the Notion API (each File block returns a
file.urlandfile.expiry_time). - Download the file before the URL expires.
- Upload to a SharePoint Document Library via Microsoft Graph API or the SharePoint REST API.
- Update the reference in the corresponding SharePoint page or list item to point to the new SharePoint-hosted URL.
For workspaces with thousands of embedded files, this requires a queue-based pipeline that respects both Notion's 3 requests/second rate limit and SharePoint's throttling limits. If your migration runs for hours or days, re-fetch blocks or pages to refresh download links when needed — do not cache the URLs blindly.
On the SharePoint side, the decoded file path cannot exceed 400 characters. Files can be up to 250 GB in document libraries, but list item attachments cap at 250 MB. Keep files in document libraries with controlled folder rules, not stuffed into list attachments. (learn.microsoft.com)
Permission mapping: Notion → SharePoint
Notion and SharePoint have fundamentally different permission architectures.
Notion permissions are page-centric. Each page can have individual sharing settings: Full Access, Can Edit, Can Edit Content, Can Comment, or Can View. Sub-pages inherit by default but can override. Teamspaces add another layer. Workspace exports exclude pages the exporter cannot access. (notion.com)
SharePoint permissions are inheritance-based. Permissions flow from Site → Library/List → Item. Breaking inheritance at the item level is technically supported (up to 50,000 unique security scopes per list or library), but Microsoft recommends staying under 5,000 for performance. When a list, library, or folder exceeds 100,000 items, you can no longer break or restore inheritance at that level. (learn.microsoft.com)
The practical mapping:
| Notion Level | SharePoint Equivalent |
|---|---|
| Workspace Members | Site Members (SharePoint Group or Entra ID security group) |
| Teamspace | Separate SharePoint site or Hub association |
| Page-level "Can Edit" | Unique permissions on a specific page/item (break inheritance) |
| Page-level "Can View" | Read permission on specific item |
| Guests | External sharing or Entra B2B guest accounts |
Do not break inheritance on every single SharePoint list item to replicate Notion's per-page permissions. This creates a performance and governance nightmare. Group content by audience into separate lists, libraries, or sites, and apply permissions at the container level.
For detailed guidance on structuring access, see our Definitive Guide to SharePoint Permissions & Security.
The API-to-API Approach: How a Proper Migration Works
The only method that preserves block structure, metadata, relations, and file references is a direct API-to-API pipeline: read from the Notion API, transform in middleware, write to the SharePoint/Microsoft Graph API.
Step 1: Extract from Notion API
# Pseudocode: Recursive block extraction
def extract_page(page_id):
page = notion.pages.retrieve(page_id)
blocks = []
cursor = None
while True:
response = notion.blocks.children.list(
block_id=page_id,
start_cursor=cursor,
page_size=100
)
for block in response['results']:
blocks.append(block)
if block.get('has_children'):
block['children'] = extract_blocks(block['id'])
if not response['has_more']:
break
cursor = response['next_cursor']
return {'page': page, 'blocks': blocks}This runs recursively for every nested block. At 3 requests per second, a 200-page workspace with an average of 50 blocks per page requires roughly 3,500+ API calls — about 20 minutes of extraction time for content alone, before files.
Step 2: Transform blocks → SharePoint content
Notion blocks need to be converted to one of:
- Text Web Part HTML (
innerHtml) for rich content pages - SharePoint List items for database rows
- Document Library entries for files
Block types like heading_1, paragraph, bulleted_list_item, and code map to HTML tags inside a Text Web Part. Block types like image, file, and video require file download and re-upload. Block types like child_database require creating a corresponding SharePoint List.
Rewrite internal links only after destination URLs exist. Create all target pages first, collect the final SharePoint URLs or IDs, then run a link-rewrite pass across page bodies and lookup references. Otherwise you end up with pages that load but do not navigate.
Step 3: Write to SharePoint via Microsoft Graph
POST /sites/{site-id}/pages
Content-Type: application/json
{
"@odata.type": "#microsoft.graph.sitePage",
"name": "migrated-page.aspx",
"title": "Migrated from Notion",
"pageLayout": "article",
"canvasLayout": {
"horizontalSections": [{
"layout": "oneColumn",
"columns": [{
"width": 12,
"webparts": [{
"@odata.type": "#microsoft.graph.textWebPart",
"innerHtml": "<h2>Section Title</h2><p>Content here...</p>"
}]
}]
}]
}
}The Graph Pages API supports 14 built-in web parts for programmatic create/edit. Collapsible sections and some layout types must be handled differently or manually configured post-migration.
For lists, use POST /sites/{site-id}/lists to create the list schema and POST /sites/{site-id}/lists/{list-id}/items to create items. The API calls are straightforward — the hard part is the transformation layer around them. (learn.microsoft.com)
Step 4: Validate
Post-migration validation should check:
- Page count match — same number of pages in SharePoint as source Notion pages
- List item count match — same number of rows per database/list
- File integrity — spot-check file sizes and content hashes
- Relation integrity — verify Lookup columns resolve to the correct target items
- Permission audit — confirm the right groups have access to the right sites/lists
- Internal links — test random links across pages
- Search indexing — ensure SharePoint Search has crawled the new content
Never sign off on row counts alone. A migration can be numerically complete and still be broken if permissions, links, page layouts, or file downloads fail.
When to Call in Help
If your workspace has fewer than 50 pages, no relational databases, and no embedded files — you can get away with a manual export-and-rebuild. Export as HTML, copy content into SharePoint pages by hand, and move on.
If your workspace has hundreds of pages, relational databases, thousands of embedded files, and granular permissions — the API-to-API pipeline is the only path that does not lose data. Building that pipeline yourself means handling Notion's recursive block model, rate limiting, file URL expiration, SharePoint's CanvasContent1 format, Graph API throttling, dependency-ordered list creation, and permission mapping — all before testing.
At ClonePartner, we have built this pipeline across multiple Notion-to-SharePoint migrations. Our scripts extract the full block tree via the Notion API, preserve Created By and Created Date metadata, re-host files into Document Libraries, map database relations to SharePoint Lookups in the correct dependency order, and validate every record post-migration. When teams cannot freeze authoring during migration, we pair the initial load with a delta sync phase so cutover is about validation, not panic.
Migrations fail when you treat them as a software problem instead of a data engineering problem. We bring the accountability and precision to move your knowledge base without data loss.
Frequently Asked Questions
- Can you migrate Notion databases to SharePoint lists?
- Yes, but not with a native export. Notion relations lose their programmatic links in CSV, rollups disappear, and formulas don't transfer. You need to map each Notion property to a SharePoint column type (e.g., Select → Choice, Relation → Lookup) and use the Notion API plus Microsoft Graph API to transfer data with structure intact. SharePoint views are limited to 12 lookup-type columns, so audit your relations first.
- Does Notion have a SharePoint migration tool?
- No. There is no native Notion-to-SharePoint migration tool from either vendor. The available approaches are: manual export (CSV/HTML) and rebuild, Zapier/Power Automate for small ongoing syncs, embedding Notion pages in SharePoint as a temporary bridge, or custom API-to-API scripting for full-fidelity migration.
- What data is lost when exporting from Notion?
- Native Notion exports lose: relational links between databases, rollup and formula logic, Created By and Created Date metadata, board and timeline view layouts, live embeds, and synced block references. Large workspace exports may also take up to 30 hours or fail entirely, and may exclude private pages the exporter cannot access.
- Can Zapier or Power Automate handle a full Notion workspace migration?
- Not effectively. The Power Automate Notion connector has a 100-calls-per-60-seconds throttle, and Zapier cannot retrieve file attachments, formula values, or relation properties from Notion. On the SharePoint side, Zapier cannot write to Person/Group or Managed Metadata columns. Both are useful for small ongoing syncs, not bulk historical migration.
- How do you map Notion permissions to SharePoint?
- Map Notion workspace members to SharePoint site member groups and Notion teamspaces to separate SharePoint sites. SharePoint supports up to 50,000 unique security scopes per list, but Microsoft recommends staying under 5,000. Avoid breaking inheritance on every individual item — restructure content by audience into separate containers and apply permissions at the site, library, or folder level.