Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/makenotion/notion-sdk-js/llms.txt

Use this file to discover all available pages before exploring further.

The Notion SDK is written in TypeScript and provides full type definitions for all API requests and responses.

Type-Safe API Calls

All client methods are fully typed with request parameters and response types:
import { Client } from '@notionhq/client'
import type { PageObjectResponse, GetPageResponse } from '@notionhq/client'

const notion = new Client({ auth: process.env.NOTION_API_KEY })

// TypeScript knows the return type
const response: GetPageResponse = await notion.pages.retrieve({
  page_id: 'page-id',
})

// response is PageObjectResponse | PartialPageObjectResponse
if ('url' in response) {
  // TypeScript narrows to PageObjectResponse
  const url: string = response.url
}

Importing Types

All types are exported from the main package:
import type {
  // Response types
  PageObjectResponse,
  BlockObjectResponse,
  DatabaseObjectResponse,
  UserObjectResponse,
  CommentObjectResponse,
  
  // Partial types
  PartialPageObjectResponse,
  PartialBlockObjectResponse,
  PartialDatabaseObjectResponse,
  
  // Request parameter types
  CreatePageParameters,
  UpdatePageParameters,
  QueryDataSourceParameters,
  
  // Response types
  CreatePageResponse,
  ListBlockChildrenResponse,
  SearchResponse,
  
  // Rich text types
  RichTextItemResponse,
  TextRichTextItemResponse,
  MentionRichTextItemResponse,
  
  // Error types
  NotionClientError,
  APIErrorCode,
  ClientErrorCode,
} from '@notionhq/client'

Request Parameter Types

Each API method has a corresponding parameter type:
import type { CreatePageParameters } from '@notionhq/client'

const params: CreatePageParameters = {
  parent: { database_id: 'database-id' },
  properties: {
    Name: {
      title: [{ text: { content: 'New Page' } }],
    },
  },
}

await notion.pages.create(params)

Common Parameter Types

import type {
  GetBlockParameters,
  UpdateBlockParameters,
  AppendBlockChildrenParameters,
  CreatePageParameters,
  UpdatePageParameters,
  GetPagePropertyParameters,
  QueryDataSourceParameters,
  SearchParameters,
  ListUsersParameters,
  CreateCommentParameters,
} from '@notionhq/client'

Response Types

Each API method returns a typed response:
import type {
  GetPageResponse,
  CreatePageResponse,
  UpdatePageResponse,
  ListBlockChildrenResponse,
  QueryDataSourceResponse,
  SearchResponse,
} from '@notionhq/client'

const page: GetPageResponse = await notion.pages.retrieve({
  page_id: 'page-id',
})

const blocks: ListBlockChildrenResponse = await notion.blocks.children.list({
  block_id: 'block-id',
})

Type Guards

Use type guards to narrow union types:

Object Type Guards

import {
  isFullPage,
  isFullBlock,
  isFullDataSource,
  isFullDatabase,
  isFullUser,
  isFullComment,
} from '@notionhq/client'
import type { GetPageResponse } from '@notionhq/client'

const response: GetPageResponse = await notion.pages.retrieve({
  page_id: 'page-id',
})

// response is PageObjectResponse | PartialPageObjectResponse
if (isFullPage(response)) {
  // response is now PageObjectResponse
  console.log(response.url)
  console.log(response.created_time)
  console.log(response.properties)
}

Full Page or Data Source

import { isFullPageOrDataSource } from '@notionhq/client'
import type { SearchResponse } from '@notionhq/client'

const searchResults: SearchResponse = await notion.search({
  query: 'meeting notes',
})

for (const result of searchResults.results) {
  if (isFullPageOrDataSource(result)) {
    // result is PageObjectResponse | DataSourceObjectResponse
    console.log(result.id)
    if ('url' in result) {
      // result is PageObjectResponse
      console.log('Page URL:', result.url)
    }
  }
}

Rich Text Type Guards

import type { RichTextItemResponse } from '@notionhq/client'

