notion-sdk-js: the official TypeScript client for the Notion API
Official Notion JavaScript Client
At a glance
- What is it?
- The official JavaScript and TypeScript client for the Notion API, published as @notionhq/client. It is a thin typed wrapper over REST, not a sync engine, with configurable automatic retries on rate limits, service overloads and server errors.
- Who is it for?
- Adopt @notionhq/client if you are writing Node 22 or newer code against the Notion API and want typed request objects, automatic retries on 429 and 529, and structured APIResponseError codes instead of hand-rolled fetch calls. Do not adopt it if you need offline caching or a synchronisation layer, since every method is a network call.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 7 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on November 14, 2026, and from our analysis. They are not legal advice.
Star history
- makenotion/notion-sdk-js5,688 ★ · +22
| Repository | 2026-09-23 | 2026-11-14 |
|---|---|---|
| makenotion/notion-sdk-js | 5.7k | 5.7k |
22 Stars in the last 52 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
What notion-sdk-js replaces, and who ends up using it
The Notion API is a REST service. Without a client library, every call means assembling a URL, setting the Authorization and Notion-Version headers, serialising a body, and parsing a response by hand. notion-sdk-js removes that layer. The README describes it as "A JavaScript and TypeScript client for the Notion API" and states that it covers the SDK's methods, options, and helpers.
The audience is narrow and specific. You are building a Node service, a script, or an internal tool that reads or writes Notion pages, databases, data sources, users, or views. The package is published as @notionhq/client, and the repository's package.json declares "engines": { "node": ">=22" }, so the floor is Node 22. TypeScript consumers get types from ./build/src/index.d.ts, which is the declared types entry point.
What it is not: a database, a cache, or a synchronisation layer. Every method is a network call. The README's own example is a single await against notion.users.list({}), and that is the whole shape of the library. If you were hoping for local state, change tracking, or offline reads, none of that appears in the documentation.
One options object, one client, and where the parameters go
The Client constructor takes a single options object. The README table lists auth, logLevel, timeoutMs, baseUrl, logger, agent, retry, and dangerouslyAllowBrowser. timeoutMs and baseUrl default to the DEFAULT_TIMEOUT_MS and DEFAULT_BASE_URL constants.
The design decision worth noticing is parameter flattening. The README states that endpoint parameters are grouped into a single object and that you do not need to remember which parameters go in the path, query, or body. So a data source query takes data_source_id and filter side by side, even though one is a path segment and the other is a body field. That removes a class of mistakes, and it also means the call signature no longer tells you anything about HTTP semantics. If you are debugging against the raw API reference, you have to translate mentally.
Authentication is a bearer token. The README says Client accepts an integration token or an OAuth access token, and that if auth is left undefined, the auth parameter should be set on each request. That second path matters for multi-tenant tools, where one client instance serves several workspaces.
Retries are the other mechanism with real consequences. The retry option configures automatic retries on rate limits (429), service overloads (529), and server errors (500, 503).
Installing @notionhq/client and making a first query
Installation is one npm command. The README gives it directly:
npm install @notionhq/clientSetup steps live outside the repository. The README points to Notion's getting started guide at developers.notion.com/docs/getting-started for creating the integration and obtaining a token. The SDK does not create integrations for you, and no CLI is shipped that does it.
Once you have a token, construct a client and call an endpoint. The README's first example reads the token from the environment:
const { Client } = require("@notionhq/client")
const notion = new Client({
auth: process.env.NOTION_TOKEN,
})A first real call is users.list, which returns a Promise resolving to the response body. The README shows it wrapped in an async IIFE and logged:
;(async () => {
const listUsersResponse = await notion.users.list({})
console.log(listUsersResponse)
})()The response shape is documented: a results array whose entries carry object, id, type, name, avatar_url, and, for person users, a nested person object with email. If your token is valid and the integration has been added to a workspace, you should see that array populated. An empty or failing response almost always means the token or the workspace connection, not the SDK.
Querying a data source shows the flattened parameter style in practice:
const myPage = await notion.dataSources.query({
data_source_id: "897e5a76-ae52-4b48-9fdf-e71f5945d1af",
filter: {
property: "Landmark",
rich_text: {
contains: "Bridge",
},
},
})For debugging, raise verbosity. LogLevel.DEBUG also logs response bodies, which the default LogLevel.WARN does not:
const { Client, LogLevel } = require("@notionhq/client")
const notion = new Client({
auth: process.env.NOTION_TOKEN,
logLevel: LogLevel.DEBUG,
})A custom logger receives logLevel, message, and extraInfo, and the README states it should return no value.
Error handling and the retry rules you have to design around
Failed requests reject with an APIResponseError, and the code property identifies the failure. APIErrorCode holds the known server error codes, and isNotionClientError narrows the catch block. The README's example checks for APIErrorCode.ObjectNotFound and suggests asking the user to select a different data source, which is the realistic recovery for that case.
The retry option takes a RetryOptions object, so test its behaviour on your write paths before production.
Timeouts are separate from retries. timeoutMs controls how long the client waits before emitting a RequestTimeoutError, defaulting to DEFAULT_TIMEOUT_MS.
Where the SDK stops: no webhooks, no browser story, no sync
The agent option is typed as http.Agent and the README names https-proxy-agent as the common use, for proxying requests. The README also documents browser usage: the client works in modern browsers with the built-in fetch, but blocks tokens in a browser unless dangerouslyAllowBrowser is set, because a token in a web page isn't a secret. The baseUrl option lets you point at a mock server, which is useful for tests, but that is about the destination, not the transport.
notion-sdk-js versus the Python client and raw fetch
The closest alternative in Python is a Python client for the same API, so the choice is mostly about your runtime. If your data pipeline is Python, use the Python client; there is no advantage to shelling out to Node. The JavaScript client's advantage is that it is the one that fits a Node service, a serverless function, or a script in an existing JS codebase, with TypeScript types shipped in the package.
The other alternative is calling the API with fetch or axios directly. That is not unreasonable for a handful of endpoints. You would set the Authorization and Notion-Version headers, build URLs, and handle 429s yourself. What you give up is the exported constants, the APIErrorCode union for narrowing errors, isNotionClientError, and the built-in retry policy. For one endpoint called once a day, hand-rolled fetch is fewer dependencies. For anything that queries data sources, handles errors, and runs unattended, the client earns its place.
A third option, relevant if your problem is really "keep two systems in sync", is a dedicated integration platform. The SDK will not do that for you, and neither will the Python client. Both are request-response clients.
Maintenance, licence, and what upgrading costs
The repository is not archived, and the last push was on 2026-10-30. The most recent release listed is v5.30.0 on 2026-10-30, following v5.29.0 on 2026-10-29 and v5.28.0 on 2026-10-22. The cadence is steady, and the package.json version matches the release tag at 5.30.0.
The licence is MIT, declared in package.json and present as a LICENSE file at the repository root. MIT permits commercial use and modification with the copyright notice retained. That is a statement about the licence text, not legal advice; if your organisation has specific obligations around attribution or distribution, check with whoever handles that.
Upgrade cost is the part to plan for. This is a v5 line, and the API it wraps is versioned separately. A minor SDK bump can still change behaviour if the underlying API version changes. The published package contains only build/package.json and build/src/**, so you cannot read the TypeScript source from node_modules; you get compiled output and .d.ts files. The repository does ship a check:compatibility script, but that runs against the build inside the repo, not in your installed copy. The practical approach is to pin the version in package.json, read the release notes before bumping, and keep integration tests that exercise the specific endpoints you call.
Editorial conclusion
Adopt @notionhq/client if you are writing Node 22 or newer code against the Notion API and want typed request objects, automatic retries on 429 and 529, and structured APIResponseError codes instead of hand-rolled fetch calls. Do not adopt it if you need offline caching or a synchronisation layer, since every method is a network call. Verify two things first: that your integration token is stored outside source control and passed as auth, and that your write paths tolerate automatic retries on server errors (500, 503).
Frequently asked questions
What are the limitations of the Notion API?
The repository documents the SDK, not limits of the Notion API itself. The client can automatically retry rate limits (429), service overloads (529), and server errors (500, 503), and it blocks tokens in a browser unless dangerouslyAllowBrowser is set.
Are Notion APIs free?
The repository does not document pricing or plan requirements for the Notion API. The SDK itself is MIT licensed, but that covers the client code, not access to the service.
What is Notion coded in?
The repository does not describe Notion's own implementation. The SDK repository's primary language is TypeScript, and the published package is compiled with tsc.
Can Notion be used for coding?
The repository does not describe Notion as a coding environment. It documents a JavaScript and TypeScript client for the Notion API, which lets you write code that reads and writes Notion content.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/makenotion-notion-sdk-js)Star history chart for your README
[](https://hysenlabs.com/projects/makenotion-notion-sdk-js)Was this useful?