Skip to main content
The Notion template (apps/notion-clone, name notion) is an API-only clone of the Notion public API: a NestJS backend on Postgres that speaks Notion’s REST surface — integration-token Bearer auth with a Notion-Version header, the workspace → users → databases → data sources → pages → blocks → comments object model, Notion’s rich property/block JSON, cursor pagination returning the {object:'list', results, has_more, next_cursor, type} envelope, and Notion’s per-token rate limit (429 rate_limited + Retry-After).
It is faithful enough at the URL + wire-format level that an unmodified @notionhq/client v5 can drive it (see SDK parity).

What’s inside

Modes

This is an API-only template — there is no frontend.

Seeding

AI seeding generates users, databases, and pages (the template’s declared entities). See Seeding.

The seeded integration token

Unlike a browser OAuth install flow, the clone seeds an integration token so the first curl or SDK call authenticates with zero setup (there is no signup/consent route):
Every request needs both the Bearer token and a Notion-Version header:
A missing Notion-Version header returns the real Notion error (400 missing_version); a present header is accepted regardless of value (known versions 2022-06-28 / 2025-09-03 / 2026-03-11 are recognized, unknown ones are normalized rather than rejected) so modern SDKs keep working. Invalid or missing Bearer tokens return 401 {code:'unauthorized'}.

Rate limiting

Like real Notion, the clone enforces a per-integration-token rate limit (token bucket, ~3 requests/second). Bursting past it returns Notion’s throttle response byte-for-byte:
A separate 529 service_overload path also exists (env-gated). The health probe is exempt. Agents that retry on Retry-After — as the official SDK does — work unchanged.

SDK parity

An unmodified @notionhq/client v5+ (which sends Notion-Version: 2026-03-11) works against the clone when constructed with the clone’s baseUrl:
The clone ships a thin data_sources facade: each database exposes exactly one data source whose id equals the database id, so the v5 SDK’s data_sources-routed calls (dataSources.query, parent.data_source_id, search({filter:{property:'object',value:'data_source'}})) resolve.

API shape — read this before pointing an agent at it

The clone is a path-faithful subset of Notion’s REST API under the /api/v1 prefix (POST /api/v1/pages, POST /api/v1/databases/:id/query, …). The template ships an API_PARITY.md that scores it against Notion’s live docs (re-fetched every audit, never from memory). The implemented core covers:
  • Usersme, list, retrieve.
  • Databases — create / retrieve / update / query (with a documented filter-operator allowlist; unsupported operators return a 400 validation error rather than silently returning all rows).
  • Data sources (2025-09-03 split) — create / retrieve / update / query.
  • Pages — create / retrieve / update (incl. trash via in_trash) / property item / move / Markdown read + write.
  • Blocks — retrieve / list children / append children / update / delete.
  • Comments — create / list / retrieve / update / delete.
  • Search — pages + databases by title, with the data_source object alias.
  • File uploads — create / list / retrieve / send / complete.
  • Views — create / list / retrieve / update / delete, plus view queries (create / retrieve / delete).
  • OAuth token endpointsoauth/token (incl. grant_type=refresh_token) / oauth/introspect / oauth/revoke.
That’s 45 endpoints at 100% shape parity against the current live surface. Surfaces outside the targeted core — webhooks / Events API, the hosted MCP server (mcp.notion.com), and relation/rollup/formula compute — are unimplemented by design; the clone never claims to emit events or ship MCP tools. See apps/notion-clone/API_PARITY.md for the per-endpoint breakdown and the filter-operator allowlist.
The verify command will fold live fidelity scoring into the CLI. Until then, API_PARITY.md and the /verify-api workflow are how fidelity is tracked.

Inspect a running clone

The schema (workspaces, users, databases, pages, blocks, comments, plus file_uploads / views / view_queries) comes straight from the two migration files — read them to know what’s queryable.