Notion Action

Create and update Notion pages, read their content as Markdown, and query rows out of a database — as a step in a pipeline. Twelve operations against Notion's REST API, with the page body handled as plain Markdown instead of a hand-built block tree.

Overview

The Notion Action node runs one operation per event. It appears on the canvas in a warm grey card — Notion's own brand is monochrome.

Webhook event--▶Notion creates the page--▶Downstream nodes
Notion only exposes what you share with the connection. Authorizing is not sharing — they are two separate gestures. Until you open a page or database in Notion and connect it (··· › Connections), every picker in this node comes back empty and every call 404s. It is the number one cause of "I set it up and nothing happens".

Credential

Two ways in, and they coexist. Both are created either inline on the node or in Settings › Credentials.

Connect Notion (recommended)

Click Connect Notion, authorize, and pick which pages and databases the connection can reach — right there on Notion's consent screen. Nothing to copy, and no integration for the user to create. Stored as notion_oauth.

Your own integration

Prefer to run against a single workspace with your own app? Create an internal integration at notion.so/profile/integrations and paste its secret — it starts with ntn_. Stored as notion_internal. It works the same minute you create it and needs nothing published on our side.


Operations

Twelve, grouped by the Notion object they work against. The dropdown shows each one's endpoint next to its name, so you can cross-check it against Notion's own documentation.

OperationEndpointWhat it does
Create pagePOST /pagesA row in a database, or a subpage. Body goes in as Markdown.
Get pageGET /pages/{id}The page object and its properties — not its content.
Get page as MarkdownGET /pages/{id}/markdownThe whole body as one Markdown string.
Update page propertiesPATCH /pages/{id}Status, dates, relations. Only what you list is touched.
Update page contentPATCH /pages/{id}/markdownReplace the body, or find-and-replace inside it.
Append contentPATCH /pages/{id}/markdownAdd at the top or the end without touching what is there.
Archive pagePATCH /pages/{id}To the trash, or back out of it. Recoverable for 30 days.
Query data sourcePOST /data_sources/{id}/queryRows matching a filter. Output is iterable.
Get data source schemaGET /data_sources/{id}Which properties exist and what type each one is.
Get block childrenGET /blocks/{id}/childrenThe raw block tree, one level down. iterable
SearchPOST /searchBy title only, not content. iterable
Add commentPOST /commentsOn a page, or as a reply in an existing thread.

Content is Markdown, not blocks

Writing content to Notion used to mean building a block tree by hand: every paragraph, heading and list item as its own typed object. It is the most expensive part of that API, and it is where most integrations stop at "create an empty page".

This node uses Notion's Markdown endpoints instead, so writing a page is sending text. Templates work inside it like anywhere else:

## Incident {{payload.id}}

Reported by **{{payload.reporter}}** at {{payload.timestamp}}.

- Severity: {{payload.severity}}
- Service: {{payload.service}}
Append is the safe one. Update page content in replace mode drops everything currently on the page, including blocks this flow did not write.

Databases and data sources

Since Notion's 2025-09-03 API version, a database holds one or more data sources, and rows live in the data source — not in the database. Most databases have exactly one, and the picker resolves it for you. Anything you read online that queries databases/{id}/query is written against the previous API.

A row is a page in Notion — same object, different parent. That is why "Create page" is also how you add a row.

Filtering a query

The three filter fields — property, condition, value — cover the common case. Behind them the node fetches the data source schema first, because Notion has no generic filter: the key inside a filter clause is the type of the property, and the same "equals" is spelled differently for a select, a date and a number. That is one extra request, and it is the difference between a filter that works and a 400 that does not name the field.

For anything the three fields cannot say — compound and/or, rollups, formulas — use Filter (raw JSON) under Advanced. When it is filled in, it wins and the simple filter is ignored.

Page and database ids

Pick from the list when you can. When you cannot — because the target comes from the event — paste a URL, an id, or a template like {{payload.pageId}}. All three work: the node normalizes whatever you give it.

Copy link on a view gives you a URL whose ?v= is also a 32-character id. The node trims the query first, so it takes the page and not the view.

Rate limits

Notion allows about three requests per second on average per connection, plus a per-workspace ceiling that scales with the workspace's plan. A 429 here is ordinary, not an incident.

The node retries on 429, 529 and the usual gateway errors, waiting as long as Notion asks in its Retry-After header. Failures that are ours — a missing credential, malformed JSON, a property that does not exist — come back as 400 and are not retried, because they will not improve.


Firing a pipeline from Notion

This node is the write side. To have Notion start a pipeline, use the Trigger node with its Notion service — it polls a database or the workspace and fires on pages added or edited.

It polls rather than receiving webhooks, and that is not a shortcut. Notion has webhooks, but their subscriptions are created by hand in Notion's developer portal and verified by pasting a token back — there is no API to register one for you. Zapier polls Notion for the same reason.

Cost

Notion's API is free on every plan, with no per-call charge. The only paid piece in Notion's own product is the no-code webhook action inside their automations, which this node does not use.