function extractText(richText: RichTextItemResponse[]): string {
  return richText
    .map(item => {
      if (item.type === 'text') {
        // TypeScript narrows to TextRichTextItemResponse
        return item.text.content
      } else if (item.type === 'mention') {
        // TypeScript narrows to MentionRichTextItemResponse
        return item.mention.type
      } else if (item.type === 'equation') {
        // TypeScript narrows to EquationRichTextItemResponse
        return item.equation.expression
      }
      return ''
    })
    .join('')
}

Error Type Guards

import {
  isNotionClientError,
  isHTTPResponseError,
  APIResponseError,
  RequestTimeoutError,
} from '@notionhq/client'

try {
  await notion.pages.retrieve({ page_id: 'page-id' })
} catch (error: unknown) {
  if (isNotionClientError(error)) {
    // error is NotionClientError
    console.log('Error code:', error.code)
  }
  
  if (isHTTPResponseError(error)) {
    // error is APIResponseError | UnknownHTTPResponseError
    console.log('HTTP status:', error.status)
    console.log('Response body:', error.body)
  }
  
  if (APIResponseError.isAPIResponseError(error)) {
    // error is APIResponseError
    console.log('API error:', error.code)
    console.log('Request ID:', error.request_id)
  }
  
  if (RequestTimeoutError.isRequestTimeoutError(error)) {
    // error is RequestTimeoutError
    console.log('Request timed out')
  }
}

Block Types

Different block types have distinct structures:
import type {
  BlockObjectResponse,
  ParagraphBlockObjectResponse,
  Heading1BlockObjectResponse,
  CodeBlockObjectResponse,
  ImageBlockObjectResponse,
  ToDoBlockObjectResponse,
} from '@notionhq/client'

const block: BlockObjectResponse = await notion.blocks.retrieve({
  block_id: 'block-id',
})

switch (block.type) {
  case 'paragraph':
    // block.paragraph is defined
    console.log(block.paragraph.rich_text)
    break
  case 'heading_1':
    // block.heading_1 is defined
    console.log(block.heading_1.rich_text)
    break
  case 'code':
    // block.code is defined
    console.log(block.code.language)
    console.log(block.code.rich_text)
    break
  case 'to_do':
    // block.to_do is defined
    console.log(block.to_do.checked)
    console.log(block.to_do.rich_text)
    break
}

Property Types

Database properties have different value types:
import type {
  PageObjectResponse,
  TitlePropertyItemObjectResponse,
  RichTextPropertyItemObjectResponse,
  NumberPropertyItemObjectResponse,
  SelectPropertyItemObjectResponse,
  MultiSelectPropertyItemObjectResponse,
  DatePropertyItemObjectResponse,
  CheckboxPropertyItemObjectResponse,
} from '@notionhq/client'

const page = await notion.pages.retrieve({ page_id: 'page-id' }) as PageObjectResponse

for (const [propertyName, property] of Object.entries(page.properties)) {
  switch (property.type) {
    case 'title':
      console.log('Title:', property.title.map(t => t.plain_text).join(''))
      break
    case 'rich_text':
      console.log('Text:', property.rich_text.map(t => t.plain_text).join(''))
      break
    case 'number':
      console.log('Number:', property.number)
      break
    case 'select':
      console.log('Select:', property.select?.name)
      break
    case 'multi_select':
      console.log('Tags:', property.multi_select.map(s => s.name).join(', '))
      break
    case 'date':
      console.log('Date:', property.date?.start)
      break
    case 'checkbox':
      console.log('Checked:', property.checkbox)
      break
  }
}

Generic Type Utilities

Extract types from SDK types:
import type { BlockObjectResponse } from '@notionhq/client'

// Extract specific block type
type ParagraphBlock = Extract<BlockObjectResponse, { type: 'paragraph' }>
type HeadingBlock = Extract<BlockObjectResponse, { type: 'heading_1' | 'heading_2' | 'heading_3' }>
type CodeBlock = Extract<BlockObjectResponse, { type: 'code' }>

function processParagraph(block: ParagraphBlock) {
  // block.paragraph is always defined
  const text = block.paragraph.rich_text.map(t => t.plain_text).join('')
  return text
}

Helper Type Examples

Create Type-Safe Wrappers

