Overview
The Notion Connector provides functionality to retrieve pages and database rows from Notion and register them in the Fess index. It authenticates as a Notion internal integration and indexes everything that the integration has been shared with.
This feature requires the fess-ds-notion plugin.
Supported Content
Pages (the text of their blocks)
Database rows, together with their property values
Notion-hosted file and PDF block attachments (each indexed as its own document)
Unresolved page comments (folded into the page document)
The following are out of scope:
Files that are only linked from Notion (external files); only Notion-hosted
fileandpdfblocks are downloadedPages and database rows that are in Notion’s trash
Per-page access control (see “Permissions” below)
Prerequisites
Plugin installation is required
A Notion internal integration must be created
The integration must be shared with the pages and databases to crawl
Plugin Installation
Install from “System” -> “Plugins” in the admin console, or run bin/fess-setup install plugin fess-ds-notion and restart Fess. See Plugin for details.
Notion Integration Setup
Create an internal integration in Notion and copy its “Internal Integration Secret”.
On the integration’s “Configuration” page, enable the following capabilities:
Capability Required Used for Read content Always Reading pages, blocks, databases and database rows. Without it, every request fails with 401 unauthorizedand the crawl aborts immediatelyRead comments Only if include_comments=true(the default)Fetching page comments. If the capability is missing, a warning is logged once and comment crawling is disabled for the rest of that crawl; the crawl itself does not fail Read user information Optional Resolving user IDs ( created_by,last_edited_byandpeopleproperties) to display names. Without it, the user ID is indexed instead of the nameShare the integration with every page or database it should crawl in Notion. Adding a capability alone does not grant access to any content.
Configuration
Configure in the admin console under “Crawler” -> “Data Store” -> “Create New”.
Basic Settings
| Item | Example |
|---|---|
| Name | Company Notion |
| Handler Name | NotionDataStore |
| Enabled | On |
Parameter Configuration
Parameter List
| Parameter | Required | Description |
|---|---|---|
token | Yes | The internal integration secret. It is not passed to the script and is not logged |
root_page_ids | No | Comma-separated IDs of the pages to start crawling from. When both this and database_ids are empty, every page and database shared with the integration is discovered through Notion’s search |
database_ids | No | Comma-separated IDs of the databases to start crawling from. Every row of each database is indexed |
include_comments | No | Fetch each page’s unresolved comments and add their text to notion.content and notion.comments (default: true) |
include_files | No | Download Notion-hosted file and pdf block attachments, extract their text and index each as its own document (default: true) |
max_file_size | No | An attachment larger than this many bytes is skipped entirely, not even indexed with empty content (default: 10485760) |
include_pattern | No | Regular expression that a document’s notion.url must match to be indexed (see “Crawl Scope”) |
exclude_pattern | No | Regular expression that a document’s notion.url must not match (see “Crawl Scope”) |
request_interval | No | Milliseconds to wait before each Notion API request, to stay under Notion’s rate limit of about 3 requests per second (default: 350) |
max_retry_count | No | Maximum number of retries for a request that ends in 429, 5xx or 529, or in a transport failure such as a timeout (default: 5) |
connection_timeout | No | Connection timeout of each API request, in milliseconds (default: 10000) |
read_timeout | No | Read timeout of each API request, in milliseconds (default: 30000) |
ignore_error | No | When text extraction fails for an attachment that was downloaded, index the attachment with empty content instead of failing the document (default: true). A download failure always fails the document regardless of this setting |
Note
For a numeric parameter, a blank or non-numeric value falls back to its default with a warning in the log. So does a negative request_interval or max_retry_count (0 is valid for both), and a true/false parameter set to anything else.
Script Configuration
Available Fields
Every document, whether a page, a database row or an attachment, is exposed to the script under the notion object.
| Field | Description |
|---|---|
notion.type | page for a page or a database row, file for an attachment |
notion.id | The page ID, or the block ID for an attachment |
notion.title | The page title (the property of type title, whatever its name), or the file name of an attachment |
notion.content | For a page: the non-title property values (database rows only), the text of its blocks and its comment text, joined together. For an attachment: the extracted text |
notion.url | The Notion URL of the page. For an attachment, the URL of the containing page with #<block ID> appended |
notion.mimetype | text/html for a page, the detected MIME type for an attachment |
notion.created_time | Creation time of the page. Not available for an attachment |
notion.last_edited_time | Last edited time of the page or of the attachment block |
notion.created_by | Creator of the page: the name if Notion returned one, otherwise the user ID. Not available for an attachment |
notion.last_edited_by | Last editor, in the same format. Not available for an attachment |
notion.parent_id | ID of the parent page, database, data source or block. Empty for a page placed directly in the workspace. Not available for an attachment |
notion.properties | Map of database column name to formatted text, for a database row. Empty for a normal page. Not available for an attachment |
notion.comments | The comment text of the page, joined together. Empty when include_comments=false or the page has no comments. Not available for an attachment |
notion.page_url | URL of the containing page. Attachments only |
notion.page_title | Title of the containing page. Attachments only |
notion.size | Size of the attachment in bytes, when Notion reported it. Attachments only |
Crawl Scope
When neither
root_page_idsnordatabase_idsis set, the connector uses Notion’s search to discover every page and database shared with the integration. Notion does not guarantee that search is exhaustive; if some content is missing from the index, setroot_page_idsordatabase_idsexplicitly.From each starting point, every child page and child database reachable from it is crawled too, so a single root page is usually enough to reach a whole section of a workspace.
include_patternandexclude_patterndecide only whether the document of that page (or attachment) is indexed. They never stop the connector from discovering the descendants: the blocks of a page are always walked before the pattern is checked against the page’s own URL, so the children of an excluded page are still crawled and indexed under their own URLs. To exclude a whole subtree, the pattern must match every URL under it, not just its root.A page or database row that is in Notion’s trash is skipped.
Permissions
The Notion API does not expose the sharing settings of a page, so this connector cannot synchronize per-page access control. Every document is indexed with only the permissions set in the “Permissions” field of the data store configuration.
Warning
Everything shared with the integration becomes searchable by the roles granted in that field, regardless of who can see it in Notion. Share the integration only with the pages and databases that those roles should be able to search.
Error Handling
The crawl aborts only on HTTP
401(the token cannot authenticate) or on a429response that namespublic_api_request_blocked(Notion’s rate limiter is blocking the integration). The crawl also aborts if Notion’s search fails while neitherroot_page_idsnordatabase_idsis set, because there is nothing to start from.Any other failure is handled per document: a
404or403for one page, database, comment thread or block, or running out ofmax_retry_counton one request, is logged and skipped, and the crawl continues.Every per-document failure is recorded under “Crawler” -> “Failure URL” with the URL of that document (the Notion URL of a page,
<page URL>#<block ID>for an attachment). The signed download URL of an attachment is never recorded or logged.With
keep_expires_docs=falsein the parameters, documents of the data store that were not re-indexed in a crawl are deleted at the end of it, and that includes a crawl that aborts. By default the documents carry an expiration and stay in the index until it passes.
Usage Examples
Crawl a Specific Page Tree
Parameters:
Script:
Crawl a Database
Parameters:
Script:
Troubleshooting
The crawl aborts immediately with 401
Check that the token is the “Internal Integration Secret” of the integration and that the “Read content” capability is enabled.
Pages are missing from the index
Verify that the integration has been shared with the page or database
If you rely on discovery, set
root_page_idsordatabase_idsexplicitly, because Notion’s search is not guaranteed to be exhaustiveCheck
include_patternandexclude_patternCheck “Crawler” -> “Failure URL” for documents that failed individually
Reference Information
Data Store Connector Overview - Data Store Connector Overview
Slack Connector - Slack Connector
Data Store Crawling - Data Store Configuration Guide
Failure URL - Failure URL
Plugin - Plugin Management Guide
Comments are not indexed
Enable the “Read comments” capability of the integration. Without it, a warning is logged once and comments are skipped for that crawl. Only unresolved comments are indexed.