# Notion Scraper & Knowledge Base Sync (`fetchfinch/notion-scraper-knowledge-base-sync`) Actor

Export public or private Notion pages, wikis, and databases as Markdown, JSON, HTML, typed rows, assets, or RAG-ready chunks—with recursive crawling and incremental sync.

- **URL**: https://apify.com/fetchfinch/notion-scraper-knowledge-base-sync.md
- **Developed by:** [Fetch Finch](https://apify.com/fetchfinch) (community)
- **Categories:** Automation, Developer tools, AI
- **Stats:** 1 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $11.00 / 1,000 page exporteds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

**Notion Scraper & Knowledge Base Sync**\
Turn Notion pages, wikis, and databases into clean data you can actually use.\
This Actor extracts public Notion content without a login. For private content, add a read-only Notion integration token. It produces a predictable dataset for exports, backups, search indexes, AI assistants, RAG pipelines, automations, and scheduled knowledge-base syncs.\
**Why use this Actor?**

- **No token required for public pages.** Paste a notion.site, notion.so, or Notion custom-domain URL.
- **Real structured extraction.** Public pages are read from Notion's record data, not flattened from a browser screenshot or fragile page text.
- **Four purpose-built output modes.** Export whole pages, typed database rows, individual blocks, or semantic RAG chunks.
- **Useful formats.** Choose Markdown, plain text, HTML, typed block JSON, or any combination.
- **Recursive wiki crawling.** Follow child pages and, optionally, the body of every database row page with cycle and depth protection.
- **Typed database values.** Dates, numbers, checkboxes, selects, people, relations, files, and generated metadata are normalized while raw values remain optional.
- **Incremental sync.** Stable hashes identify new, updated, and unchanged pages. Scheduled runs can emit changes only.
- **Portable assets.** Optionally preserve images/files in private run storage and build a ZIP bundle per page.
- **Honest partial failures.** One inaccessible page does not have to fail a batch. Errors are normalized and never attached to a paid result event.\
  **Quick start**\
  The default input is enough for a public page:\
  {\
     "startUrls": \[\
       {\
         "url": "https://darshgupta.notion.site/Getting-Started-1236ce47943c43fd8bbe8a236a25b9a6"\
       }\
     ]\
   }\
   \
  For an embedding-ready knowledge base:\
  {\
     "startUrls": \[{ "url": "https://your-team.notion.site/Handbook-..." }],\
    "crawlSubpages": true,\
     "crawlDatabasePages": true,\
     "outputMode": "chunks",\
     "formats": \["markdown", "text"],\
     "targetChunkTokens": 700,\
     "maxChunkTokens": 1000,\
     "overlapTokens": 80,\
     "syncKey": "production-handbook",\
     "emitOnlyChanges": true\
   }\
   \
  For a database export:\
  {\
     "startUrls": \[{ "url": "https://your-team.notion.site/Database-..." }],\
     "outputMode": "databaseRows",\
     "maxDatabaseRows": 5000,\
     "includeRawProperties": false\
   }\
   \
  **Output modes**\
  pages\
  One dataset item per page. Each record includes page identity, URL, hierarchy, change state, hashes, source/fidelity metadata, selected content formats, database summaries, and optional stored assets.\
  databaseRows\
  One item per database row. Properties use a stable shape:\
  {\
     "recordType": "databaseRow",\
     "databaseId": "12dd2044-a388-43db-8b6d-55bb7883b71f",\
     "rowPageId": "0a5f0f62-694e-4f6b-9f37-1c48ab3bbfea",\
     "title": "Example Brainstorm",\
     "properties": {\
       "Tags": {\
         "id": "notion://docs/doc\_tags\_property",\
         "name": "Tags",\
         "type": "multi\_select",\
         "value": \["Product"]\
       }\
     }\
   }\
   \
  blocks\
  One item per Notion block with its type, parent, depth, heading path, and selected formats. This is useful when downstream logic needs fine-grained citations or its own renderer.\
  chunks\
  Embedding-ready records with stable chunk IDs, approximate token counts, heading paths, source block IDs, and Markdown/text. The chunker respects semantic and heading boundaries and keeps code/table blocks indivisible.\
  **Private pages and databases**\
  Create an internal Notion integration, grant it read access only to the pages you want to export, and enter the token in the secret notionToken field. The Actor uses Notion's official API for connected content. The token is not written to logs, datasets, manifests, or errors.\
  Public URLs use the faster structured public-page path first. Supplying a token also provides a fallback for connected pages that are not publicly published.\
  **Incremental sync**\
  Set syncKey to persist a private manifest between scheduled runs. Content and metadata are hashed deterministically:
- new: the page was not in the preceding complete run;
- updated: content or relevant metadata changed;
- unchanged: both hashes match.\
  Enable emitOnlyChanges to omit unchanged pages. A manifest is committed only after a complete run; page failures, scope truncation, or a PPE budget limit leave the last good manifest intact. The run's committed manifest is also linked from the Actor output.\
  **Assets and ZIP bundles**\
  Enable downloadAssets to copy eligible images and files into the default key-value store. Enable createZip to add page.md, page.txt, metadata, and downloaded assets to a portable per-page archive.\
  Downloads are streamed through per-file, per-page-count, and total-run limits. Redirects and custom-domain DNS are checked to prevent access to private network addresses. Stored records expose storage keys rather than leaking authenticated public URLs.\
  **Pay-per-event pricing**\
  The Actor is designed for transparent pay-per-event pricing:\
  | | | |\
  |-|-|-|\
  | **Event** | **Suggested price** | **Charged when** |\
  | page-checked | $0.001 | A page/database is successfully loaded and normalized |\
  | page-exported | $0.011 | A page's page, block, or chunk output is emitted (once per page) |\
  | database-row-exported | $0.003 | One normalized database row is emitted |\
  | asset-stored | $0.002 | A downloaded asset or generated ZIP is successfully stored |\
   \
  Invalid URLs, inaccessible pages, skipped oversized assets, and error records do not trigger those result events. Apify run budgets are honored before additional paid output is produced.\
  **Local development**\
  npm install\
   npm test\
   npm run check\
   npm run build\
   apify run --input '{"startUrls":\[{"url":"https://..."}]}'\
   \
  Node.js 22 is used in the Actor image. Tests cover input rules, Notion record unboxing, rich text, rendering, database normalization, semantic chunking, IDs, hashes, and URL safety.

# Actor input Schema

## `startUrls` (type: `array`):

One or more Notion pages, wikis, or databases. Duplicate page IDs are processed once.

## `notionToken` (type: `string`):

Optional encrypted read-only integration token for private or connected pages. Public pages do not need it. The token is never written to output or logs.

## `crawlSubpages` (type: `boolean`):

Follow child-page blocks within the same Notion site.

## `crawlDatabasePages` (type: `boolean`):

Visit the body of every database row page. Leave off for fast property-only database export.

## `maxDepth` (type: `integer`):

Start URLs have depth 0.

## `maxPages` (type: `integer`):

Global page safety limit after duplicate IDs are removed.

## `maxDatabaseRows` (type: `integer`):

Limits rows returned from each queried database view.

## `outputMode` (type: `string`):

Pages contain whole documents; database rows are flat property records; blocks preserve fine-grained structure; chunks are embedding-ready.

## `formats` (type: `array`):

Select only what downstream systems need to keep dataset rows compact.

## `includeRawProperties` (type: `boolean`):

Keep the original Notion property payload beside normalized values for debugging and uncommon property types.

## `targetChunkTokens` (type: `integer`):

Preferred approximate chunk size; semantic boundaries can produce smaller chunks.

## `maxChunkTokens` (type: `integer`):

Hard approximate size limit except for indivisible code and table blocks.

## `overlapTokens` (type: `integer`):

Approximate semantic overlap copied from the end of the preceding chunk.

## `downloadAssets` (type: `boolean`):

Preserve assets in the run's key-value store instead of returning expiring source links only.

## `createZip` (type: `boolean`):

Store Markdown and downloaded assets in a portable bundle. Implies asset download.

## `maxAssetsPerPage` (type: `integer`):

Stops pages with many attachments from consuming unbounded storage.

## `maxAssetBytes` (type: `integer`):

Files larger than this limit are skipped without an asset charge.

## `maxTotalAssetBytes` (type: `integer`):

Combined safety limit for downloaded files and generated ZIP bundles.

## `syncKey` (type: `string`):

Optional persistent state name. It is hashed before storage use.

## `emitOnlyChanges` (type: `boolean`):

Requires a sync key. Unchanged pages are checked but omitted from the dataset.

## `includeUnchanged` (type: `boolean`):

Include unchanged records when a sync key is configured and change-only output is disabled.

## `includeErrors` (type: `boolean`):

Write normalized, uncharged error rows to the dataset.

## `failOnPageError` (type: `boolean`):

Strict mode. By default, batch runs continue and report partial errors in the summary.

## `requestTimeoutSecs` (type: `integer`):

Maximum time allowed for each Notion page, API, or asset request.

## `maxRequestRetries` (type: `integer`):

Retries timeouts, rate limits, and transient server failures with backoff.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://darshgupta.notion.site/Getting-Started-1236ce47943c43fd8bbe8a236a25b9a6"
    }
  ],
  "crawlSubpages": true,
  "crawlDatabasePages": false,
  "maxDepth": 10,
  "maxPages": 500,
  "maxDatabaseRows": 5000,
  "outputMode": "pages",
  "formats": [
    "markdown",
    "text"
  ],
  "includeRawProperties": false,
  "targetChunkTokens": 700,
  "maxChunkTokens": 1000,
  "overlapTokens": 80,
  "downloadAssets": false,
  "createZip": false,
  "maxAssetsPerPage": 50,
  "maxAssetBytes": 25000000,
  "maxTotalAssetBytes": 250000000,
  "emitOnlyChanges": false,
  "includeUnchanged": false,
  "includeErrors": true,
  "failOnPageError": false,
  "requestTimeoutSecs": 30,
  "maxRequestRetries": 3
}
```

# Actor output Schema

## `results` (type: `string`):

The homogeneous page, database-row, block, or chunk records selected in the input.

## `summary` (type: `string`):

Crawl, extraction, change, fallback, error, and billing counts.

## `manifest` (type: `string`):

Persistent content hashes when a sync key is configured.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "startUrls": [
        {
            "url": "https://darshgupta.notion.site/Getting-Started-1236ce47943c43fd8bbe8a236a25b9a6"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("fetchfinch/notion-scraper-knowledge-base-sync").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "startUrls": [{ "url": "https://darshgupta.notion.site/Getting-Started-1236ce47943c43fd8bbe8a236a25b9a6" }] }

# Run the Actor and wait for it to finish
run = client.actor("fetchfinch/notion-scraper-knowledge-base-sync").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "startUrls": [
    {
      "url": "https://darshgupta.notion.site/Getting-Started-1236ce47943c43fd8bbe8a236a25b9a6"
    }
  ]
}' |
apify call fetchfinch/notion-scraper-knowledge-base-sync --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,fetchfinch/notion-scraper-knowledge-base-sync"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/Zl5IFf89ZPUUZfW4B/builds/r1i4jPfZVwcTbck6C/openapi.json