import type { CreatePageParameters, PageObjectResponse } from '@notionhq/client'

interface DatabasePage {
  title: string
  status: string
  tags: string[]
  dueDate?: string
}

function createDatabasePage(
  databaseId: string,
  page: DatabasePage
): CreatePageParameters {
  return {
    parent: { database_id: databaseId },
    properties: {
      Name: {
        title: [{ text: { content: page.title } }],
      },
      Status: {
        select: { name: page.status },
      },
      Tags: {
        multi_select: page.tags.map(tag => ({ name: tag })),
      },
      ...(page.dueDate && {
        'Due Date': {
          date: { start: page.dueDate },
        },
      }),
    },
  }
}

await notion.pages.create(
  createDatabasePage('database-id', {
    title: 'New Task',
    status: 'In Progress',
    tags: ['urgent', 'bug'],
    dueDate: '2026-03-10',
  })
)

Extract Property Values

import type { PageObjectResponse } from '@notionhq/client'

function getPageTitle(page: PageObjectResponse): string {
  for (const property of Object.values(page.properties)) {
    if (property.type === 'title') {
      return property.title.map(t => t.plain_text).join('')
    }
  }
  return 'Untitled'
}

function getPropertyValue(
  page: PageObjectResponse,
  propertyName: string
): unknown {
  const property = page.properties[propertyName]
  if (!property) return undefined

  switch (property.type) {
    case 'title':
      return property.title.map(t => t.plain_text).join('')
    case 'rich_text':
      return property.rich_text.map(t => t.plain_text).join('')
    case 'number':
      return property.number
    case 'select':
      return property.select?.name
    case 'multi_select':
      return property.multi_select.map(s => s.name)
    case 'date':
      return property.date
    case 'checkbox':
      return property.checkbox
    default:
      return undefined
  }
}

Type-Safe Configuration

import { Client, LogLevel } from '@notionhq/client'
import type { ClientOptions, RetryOptions } from '@notionhq/client'

const retryConfig: RetryOptions = {
  maxRetries: 3,
  initialRetryDelayMs: 1000,
  maxRetryDelayMs: 30000,
}

const clientOptions: ClientOptions = {
  auth: process.env.NOTION_API_KEY,
  logLevel: LogLevel.INFO,
  timeoutMs: 60000,
  retry: retryConfig,
}

const notion = new Client(clientOptions)

Working with Unknown Types

When dealing with dynamic data, use type assertions carefully:
import type { PageObjectResponse } from '@notionhq/client'
import { isFullPage } from '@notionhq/client'

const response = await notion.pages.retrieve({ page_id: 'page-id' })

// Option 1: Type guard
if (isFullPage(response)) {
  const page: PageObjectResponse = response
  console.log(page.url)
}

// Option 2: Type assertion (use sparingly)
const page = response as PageObjectResponse
if ('url' in page) {
  console.log(page.url)
}
Use type assertions sparingly. Prefer type guards like isFullPage() to safely narrow types.

Best Practices

  1. Import types separately - Use import type { ... } for type-only imports
  2. Use type guards - Prefer isFullPage() over manual type checks
  3. Leverage discriminated unions - Use type fields to narrow block/property types
  4. Avoid any - The SDK provides complete types; use them
  5. Type function parameters - Explicitly type parameters that accept SDK types
import type { BlockObjectResponse, PageObjectResponse } from '@notionhq/client'
import { isFullBlock, isFullPage } from '@notionhq/client'

// Good: Explicitly typed and type-guarded
async function getPageBlocks(
  notion: Client,
  pageId: string
): Promise<BlockObjectResponse[]> {
  const response = await notion.blocks.children.list({ block_id: pageId })
  return response.results.filter(isFullBlock)
}

// Good: Type guard before accessing properties
function extractPageTitle(page: GetPageResponse): string {
  if (isFullPage(page)) {
    return getPageTitle(page)
  }
  return 'Unknown'
}

// Avoid: Implicit any
// function processPage(page) { ... }

// Avoid: Type assertion without guard
// const page = response as PageObjectResponse

Build docs developers (and LLMs) love