Skip to content

Configuration & Features

Configure the Notion client with various options to customize behavior.


Initialize the Notion client with configuration options:

import { Notion } from '@visus-io/notion-sdk-ts';
const notion = new Notion({
auth: process.env.NOTION_TOKEN, // Required
// All other options are optional
});

The auth parameter is required. It must contain your Notion integration token.

  1. Go to https://www.notion.so/my-integrations.
  2. Click “New integration”.
  3. Give the integration a name.
  4. Select the capabilities you need.
  5. Copy the “Internal Integration Token”.
const notion = new Notion({
auth: process.env.NOTION_TOKEN,
});

Security best practice: Always use environment variables for tokens. Do not hardcode tokens in your code.

Terminal window
# .env file
NOTION_TOKEN=secret_your_token_here
// Load from .env
import { config } from 'dotenv';
config();
const notion = new Notion({
auth: process.env.NOTION_TOKEN,
});

The SDK uses Notion API version 2026-03-11. This version is a fixed constant. You cannot override it with client options. All schemas, request bodies, and helpers depend on this version.

The SDK exports the target version as a read-only constant:

import { NOTION_VERSION } from '@visus-io/notion-sdk-ts';
console.log(NOTION_VERSION); // '2026-03-11'
// Useful for logging or conditional logic
console.log(`Using Notion API version: ${NOTION_VERSION}`);

Every outgoing HTTP request carries the header Notion-Version: 2026-03-11 automatically. You do not need to configure this header.

See the Migration Guide to upgrade from an earlier SDK version.


Set how long the client waits for a response before the request times out.

const notion = new Notion({
auth: process.env.NOTION_TOKEN,
// Default: 60,000ms (60 seconds)
});
const notion = new Notion({
auth: process.env.NOTION_TOKEN,
timeoutMs: 30_000, // 30 seconds
});
const notion = new Notion({
auth: process.env.NOTION_TOKEN,
timeoutMs: 5_000, // 5 seconds (for fast-fail scenarios)
});
import { NotionRequestTimeoutError } from '@visus-io/notion-sdk-ts';
try {
await notion.pages.retrieve('page-id');
} catch (error) {
if (error instanceof NotionRequestTimeoutError) {
console.error('Request timed out after', error.message);
}
}

The SDK handles rate limiting automatically with retry logic.

const notion = new Notion({
auth: process.env.NOTION_TOKEN,
retryOnRateLimit: true, // Default: automatically retry 429 responses
maxRetries: 3, // Default: retry up to 3 times
});

How it works:

  1. The SDK receives a 429 Too Many Requests response.
  2. The SDK checks the Retry-After header from the Notion API.
  3. The SDK waits for the duration in the header.
  4. If the header is missing, the SDK uses exponential backoff instead: 1 second, 2 seconds, 4 seconds, 8 seconds, and so on, up to a maximum of 60 seconds.
  5. The SDK retries the request automatically.
const notion = new Notion({
auth: process.env.NOTION_TOKEN,
retryOnRateLimit: false, // Do not retry on 429
});

Note: retryOnRateLimit controls only 429 retries. The SDK always retries 529 Service Overload responses, up to maxRetries, no matter the value of retryOnRateLimit. See isServiceOverloaded().

const notion = new Notion({
auth: process.env.NOTION_TOKEN,
maxRetries: 5, // Retry up to 5 times
});
const notion = new Notion({
auth: process.env.NOTION_TOKEN,
maxRetries: 0, // Never retry
});

Retries do not prevent all rate limits. You might still get a rate-limited response:

import { NotionAPIError } from '@visus-io/notion-sdk-ts';
try {
await notion.pages.retrieve('page-id');
} catch (error) {
if (error instanceof NotionAPIError && error.isRateLimited()) {
console.error('Rate limited after retries');
console.error('Retry after:', error.message);
}
}

The SDK uses the native fetch API in Node 18 and later. You can provide your own implementation instead.

const notion = new Notion({
auth: process.env.NOTION_TOKEN,
// Uses native fetch by default
});

A custom fetch implementation is useful for these cases:

  • Custom logging or telemetry
  • A different HTTP client
  • Proxy support
  • Tests with mock data
const notion = new Notion({
auth: process.env.NOTION_TOKEN,
fetch: async (url, init) => {
console.log(`Fetching: ${url}`);
const response = await fetch(url, init);
console.log(`Status: ${response.status}`);
return response;
},
});
import { HttpsProxyAgent } from 'https-proxy-agent';
const proxyAgent = new HttpsProxyAgent('http://proxy.example.com:8080');
const notion = new Notion({
auth: process.env.NOTION_TOKEN,
fetch: (url, init) => fetch(url, { ...init, agent: proxyAgent }),
});
const mockFetch = async (url: string, init?: RequestInit) => {
return new Response(JSON.stringify({ id: 'test-id' }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
};
const notion = new Notion({
auth: 'test-token',
fetch: mockFetch,
});

Change the API base URL. Most projects do not need this option.

const notion = new Notion({
auth: process.env.NOTION_TOKEN,
baseUrl: 'https://api.notion.com', // Default
});

A custom base URL is useful for these cases:

  • Tests against a mock server
  • A proxy
  • Development or staging environments
const notion = new Notion({
auth: process.env.NOTION_TOKEN,
baseUrl: 'http://localhost:3000', // Local mock server
});

This example shows a fully configured client with all options:

import { Notion } from '@visus-io/notion-sdk-ts';
import { config } from 'dotenv';
config(); // Load .env
const notion = new Notion({
// Required
auth: process.env.NOTION_TOKEN!,
// API Configuration
baseUrl: 'https://api.notion.com', // Default
// Timeout Configuration
timeoutMs: 60_000, // 60 seconds (default)
// Rate Limiting & Retries
retryOnRateLimit: true, // Default
maxRetries: 3, // Default
// Custom Fetch (optional)
fetch: async (url, init) => {
// Add custom logging or a proxy
console.log(`API Call: ${url}`);
return fetch(url, init);
},
});

Configure the client differently for each environment:

const isProduction = process.env.NODE_ENV === 'production';
const notion = new Notion({
auth: process.env.NOTION_TOKEN!,
timeoutMs: isProduction ? 60_000 : 10_000, // Use a shorter timeout for development
maxRetries: isProduction ? 3 : 0, // Skip retries in development for faster feedback
fetch: isProduction
? undefined // Use default fetch
: async (url, init) => {
// Add debug logging in development
console.log(`[DEV] ${init?.method || 'GET'} ${url}`);
return fetch(url, init);
},
});