Notion platform
Notion API limits, cited
Block creation
| Limit | Value | How we handle it |
|---|---|---|
| Children per append request | 100 | The installer chunks every page body into groups of 100 and issues one PATCH per chunk. |
| Nesting depth per request | 2 levels | A column_list containing columns containing paragraphs is already at the ceiling. Deeper trees are split across requests, appended to the block id the previous request returned. |
| Block types available | See reference | paragraph, heading_1-4, bulleted/numbered_list_item, to_do, toggle, quote, callout, divider, code, equation, embed, bookmark, column_list, synced_block, table, table_row, breadcrumb, table_of_contents, child_page, child_database. |
button block type | does not exist | Buttons are the single most-requested thing a recipe cannot create. Every recipe that needs one ships the exact click path instead. |
| Insertion position | position | Since 2026-03-11, <code>position</code> replaces the older <code>after</code> field for placing children. |
| Notifications on write | mode: "silent" | Bulk installs should send this, otherwise every appended block can notify watchers of the page. |
Sources: Append block children and Block object.
The data-source split, and why installs are two-pass
Notion separated databases from data sources. A database is the container; a data source holds the schema and the rows. Every database created through the API arrives with exactly one default data source, whose id you do not choose and cannot predict.
A relation property targets a data source. So creating Deals with a relation to Companies requires the data_source_id of Companies, which only exists after Companies has been created and read back.
pass 1 · POST /v1/databases create every database, base properties only
(relations, rollups and dependent formulas omitted)
pass 2 · GET /v1/databases/{id} read each one back, take data_sources[0].id
pass 3 · PATCH /v1/data_sources/{id}
now add the relation properties, using the ids
pass 4 · PATCH /v1/data_sources/{id}
then rollups (they name a relation property)
pass 5 · PATCH /v1/data_sources/{id}
then formulas that read either of the aboveref: a local name like companies. The installer marks those with an internal __relationRef field, strips them from pass 1, and resolves each one to a real data_source_id in pass 3. That is why a recipe can declare a bidirectional relation between two databases that do not exist yet.Sources: Create a data source and Property object.
Properties the API will not write
Two property types exist in the response schema and are silently ignored on create or update:
| Property type | Readable | Writable | Note |
|---|---|---|---|
button | Returned by the API on databases that have one. Cannot be created or configured programmatically: it is a page-level control, not a schema field. | ||
verification | The wiki page-verification badge. Response-only. | ||
status groups | On create, each option may carry a <code>group</code> of To-do, In progress or Complete. In responses, groups arrive separately with <code>option_ids</code>, so a round-trip is not symmetrical. | ||
unique_id | You can set the <code>prefix</code>. You cannot set the number. | ||
relation | Current versions target a data source, not a database id. Older integrations pinned to an earlier Notion-Version still address databases directly. |
Source: Property object.
Views
The Views API is recent, and it is the reason provisioning a workspace from code finally works end to end. Before it, you could create a database but not shape how anyone looked at it.
POST /v1/viewscreates table, board, list, calendar, timeline, gallery, form, chart, map and dashboard views.- Board groups, timeline date properties, calendar ranges, chart axes and form questions are all configurable at creation time.
- Dashboard layouts are read-only. You can create a dashboard view, and you can create the widget views it would display, but you cannot position them. Notion composes the layout itself from the widget views attached to the same data source.
Our habit-os-native recipe creates a dashboard view plus each chart view as its own request, then tells you in the manual steps that the arrangement is Notion's to decide.
Source: Working with views.
The four gaps every recipe works around
These appear as manual[] entries in 11 of our 11 recipes. Each entry carries the reason, verbatim, so you can check it against the reference yourself.
Rate limits and quotas
| Constraint | Value | Effect on an install |
|---|---|---|
| Request rate | ~3 req/s average | A 26-request CRM install takes well under a minute even paced conservatively. Our installer does not parallelise writes to the same parent. |
| Children per request | 100 | Long documentation pages are chunked; the chunk count is visible in the plan before you run it. |
| Nesting per request | 2 levels | Deep trees need sequential appends against returned block ids. |
| Payload size | 500 KB per request | Page bodies of a few hundred blocks are fine. A simple_table whose serialized body exceeds 500 KB needs splitting across requests. |
| File upload size | Depends on plan | Widget HTML is 15-90 KB, so uploads are never close to the limit on any plan. |
| Integration connections | Per page or per workspace | An internal integration must be explicitly connected to every page it writes to, or writes return restricted_resource. |
POST /api/install with mode: "plan" requires no credentials and returns every request: method, path, body and a note. You can count them, diff them, and estimate the runtime before anything touches your workspace.Notion-Version
Every request in this project sends Notion-Version: 2026-03-11. That version introduced the File Upload path for HTML blocks, heading_4, tab blocks, the position field, silent notification mode, and a typed database_type on database objects.
Why pinning matters
- Notion's API is versioned by date, and behaviour changes between versions, notably the database/data-source split, which changes what a
relationproperty points at. - A recipe that resolves
data_sources[0].idis only correct on versions where data sources exist. Pinning is what makes that safe. - If you fork this project and change the version constant, re-run the plan for one recipe and diff it before installing anything.
Source: Notion API changelog.