Notion Uploader
Pricing
Pay per usage
Notion Uploader
Upload data into a specified Notion database. It dynamically maps data from any Actor / Dataset to your Notion properties, ensuring your database stays up-to-date with the latest information.
Pricing
Pay per usage
Rating
5.0
(1)
Developer
Filip Cicvárek
Maintained by CommunityActor stats
8
Bookmarked
82
Total users
6
Monthly active users
3 days ago
Last modified
Categories
Share
Upload any Apify dataset to Notion: one-click connection, self-configuring mapping, rerun-safe
Uploads items from an Apify dataset into Notion: an existing database, a database created for you with a schema inferred from the data, or a page. Connect Notion with one click via an Apify MCP connector (the Actor never sees your token) or with a classic Notion API key. Field mapping resolves itself by name matching, a plain-English AI prompt, or explicit JSON; upserts keep reruns duplicate-free, and AI can transform values or enrich items on the way in.
Quick start
MCP connector (recommended)
- In Apify Console, open Settings → Integrations → MCP connectors and add a Notion connector (one OAuth click).
- Run this Actor: pick the connector, pick a source dataset, and either paste a target database URL or enable Create a database for me.
- Done. Check the run's Upload report dataset and the created rows in Notion.
Notion API key (works everywhere)
- Create an internal integration at notion.so/profile/integrations and copy the
ntn_…secret. - In Notion, open the target database/page → ••• → Connections → add your integration.
- Run the Actor with
notionApiKeyand the database URL.
Turn on Dry run for the first attempt: it resolves and prints the field mapping, validates everything against the Notion schema, and writes nothing.
Destinations
| Mode | How to select | What happens |
|---|---|---|
| Existing database | fill notionDatabaseId | one row per item; missing property values are skipped with per-item warnings |
| New database | leave notionDatabaseId empty, set createDatabaseIfMissing: true | schema (column names and types) is inferred from your data; the created database URL is in the log and SUMMARY |
| Page | fill notionPageId | items are rendered as a markdown table, a bulleted digest, or raw text, and appended, replacing content, or placed on a new child page |
Field mapping
Three layers, strongest wins. The result is always printed in the log and saved to the RESOLVED_MAPPING record:
- Explicit:
dataMapping: {"Name": "title", "Score": "stats.score"}. Dot paths reach nested fields. - AI prompt:
mappingPrompt: "Put the listing headline into Name and the monthly rent into Price". One LLM call (needsanthropicApiKey); proposals are validated against the real schema and dataset fields. - Automatic: properties are matched to fields by normalized name where sampled values are type-compatible.
Values are coerced per property type: numbers from strings, dates from ISO strings and epochs, checkboxes from yes/true/1, multi-select from arrays, JSON, and comma lists, plus URLs, emails, and files. Unparseable values become per-item warnings, never silent data corruption.
Duplicate handling (rerun-safe uploads)
Set dedupeMode to upsert or skip-existing with keyProperty (for example URL): existing rows are matched by that key and updated or skipped instead of duplicated. Lookups run via the Notion API key, or via the connector on Notion Business+ plans; otherwise the run falls back to create-only with a warning and dedupeDegraded: true in SUMMARY.
AI value transformation
transformPrompt: "append CZK to all price values" rewrites existing field values before upload. One planning call compiles the instruction into per-field rules: mechanical changes become templates applied locally to every item (no per-item LLM cost); fields that need judgment ("translate the description to English") get one call per item. The rule plan is saved to the TRANSFORM_PLAN record. Transforms run before type inference, so new-database column types adapt; in an existing database, transformed values must still fit the column type.
AI enrichment
enrichmentPrompt: "One-sentence summary: {{title}}, {{description}}" generates a new value per item (Claude Haiku by default) and writes it into enrichmentProperty (created automatically). {{field}} placeholders are filled from the item; the full item JSON is appended for context.
Input examples
Zero-config: dataset to a brand-new Notion database via connector:
{"notionConnector": "<your connector id>","createDatabaseIfMissing": true,"newDatabaseTitle": "Scraped listings","datasetId": "<dataset id>"}
Existing database, rerun-safe, with an API key:
{"notionApiKey": "ntn_...","notionDatabaseId": "https://www.notion.so/workspace/My-DB-82e8e449d87949898ff6e705ed2ecbbf","datasetId": "<dataset id>","dedupeMode": "upsert","keyProperty": "URL"}
Digest appended to a page:
{"notionConnector": "<your connector id>","notionPageId": "https://www.notion.so/workspace/Weekly-digest-391f39950a22819fa2b6e5283fdd08d3","pageWriteMode": "append","pageRenderMode": "table","datasetId": "<dataset id>"}
AI mapping, value transformation, and per-item summaries:
{"notionConnector": "<your connector id>","notionDatabaseId": "<database url>","datasetId": "<dataset id>","mappingPrompt": "Map the listing headline into Name, the monthly rent into Price, the link into URL.","transformPrompt": "Append CZK to all price values.","enrichmentPrompt": "One short English sentence describing this listing: {{title}}, district {{district}}.","enrichmentProperty": "Summary","anthropicApiKey": "sk-ant-..."}
Calling from an agent or backend (the input schema is machine-readable; agents can fill it from the field descriptions):
curl -X POST "https://api.apify.com/v2/acts/<username>~notion-uploader/run-sync?token=$APIFY_TOKEN" \-H 'Content-Type: application/json' \-d '{"notionConnector": "...", "createDatabaseIfMissing": true, "datasetId": "..."}'
Output
- Upload report (default dataset): one row per item with
itemIndex,status(created|updated|skipped|failed),key,notionUrl,error, andwarnings. Written incrementally, so partial results survive aborts. SUMMARY(key-value store): counts, warnings,dedupeDegraded,createdDatabaseUrl,mappingSource.RESOLVED_MAPPING: the final mapping table with each entry's source (explicit/llm/auto).TRANSFORM_PLAN: the compiled value-transformation rules.WOULD_WRITE(dry runs): a sample of exactly what would be sent to Notion.
Limits and behavior notes
| Topic | Behavior |
|---|---|
| Notion rate limits | API-key path is rate-limited to ~2.5 requests/s with Retry-After handling; connector path batches up to 100 pages per call |
| Property types | title, rich text, number, URL, checkbox, select, multi-select, date, status, email, phone, files; other types (relation, formula, people) are reported and skipped |
| Files property | writable on the API-key path only (external URLs); the connector path skips it with a warning |
| Page mode | renders at most 500 items per run |
| Duplicate lookups | need an API key or a Business+ Notion plan on the connector; otherwise the run degrades to create-only with a warning |
| New-database reruns | each run with createDatabaseIfMissing creates a new database; pass the created database URL back in for the next run |
| Statuses | Notion does not auto-create status options; unknown status values are skipped with a warning |
Development
npm installapify run # local run against ./storagenpm test # vitest unit tests (fixtures in test/fixtures/)apify push # deploy to the Apify platform
See AGENTS.md for architecture, MCP-protocol facts, and probe fixtures.


