Notion Extractor Plugin Deep Dive
Overview
The Notion Extractor plugin extracts content from Notion pages and converts it to clean markdown for use as source material during work generation. It supports both public pages (via the free Splitbee API) and private pages (via the official Notion API with an integration key). The plugin is additive -- it only handles Notion URLs (notion.so, notion.site) and delegates all other URLs to the default content extractor.
Architecture
The plugin implements two interfaces: IPlugin and IContentExtractorPlugin. It delegates Notion-specific logic to a dedicated service:
NotionService-- handles page ID extraction from URLs, content fetching via two API backends, and block-to-markdown conversion.
NotionExtractorPlugin
|-- NotionService
|-- Splitbee API (https://notion-api.splitbee.io) [public pages]
|-- Official Notion API (https://api.notion.com) [private pages]
The plugin is not a system plugin and must be explicitly enabled by the user. It is marked as supplementary: true, meaning it augments rather than replaces the default content extractor. The canExtract method is the key routing mechanism: it returns true only for Notion URLs, allowing non-Notion URLs to fall through to other extractors.
Configuration
Environment Variables
| Variable | Required | Description |
|---|---|---|
| N/A | -- | No environment-variable fallbacks |
Settings Schema
interface NotionExtractorSettings {
apiKey?: string; // Notion integration API key (optional, x-secret, user-scoped)
useSplitbeeForPublicPages: boolean; // Use Splitbee API for public pages (default: true)
timeout: number; // HTTP request timeout in ms (default: 30000, range: 5000-120000)
}
configurationMode: defaults to standard plugin settings resolution.apiKeyis optional -- public pages can be extracted without any key.- The 4-level settings hierarchy applies: work > user > admin > environment.
Capabilities
| Capability | Description |
|---|---|
content-extractor | Extracts Notion page content as markdown |
Supported output formats: text and markdown.
API Reference
Content Extraction
| Method | Signature | Description |
|---|---|---|
canExtract | (url: string) => Promise<boolean> | Returns true for notion.so and notion.site URLs |
extract | (options: ContentExtractionOptions) => Promise<ContentExtractionResult> | Extracts a single Notion page |
extractBatch | (urls: readonly string[], options?) => Promise<readonly ContentExtractionResult[]> | Sequentially extracts multiple Notion pages |
isAvailable | () => Promise<boolean> | Always returns true |
getSupportedFormats | () => readonly ('text' | 'html' | 'markdown')[] | Returns ['text', 'markdown'] |
Implementation Details
URL Detection
The canExtract method uses a regex to match Notion URLs:
/^([\w-]+\.)?notion\.(so|site)$/.test(parsed.hostname);
This matches notion.so, notion.site, www.notion.so, and any subdomain like myworkspace.notion.site.
Page ID Extraction
NotionService.extractNotionPageId(url) parses Notion URLs in multiple formats:
- Standard 32-character hex IDs (e.g.,
notion.so/Page-Title-abc123def456...) - UUIDs with dashes (e.g.,
12345678-1234-1234-1234-123456789012) - IDs embedded in complex URL paths with query parameters
The extracted ID is normalized to UUID format with dashes for API compatibility.
Dual API Strategy
The extraction flow depends on configuration:
-
Splitbee API (default for public pages): Calls
https://notion-api.splitbee.io/v1/page/{pageId}which returns the full block tree as JSON. TheNotionServicethen recursively converts blocks to markdown. -
Official Notion API (for private pages): When an API key is provided and
useSplitbeeForPublicPagesisfalse, the plugin useshttps://api.notion.com/v1/pages/{pageId}for page metadata andhttps://api.notion.com/v1/blocks/{blockId}/childrenfor block content. Blocks are fetched recursively up to 10 levels deep with cursor-based pagination. -
Fallback: If the official API fails, the plugin falls back to the Splitbee API for public pages.
Block-to-Markdown Conversion
The NotionService converts Notion blocks to markdown, supporting:
- Headings (
header,sub_header,sub_sub_header/heading_1,heading_2,heading_3) - Paragraphs and text with rich formatting (bold, italic, strikethrough, code, links)
- Bulleted and numbered lists with nesting
- To-do items with checkboxes
- Code blocks with language detection
- Quotes and callouts
- Images, videos, embeds, and bookmarks
- Toggle blocks (converted to
<details>elements) - Dividers
- Collection views / databases (rendered as markdown tables)
- Equations
Cycle Prevention
Both the Splitbee and official API extraction paths maintain a processingHistory set of already-visited block/page IDs to prevent infinite recursion in pages with circular references.
Batch Extraction
extractBatch processes URLs sequentially (not in parallel) with a 200ms delay between requests to avoid rate limiting the Notion/Splitbee APIs.
Usage Examples
// Check if a URL is a Notion page
const isNotion = await notionPlugin.canExtract('https://notion.so/My-Page-abc123...');
// true
// Extract a public Notion page (no API key needed)
const result = await notionPlugin.extract({
url: 'https://myworkspace.notion.site/My-Public-Page-abc123...',
settings: {}
});
if (result.success) {
console.log(result.title); // Extracted page title
console.log(result.markdown); // Full page as markdown
}
// Extract a private Notion page
const privateResult = await notionPlugin.extract({
url: 'https://notion.so/Private-Page-abc123...',
settings: {
apiKey: 'ntn_...',
useSplitbeeForPublicPages: false
}
});
// Batch extract multiple Notion pages
const results = await notionPlugin.extractBatch(['https://notion.so/Page-1-...', 'https://notion.so/Page-2-...'], {
settings: { apiKey: 'ntn_...' }
});
Rate Limiting & Quotas
- Splitbee API: No documented rate limits, but a 200ms delay between batch requests is applied as a courtesy.
- Official Notion API: 3 requests per second per integration. The plugin respects this by processing batch extractions sequentially.
- Recursive depth: Block fetching is limited to 10 levels deep to prevent excessive API calls on deeply nested pages.
- Pagination: The official API uses cursor-based pagination, fetching all children blocks per parent.
Error Handling
- Not a Notion URL: Returns
{ success: false, error: 'Not a Notion URL...' }if the URL does not match Notion domains. - Invalid page ID: Returns
{ success: false, error: 'Could not extract Notion page ID from URL' }. - Service not initialized: Returns
{ success: false }if the plugin was not properly loaded. - Official API failures: Caught and logged; the plugin falls back to the Splitbee API for public pages. Specific error codes handled:
401: Invalid API key or insufficient permissions404: Page not found or not shared with the integration
- Empty or private pages via Splitbee: Throws a descriptive error when no data is returned.
- All errors in
extractare caught and returned as{ success: false, error: message }rather than thrown, ensuring the pipeline can continue with other sources.
Related Plugins
- Firecrawl Plugin Deep Dive -- general-purpose web content extractor.
- Exa Plugin Deep Dive -- search and content extraction with neural capabilities.
- Notion Plugin -- overview documentation for the Notion Extractor plugin.