Notion as a headless CMS
What you run into when a website reads its content from the Notion API — rate limits, nested blocks, expiring file URLs, id-only webhooks — and how Spunto CMS handles each one.
Using Notion as the CMS of a website is a reasonable first move. The team is already writing there, the editor is excellent, and the API can query a database. Plenty of blogs and docs sites run on it.
This page is about what tends to show up once the site grows, and what Spunto CMS does differently. It tries to be fair to both: where Notion is better, it says so.
Sources
Every statement about Notion below comes from Notion's own developer documentation, as read in October 2026, and links to the page it comes from. Notion changes its API; check the link before you rely on a number.
What a site runs into
Request limits
Notion's API allows
180 requests per minute, an average of 3 per second,
on most plans; Business and Enterprise get 600 per minute. Over the limit, you
get a 429.
That is plenty for an editor clicking around, and tight for a static build: one page with its blocks is several requests (see below), so a site with a few hundred pages spends its build waiting on the limiter.
Nested blocks, one level per call
Retrieving block children "returns only the first level of children", with at most 100 results per call. A toggle, a nested list or a column layout is another level, so another request. Rendering one page is a recursive walk.
File URLs expire
A Notion-hosted file comes with a temporary URL valid for one hour, and the documentation says plainly: "Don't cache or statically reference these URLs." A statically built page that embeds them shows broken images an hour after the build — unless the build copies every file somewhere else first.
Change notifications carry identifiers
Notion does have webhooks.
A delivery carries the page id and the event type, not the content, and
page.content_updated events "are batched and may not be sent immediately" —
the documentation suggests waiting a minute or two. To rebuild one page you
fetch it again, through the same rate limit.
What Spunto CMS does instead
Spunto CMS is a workspace in the style of Notion — lists of cards, typed properties, block-structured page bodies, saved views — and the rows the team edits are the rows the API returns. Here is how it answers each point above.
| Notion API | Spunto CMS | |
|---|---|---|
| Request limits | 3 req/s average on most plans, 10 on Business and Enterprise | None today — stated as a fact, not a promise |
| A page body | Recursive walk, one level and 100 blocks per call | One call: GET …/items/{itemId} returns the whole document — a flat list of blocks with an indent (page bodies) |
| Listing | Database query, 100 per page | View query with a filter tree, 200 per page, keyset cursor (querying) |
| Files | Temporary public URL, valid one hour | No public URL at all: the API serves the bytes behind your token, and a file id never changes content — your site relays it once and caches it for good (files) |
| Change notifications | Ids and event type; content updates batched | The changed keys with before and after, signed (HMAC with a timestamp), retried, one delivery per bulk edit, and a log you can replay from a cursor (events and webhooks) |
| Types for your code | Official JavaScript SDK (@notionhq/client), typed | Generated from the OpenAPI specification in one command — TypeScript, or any language with an OpenAPI generator |
The files row is the one people misread. Spunto does not give you a URL that works forever either — it gives you no URL. That is deliberate: a shareable link would be a second way in, weaker than the token. What changes is that the bytes behind an id are immutable, so one relay route on your site (example) caches them indefinitely instead of re-fetching them every hour.
Where Notion is ahead
Be clear about this before moving anything:
- The editor and the database model are far richer. Spunto has ten property types and no relations, rollups or formulas. If your content model depends on those, stay.
- Public pages. Notion can publish a page to the web by itself. Spunto has no public page and no share link; your site is the only public surface.
- Maturity and ecosystem. Notion has years of integrations and community renderers. Spunto CMS is young.
Read what it does not do before deciding. It is short.
Moving a Notion database over
POST /workspaces/{workspaceId}/import exists for this. The request names its
source (notion) and each card carries its Notion page id as externalId,
so the import is replayable: run it again and it updates instead of
duplicating, and a card that has not changed is not rewritten — no write, no
event, no webhook. Imports always fail at least once; this one you can run
again until it is right.
You map Notion's properties and blocks to Spunto's yourself: statuses to
statuses, select to select, and Notion's blocks to the
ten block types. The rules are in
imports you can run twice.
Next
The Next.js guide wires a site to Spunto end to end:
typed client, rendered bodies, images and webhook revalidation. A free account
at /login is enough to follow it.