Skip to main content
Notion API client following the official Notion CLI grammar. Install it on the workspace and the whole tree is discoverable with ntn --help or man ntn.

Install

Two installs under different names are two accounts. In YAML, the same install rides the clis: section; see the CLI overview.

Verbs

The grammar matches the official Notion CLI verb for verb, and every case is gated against the real ntn binary in CI, so what is written here is what the program does. Ids are positional, not flags. There is no --page, --block or --datasource; each verb names its own operand.
There is no ntn blocks, ntn comments or ntn search. Those are reached through ntn api with the REST API’s own paths, exactly as upstream reaches them. Upstream’s interactive and deploy verbs (login, logout, update, workers, notion-as-code, doctor, files) are out of scope for a virtualized CLI.

Pages

Page bodies are Markdown, not property JSON. create takes the body on --content or from stdin, and the first heading becomes the title.
--parent takes page:<id>, database:<id> or data-source:<id>. edit replaces the page body wholesale. trash refuses without --yes unless a prompt can be answered, and sets in_trash. To set a row’s property values rather than its body, use ntn api:

Data sources

Since 2025-09-03 a database is a container of data sources, and the rows and the column schema live on the data source. resolve turns a database id into its data source ids; query accepts either in the same slot.
query prints one tab-separated line per row: the page id, then the property values in alphabetical order by column name. The columns are the ones the returned rows actually carry, so a result set that does not cover the whole schema prints narrower.

Raw API

ntn api reaches every route that has no typed verb, including the only delete verb the public API has (DELETE /v1/blocks/{id}, which trashes a block, a page, or a database row).
The body comes from exactly one source: stdin, --data/-d, or inline path=value / path:=json inputs. Naming two is an error. name==value stays a query parameter whatever the method is, and Header:Value sets a header. Any body source makes the call a POST unless -X/--method says otherwise; GET, POST, PATCH, PUT and DELETE are accepted. Use the <page-id> / <database-id> / <data-source-id> from a mounted path segment as the operand.