
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
notebooklm-kit
Advanced tools

A TypeScript SDK for programmatic access to Google NotebookLM.
The NotebookLM Kit provides a clean, service-based interface to all NotebookLM features. Perfect for building AI research assistants, study tools, content generators, and automated knowledge management systems.
| Feature | Method | Example |
|---|---|---|
Notebook Management list(), create(), get(), update(), delete(), share() | ||
| List Notebooks | sdk.notebooks.list() | notebook-list.ts |
| Create Notebook | sdk.notebooks.create() | notebook-create.ts |
| Get Notebook | sdk.notebooks.get() | notebook-get.ts |
| Update Notebook | sdk.notebooks.update() | notebook-update.ts |
| Delete Notebook | sdk.notebooks.delete() | notebook-delete.ts |
| Share Notebook ⚠️ Experimental | sdk.notebooks.share() | notebook-share.ts |
Source Management list(), get(), add.url(), add.text(), add.youtube(), add.file(), add.drive(), add.batch(), add.web.searchAndWait(), update(), delete(), status() | ||
| List Sources | sdk.sources.list() | source-list.ts |
| Add URL Source | sdk.sources.add.url() | source-add-url.ts |
| Add Text Source | sdk.sources.add.text() | source-add-text.ts |
| Add YouTube Source | sdk.sources.add.youtube() | source-add-youtube.ts |
| Add File Source | sdk.sources.add.file() | source-add-file.ts |
| Add Drive Source ⚠️ Experimental | sdk.sources.add.drive() | source-add-drive.ts |
| Add Batch Sources | sdk.sources.add.batch() | source-add-batch.ts |
| Web Search Source | sdk.sources.add.web.searchAndWait() | source-web-search.ts |
| Advanced Web Search | sdk.sources.add.web.searchAndWait() | source-web-search-advanced.ts |
| Get Source | sdk.sources.get() | source-get.ts |
| Update Source | sdk.sources.update() | source-update.ts |
| Delete Source | sdk.sources.delete() | source-delete.ts |
| Check Source Status | sdk.sources.status() | source-status.ts |
Artifact Generation create(), list(), get(), download(), rename(), delete(), share() | ||
| Create Artifact | sdk.artifacts.create() | artifact-create.ts |
| Create Artifact (Subservices) | sdk.artifacts.{type}.create() | artifact-create-subservices.ts |
| List Artifacts | sdk.artifacts.list() | artifact-list.ts |
| Get Artifact | sdk.artifacts.get() | artifact-get.ts |
| Download Artifact | sdk.artifacts.download() | artifact-download.ts |
| Download Video | sdk.artifacts.download() | artifact-video.ts |
| Download Slides | sdk.artifacts.download() | slide-download-test.ts |
| Rename Artifact | sdk.artifacts.rename() | artifact-rename.ts |
| Delete Artifact | sdk.artifacts.delete() | artifact-delete.ts |
| Share Artifact | sdk.artifacts.share() | artifact-share.ts |
Chat & Generation chat(), chatStream(), setChatConfig() | ||
| Chat | sdk.generation.chat() | chat-basic.ts |
| Stream Chat | sdk.generation.chatStream() | chat-conversation.ts |
| Set Chat Config | sdk.generation.setChatConfig() | generation-set-chat-config.ts |
Notes Management list(), create(), update(), delete() | ||
| List Notes | sdk.notes.list() | note-list.ts |
| Create Note | sdk.notes.create() | note-create.ts |
| Update Note | sdk.notes.update() | note-update.ts |
| Delete Note | sdk.notes.delete() | note-delete.ts |
npm install notebooklm-kit
From source:
git clone https://github.com/photon-hq/notebooklm-kit.git && cd notebooklm-kit && npm run setup
Requirements: Node.js >=18.0.0
Current version: 2.2.0
When working with the repository, you can use the following npm scripts:
| Script | Description |
|---|---|
npm install | Install dependencies and automatically build (runs postinstall) |
npm run setup | Full setup: install dependencies, Playwright, and build |
npm run build | Compile TypeScript to JavaScript |
npm run build:dev | Build only (no reinstall) |
npm run dev | Watch mode (auto-rebuild on file changes) |
npm run clean | Remove compiled dist/ directory |
First-time setup:
npm run setup
Build only (no reinstall):
npm run build:dev
Watch mode (auto-rebuild):
npm run dev
Clean build:
npm run clean && npm run build
npm install notebooklm-kit
Auto (browser): Create .env with GOOGLE_EMAIL and GOOGLE_PASSWORD (no 2FA required)
Manual: Create .env with NOTEBOOKLM_AUTH_TOKEN and NOTEBOOKLM_COOKIES from browser DevTools (Network → Cookie header, Console → window.WIZ_global_data.SNlM0e)
import { NotebookLMClient } from 'notebooklm-kit';
import dotenv from 'dotenv';
// Load .env file from project root (automatically detected)
dotenv.config();
async function main() {
const sdk = new NotebookLMClient({
// Credentials are automatically loaded from .env file
// Priority: NOTEBOOKLM_AUTH_TOKEN/NOTEBOOKLM_COOKIES > GOOGLE_EMAIL/GOOGLE_PASSWORD
});
try {
await sdk.connect();
// List notebooks
const notebooks = await sdk.notebooks.list();
console.log(`Found ${notebooks.length} notebooks`);
// Create a notebook
const notebook = await sdk.notebooks.create({
title: 'My Research',
emoji: '📚',
});
console.log(`Created: ${notebook.title}`);
} catch (error) {
console.error('Error:', error);
} finally {
sdk.dispose();
}
}
main();
Note: The SDK automatically loads credentials from environment variables. See SDK Initialization for all configuration options.
The repository includes working examples in the examples/ directory. To run them:
.env file in the project root (see Authentication above)tsx:
npx tsx examples/notebook-list.ts
npx tsx examples/chat-basic.ts
Chat Example Usage:
# Interactive mode (prompts for notebook and message)
npx tsx examples/chat-basic.ts
# With notebook ID and message (streaming mode - default)
npx tsx examples/chat-basic.ts <notebook-id> "What are the key findings?"
# Non-streaming mode (get complete response at once)
npx tsx examples/chat-basic.ts <notebook-id> "What are the key findings?" --no-stream
Note: The examples automatically detect and load the .env file from the project root, regardless of where you run them from.
sdk.notebooks - Notebook Management| Feature | Description | Method | Example |
|---|---|---|---|
| List Notebooks | List all your notebooks (recently viewed) | sdk.notebooks.list() | notebook-list.ts |
| Get Notebook | Get full details of a specific notebook | sdk.notebooks.get(notebookId) | notebook-get.ts |
| Create Notebook | Create a new notebook (auto-generates title if empty) | sdk.notebooks.create(options) | notebook-create.ts |
| Update Notebook | Update notebook title or emoji | sdk.notebooks.update(notebookId, options) | notebook-update.ts |
| Delete Notebook | Delete one or more notebooks | sdk.notebooks.delete(notebookIds) | notebook-delete.ts |
| Share Notebook ⚠️ Experimental | Share notebook with users or enable link sharing | sdk.notebooks.share(notebookId, options) | notebook-share.ts |
sdk.sources - Source Managementsdk.artifacts - Artifact Management| Feature | Description | Method | Example |
|---|---|---|---|
| Create Artifact | Create study material (quiz, flashcards, mind map, etc.) | sdk.artifacts.create() or sdk.artifacts.{type}.create() | artifact-create.ts artifact-create-subservices.ts |
| List Artifacts | List all artifacts in a notebook (with filtering) | sdk.artifacts.list() | artifact-list.ts |
| Get Artifact | Get artifact details (auto-fetches content when ready) | sdk.artifacts.get() | artifact-get.ts |
| Download Artifact | Download artifact data to disk (quiz/flashcard JSON, audio file) | sdk.artifacts.download() | artifact-download.ts |
| Download Video | Download video artifact as MP4 file | sdk.artifacts.download() | artifact-video.ts |
| Download Slides | Download slide deck as PDF or PNG files | sdk.artifacts.download() | slide-download-test.ts |
| Rename Artifact | Rename an artifact | sdk.artifacts.rename() | artifact-rename.ts |
| Delete Artifact | Delete an artifact | sdk.artifacts.delete() | artifact-delete.ts |
| Share Artifact | Share artifact/notebook with users or enable link sharing | sdk.artifacts.share() | artifact-share.ts |
sdk.generation - Generation & Chat| Feature | Description | Method | Example |
|---|---|---|---|
| Chat (Non-streaming) | Chat with notebook content - returns complete response | sdk.generation.chat(notebookId, prompt, options?) | chat-basic.ts |
| Chat Stream | Chat with real-time streaming response chunks | sdk.generation.chatStream(notebookId, prompt, options?) | chat-basic.ts |
| Chat Conversation | Multi-turn conversations with history tracking | sdk.generation.chat(notebookId, prompt, { conversationHistory }) | chat-conversation.ts |
| Set Chat Config | Configure chat (custom prompt, learning guide, response length) | sdk.generation.setChatConfig(notebookId, config) | generation-set-chat-config.ts |
sdk.notes - Notes Management| Feature | Description | Method | Example |
|---|---|---|---|
| List Notes | List all notes in a notebook | sdk.notes.list(notebookId) | note-list.ts |
| Create Note | Create a new note | sdk.notes.create(notebookId, options) | note-create.ts |
| Update Note | Update a note | sdk.notes.update(notebookId, noteId, options) | note-update.ts |
| Delete Note | Delete a note | sdk.notes.delete(notebookId, noteIds) | note-delete.ts |
Methods: sdk.connect() | sdk.dispose()
Basic Usage:
import { NotebookLMClient } from 'notebooklm-kit';
import dotenv from 'dotenv';
dotenv.config(); // Load .env from project root
const sdk = new NotebookLMClient({
// Credentials loaded automatically from environment variables:
// NOTEBOOKLM_AUTH_TOKEN, NOTEBOOKLM_COOKIES
// or GOOGLE_EMAIL, GOOGLE_PASSWORD
});
try {
await sdk.connect(); // Initialize SDK, authenticate, start auto-refresh
// Now you can use sdk.notebooks, sdk.sources, etc.
const notebooks = await sdk.notebooks.list();
} finally {
sdk.dispose(); // Always cleanup
}
Explicit Credentials:
const sdk = new NotebookLMClient({
authToken: process.env.NOTEBOOKLM_AUTH_TOKEN!,
cookies: process.env.NOTEBOOKLM_COOKIES!,
// or
auth: {
email: process.env.GOOGLE_EMAIL!,
password: process.env.GOOGLE_PASSWORD!,
},
});
await sdk.connect();
Enable Debug Mode:
Debug mode provides detailed logging for troubleshooting, API calls, authentication, and internal operations.
Option 1: Environment Variable (Recommended)
# In your .env file
NOTEBOOKLM_DEBUG=true
Option 2: Config Option
const sdk = new NotebookLMClient({
debug: true, // Enable debug logging
// ... other config
});
await sdk.connect();
Option 3: Development Mode
# Automatically enables debug in development
NODE_ENV=development
What Debug Mode Logs:
Example Debug Output:
[DEBUG] RPC Call: wXbhsf with args: [null, 1, null, [2]]
[DEBUG] Response received: 200 OK
[DEBUG] Parsing chunked response: 3 chunks found
[DEBUG] Auto-refresh: Token expires in 5 minutes, refreshing now...
[DEBUG] Authentication: Extracting credentials from browser...
Disable Debug:
// Explicitly disable (overrides environment variable)
const sdk = new NotebookLMClient({
debug: false,
});
# Or in .env
NOTEBOOKLM_DEBUG=false
Credentials Resolution (in priority order):
authToken/cookies)NOTEBOOKLM_AUTH_TOKEN/NOTEBOOKLM_COOKIES)credentials.json in project root) - reused automaticallyauth.email/auth.password provided) - only if no saved credentialsNote: Set FORCE_REAUTH=true in .env to force re-authentication and ignore saved credentials
Initialization:
notebooks, sources, artifacts, etc.)Auto-Refresh:
connect()Always call dispose() when done:
try {
await sdk.connect();
// ... use SDK ...
} finally {
await sdk.dispose(); // Always cleanup
}
Authentication is handled automatically when you call sdk.connect(). Credentials are resolved in this priority order:
authToken/cookies)NOTEBOOKLM_AUTH_TOKEN/NOTEBOOKLM_COOKIES)credentials.json in project root)auth.email/auth.password provided)See the Authentication section for detailed setup instructions and all configuration options.
Reference: Official Documentation
Plan Types: standard (default) | plus | pro | ultra
| Limit | Standard | Plus | Pro | Ultra |
|---|---|---|---|---|
| Notebooks | 100/user | 200/user | 500/user | 500/user |
| Sources/Notebook | 50 | 100 | 300 | 600 |
| Words/Source | 500,000 | 500,000 | 500,000 | 500,000 |
| File Size | 200MB | 200MB | 200MB | 200MB |
| Chats/Day | 50 | 200 | 500 | 5,000 |
| Audio/Video/Day | 3 | 6 | 20 | 200 |
| Reports/Day | 10 | 20 | 100 | 1,000 |
| Deep Research/Month | 10 | 90 | 600 | 6,000 |
| Mind Maps | Unlimited | Unlimited | Unlimited | Unlimited |
enforceQuotas: true), disabled by defaultplan: 'pro'Method: Use auth config with email/password
const sdk = new NotebookLMClient({
auth: {
email: process.env.GOOGLE_EMAIL,
password: process.env.GOOGLE_PASSWORD,
headless: true, // default: true
},
});
await sdk.connect(); // Logs in, extracts auth token, prompts for cookies, saves to credentials.json
File Location: Create .env in your project root directory (same directory as package.json).
# .env file location: /path/to/your-project/.env
# Option 1: Auto-login with email/password (recommended - requires no 2FA)
GOOGLE_EMAIL="your-email@gmail.com"
GOOGLE_PASSWORD="your-password"
# Option 2: Manual credentials (for production or when auto-login isn't available)
NOTEBOOKLM_AUTH_TOKEN="ACi2F2NZSD7yrNvFMrCkP3vZJY1R:1766720233448"
NOTEBOOKLM_COOKIES="_ga=GA1.1.1949425436.1764104083; SID=g.a0005AiwX...; ..."
# Optional: Retry configuration
NOTEBOOKLM_MAX_RETRIES=1 # Default: 1
NOTEBOOKLM_RETRY_DELAY=1000 # Default: 1000ms
NOTEBOOKLM_RETRY_MAX_DELAY=5000 # Default: 5000ms
# Optional: Debug mode (enables detailed logging)
NOTEBOOKLM_DEBUG=true # Enable debug logging for troubleshooting
# Optional: Force re-authentication (ignore saved credentials)
FORCE_REAUTH=true
Important:
.env file must be in the project root (not in subdirectories).env file is automatically ignored by git (see .gitignore)Method: Provide authToken and cookies directly
const sdk = new NotebookLMClient({
authToken: process.env.NOTEBOOKLM_AUTH_TOKEN!,
cookies: process.env.NOTEBOOKLM_COOKIES!,
enforceQuotas: true, // optional
plan: 'standard', // optional: 'standard' | 'plus' | 'pro' | 'ultra'
});
await sdk.connect();
window.WIZ_global_data.SNlM0eLocation: credentials.json in project root (e.g., notebooklm-kit/credentials.json)
When using auto-login with email/password:
credentials.json for future useSubsequent runs:
To force re-authentication:
FORCE_REAUTH=true in .env, orcredentials.json fileSecurity Note: credentials.json contains sensitive authentication data. It's automatically added to .gitignore to prevent accidental commits.
Default: Enabled with 'auto' strategy (recommended)
// Default: auto strategy (expiration-based + time-based fallback)
const sdk = new NotebookLMClient({
auth: { email: '...', password: '...' },
// autoRefresh: true (default)
});
// Time-based (simple, predictable)
autoRefresh: { strategy: 'time', interval: 10 * 60 * 1000 }
// Expiration-based (maximum efficiency)
autoRefresh: { strategy: 'expiration', refreshAhead: 5 * 60 * 1000 }
// Disable
autoRefresh: false
// Manual refresh
await sdk.refreshCredentials();
Method: sdk.getUsage() | sdk.getRemaining() | sdk.getQuotaManager()
const sdk = new NotebookLMClient({
auth: { email: '...', password: '...' },
enforceQuotas: true, // Enable client-side validation (disabled by default)
plan: 'pro', // Set plan for accurate limits
});
await sdk.connect();
// Check usage
const usage = sdk.getUsage();
const remaining = sdk.getRemaining('chats');
const limits = sdk.getQuotaManager().getLimits();
enforceQuotas: trueRateLimitError if limit exceeded (when enabled)Examples: notebook-list.ts | notebook-get.ts | notebook-create.ts | notebook-update.ts | notebook-delete.ts | notebook-share.ts
Method: sdk.notebooks.list()
Example: notebook-list.ts
Returns: Promise<Notebook[]>
Description: Lists all your notebooks (recently viewed). Returns a lightweight array of notebooks with essential information for display/selection.
Return Fields:
projectId: string - Unique notebook ID (required for other operations)title: string - Notebook titleemoji: string - Visual identifiersourceCount: number - Number of sources in the notebookget() for that)sources service for source operations)get() for sharing details)Usage:
const notebooks = await sdk.notebooks.list()
console.log(`Found ${notebooks.length} notebooks`)
notebooks.forEach(nb => {
console.log(`${nb.emoji} ${nb.title} (${nb.sourceCount} sources)`)
})
Method: sdk.notebooks.get(notebookId)
Example: notebook-get.ts
Parameters:
notebookId: string - The notebook ID (required)Returns: Promise<Notebook>
Description: Retrieves full details of a specific notebook, including analytics and sharing information. Makes parallel RPC calls to get complete notebook data.
Return Fields:
projectId: string - Unique notebook IDtitle: string - Notebook titleemoji: string - Visual identifiersourceCount?: number - Number of sources (analytics)lastAccessed?: string - Last accessed timestamp (ISO format, analytics)sharing?: SharingSettings - Sharing configuration:
isShared: boolean - Whether notebook is sharedshareUrl?: string - Share URL if sharedshareId?: string - Share IDpublicAccess?: boolean - Whether public access is enabledallowedUsers?: string[] - Array of user emails with accessRPC_GET_PROJECT and RPC_GET_SHARING_DETAILS in parallel for efficiencysources service for source operations)lastAccessed is extracted from notebook metadata if availableUsage:
const notebook = await sdk.notebooks.get('notebook-id')
console.log(`Title: ${notebook.title}`)
console.log(`Sources: ${notebook.sourceCount || 0}`)
console.log(`Last accessed: ${notebook.lastAccessed || 'Never'}`)
if (notebook.sharing?.isShared) {
console.log(`Share URL: ${notebook.sharing.shareUrl}`)
}
Method: sdk.notebooks.create(options)
Example: notebook-create.ts
Parameters:
options: CreateNotebookOptions
title: string - Notebook title (optional, auto-generated if empty)emoji?: string - Notebook emoji (optional)Returns: Promise<Notebook>
Description: Creates a new notebook. Automatically generates a title if not provided. Validates title length before creation.
Return Fields:
projectId: string - Unique notebook ID (use this for subsequent operations)title: string - Notebook title (as provided or auto-generated)emoji: string - Default emojiAuto-Generated Title Format:
If title is empty or not provided, generates: "Untitled Notebook {current date}"
Example: "Untitled Notebook 12/30/2024"
APIError if title exceeds limitsourceCount, lastAccessed, or sharing (not available for new notebooks)Usage:
// With title
const notebook = await sdk.notebooks.create({
title: 'My Research Project',
})
// With title and emoji
const notebook = await sdk.notebooks.create({
title: 'My Research Project',
emoji: '📚',
})
// Auto-generated title
const untitled = await sdk.notebooks.create({})
Method: sdk.notebooks.update(notebookId, options)
Example: notebook-update.ts
Parameters:
notebookId: string - The notebook ID (required, automatically trimmed)options: UpdateNotebookOptions
title?: string - New title (optional)emoji?: string - New emoji (optional)metadata?: Record<string, any> - Other metadata updates (optional)Returns: Promise<Notebook> (same as get() - full notebook details)
Description:
Updates notebook title or emoji. Returns full notebook details after update (same structure as get()). Supports updating emoji only, title only, or both together.
title or emoji) must be providedReturn Fields:
Same as get() - includes projectId, title, emoji, sourceCount, lastAccessed, sharing
Usage:
// Update title only
const updated = await sdk.notebooks.update('notebook-id', {
title: 'Updated Title',
})
// Update emoji only
const updated = await sdk.notebooks.update('notebook-id', {
emoji: '🔥',
})
// Update both title and emoji
const updated = await sdk.notebooks.update('notebook-id', {
title: 'New Title',
emoji: '⭐',
})
// Update all fields
const updated = await sdk.notebooks.update('notebook-id', {
title: 'New Title',
emoji: '🎯',
})
Method: sdk.notebooks.delete(notebookIds, options?)
Example: notebook-delete.ts
Parameters:
notebookIds: string | string[] - Single notebook ID or array of IDs (required)options?: DeleteNotebookOptions - Optional deletion options:
mode?: 'parallel' | 'sequential' - Execution mode (default: 'parallel')Returns: Promise<DeleteNotebookResult>
Description: Deletes one or more notebooks. For multiple notebooks, deletions are performed individually (either in parallel or sequentially) since Google's API does not support true batch deletion. Returns confirmation with deleted IDs and count.
Return Fields:
deleted: string[] - Array of successfully deleted notebook IDscount: number - Number of notebooks successfully deletedfailed?: string[] - Array of notebook IDs that failed to delete (only present if some failed)failedCount?: number - Number of notebooks that failed to deleteAPIError if any ID is invalidPromise.all(). Faster but may hit rate limits.deleted and failed arraysfailed array)Usage:
// Delete single notebook
const result = await sdk.notebooks.delete('notebook-id')
console.log(`Deleted ${result.count} notebook: ${result.deleted[0]}`)
// Delete multiple notebooks (parallel - default)
const result = await sdk.notebooks.delete(['id-1', 'id-2', 'id-3'])
console.log(`Deleted ${result.count} notebooks: ${result.deleted.join(', ')}`)
// Delete multiple notebooks (sequential - recommended for large batches)
const result = await sdk.notebooks.delete(['id-1', 'id-2', 'id-3'], { mode: 'sequential' })
console.log(`Deleted ${result.count} notebooks: ${result.deleted.join(', ')}`)
if (result.failed && result.failed.length > 0) {
console.log(`Failed to delete: ${result.failed.join(', ')}`)
}
⚠️ Experimental: This feature is experimental and may have limitations or breaking changes in future versions.
Method: sdk.notebooks.share(notebookId, options)
Example: notebook-share.ts
Parameters:
notebookId: string - The notebook ID (required, automatically trimmed)options: ShareNotebookOptions
users?: Array<{email: string, role: 2|3|4}> - Users to share with (optional)notify?: boolean - Notify users (default: true, only used when users are provided)accessType?: 1|2 - Access type: 1=anyone with link, 2=restricted (optional, default: 2)Returns: Promise<ShareNotebookResult>
Description: Shares notebook with users or enables link sharing. Supports multiple users with different roles. Automatically fetches updated sharing state after operation.
User Roles:
| Role | Value | Description |
|---|---|---|
| Editor | 2 | Can edit notebook content |
| Viewer | 3 | Can view notebook only |
| Remove | 4 | Remove user from shared list |
Access Types:
| Access Type | Value | Description |
|---|---|---|
| Anyone with link | 1 | Public access via share link |
| Restricted | 2 | Only specified users can access |
Return Fields:
shareUrl: string - Share URL (always present, even if not shared)success: boolean - Whether the share operation succeedednotebookId: string - The notebook ID that was sharedaccessType: 1|2 - Access type: 1=anyone with link, 2=restrictedisShared: boolean - Whether the notebook is shared (true if shared with users or link enabled)users?: Array<{email: string, role: 2|3}> - Users with access (only present if users were shared)Notify Behavior:
notify is only used when users are providedtrue (users are notified when permissions change)false to share silentlyAPIError if any email is invalidUsage:
// Share with users (restricted access, notify enabled by default)
const result = await sdk.notebooks.share('notebook-id', {
users: [
{ email: 'user1@example.com', role: 2 }, // editor
{ email: 'user2@example.com', role: 3 }, // viewer
],
notify: true,
accessType: 2, // restricted
})
// Share with users (silent, no notification)
const result = await sdk.notebooks.share('notebook-id', {
users: [
{ email: 'user@example.com', role: 2 },
],
notify: false,
accessType: 2,
})
// Enable link sharing (anyone with link)
const result = await sdk.notebooks.share('notebook-id', {
accessType: 1, // 1=anyone with link, 2=restricted
})
// Remove user (role: 4)
const result = await sdk.notebooks.share('notebook-id', {
users: [
{ email: 'user@example.com', role: 4 }, // remove
],
accessType: 2,
})
addFromURL(notebookId: string, options: AddURLSourceOptions) → Promise<string>Add a source from a URL (web page, YouTube, etc.).
Parameters:
notebookId: string - The notebook IDoptions.url: string - URL to addReturns:
string - Source IDExample:
const sourceId = await sdk.sources.addFromURL('notebook-id', {
url: 'https://example.com/article',
})
addFromText(notebookId: string, options: AddTextSourceOptions) → Promise<string | AddSourceResult>Add a source from text content.
Auto-Chunking: Large texts (>500k words) are automatically split into chunks and uploaded in parallel.
Parameters:
notebookId: string - The notebook IDoptions.title: string - Source titleoptions.content: string - Text contentReturns:
string - Source ID (if not chunked)AddSourceResult - Chunk metadata (if auto-chunked)Example:
// Small text (returns string)
const sourceId = await sdk.sources.addFromText('notebook-id', {
title: 'Research Notes',
content: 'Your text content here...',
})
// Large text (auto-chunked)
const result = await sdk.sources.addFromText('notebook-id', {
title: 'Large Document',
content: veryLongText, // > 500k words
})
if (typeof result === 'string') {
console.log(`Source ID: ${result}`)
} else {
console.log(`Uploaded ${result.chunks?.length || 0} chunks`)
}
addFromFile(notebookId: string, options: AddFileSourceOptions) → Promise<string | AddSourceResult>Add a source from a file (PDF, image, etc.).
Auto-Chunking: Large files (>200MB or >500k words) are automatically split into chunks and uploaded in parallel.
Parameters:
notebookId: string - The notebook IDoptions.content: Buffer | string - File content as Buffer or base64 stringoptions.fileName: string - File nameoptions.mimeType?: string - MIME type (e.g., 'application/pdf')Returns:
string - Source ID (if not chunked)AddSourceResult - Chunk metadata (if auto-chunked)Example:
import { readFile } from 'fs/promises'
// Small file (returns string)
const buffer = await readFile('./document.pdf')
const sourceId = await sdk.sources.addFromFile('notebook-id', {
content: buffer,
fileName: 'document.pdf',
mimeType: 'application/pdf',
})
// Large file (auto-chunked)
const largeBuffer = await readFile('./large-document.pdf')
const result = await sdk.sources.addFromFile('notebook-id', {
content: largeBuffer, // > 200MB or > 500k words
fileName: 'large-document.pdf',
})
if (typeof result === 'string') {
console.log(`Source ID: ${result}`)
} else {
console.log(`Uploaded ${result.chunks?.length || 0} chunks`)
}
addYouTube(notebookId: string, options: AddYouTubeSourceOptions) → Promise<string>Add a YouTube video as a source.
Parameters:
notebookId: string - The notebook IDoptions.urlOrId: string - YouTube URL or video IDReturns:
string - Source IDExample:
const sourceId = await sdk.sources.addYouTube('notebook-id', {
urlOrId: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ',
})
searchWebAndWait(notebookId: string, options: SearchWebOptions) → Promise<SearchWebResult>Search the web or Google Drive and wait for results.
Parameters:
notebookId: string - The notebook IDoptions.query: string - Search queryoptions.sourceType: SearchSourceType - WEB or GOOGLE_DRIVEoptions.mode: ResearchMode - STANDARD or DEEPReturns:
SearchWebResult - Object with:
sessionId: string - Session ID for adding sourcessources: Array<{sourceId: string, title: string, ...}> - Found sourcesExample:
import { SearchSourceType, ResearchMode } from 'notebooklm-kit'
const result = await sdk.sources.searchWebAndWait('notebook-id', {
query: 'machine learning trends 2024',
sourceType: SearchSourceType.WEB,
mode: ResearchMode.STANDARD,
})
// Add selected sources
const sourceIds = await sdk.sources.addDiscovered('notebook-id', {
sessionId: result.sessionId,
sourceIds: result.sources.slice(0, 5).map(s => s.sourceId),
})
pollProcessing(notebookId: string) → Promise<SourceProcessingStatus>Check source processing status.
Parameters:
notebookId: string - The notebook IDReturns:
SourceProcessingStatus - Object with:
readyCount: number - Number of ready sourcestotalCount: number - Total sourcesprocessingCount: number - Sources being processedfailedCount: number - Failed sourcesallReady: boolean - Whether all sources are readyExample:
const status = await sdk.sources.pollProcessing('notebook-id')
console.log(`Ready: ${status.readyCount}/${status.totalCount}`)
Examples: source-list.ts | source-get.ts | source-add-url.ts | source-add-text.ts | source-add-file.ts | source-add-youtube.ts | source-add-drive.ts | source-add-batch.ts | source-web-search.ts | source-web-search-advanced.ts | source-update.ts | source-delete.ts | source-status.ts
Method: sdk.sources.list(notebookId)
Example: source-list.ts
Parameters:
notebookId: string - The notebook ID (required)Returns: Promise<Source[]>
Description: Retrieves a list of all sources (URLs, text, files, YouTube videos, Google Drive files, etc.) associated with a notebook. Sources are extracted from the notebook response efficiently without requiring a separate RPC call.
Return Fields:
sourceId: string - Unique identifier for the sourcetitle?: string - Source title/nametype?: SourceType - Source type (URL, TEXT, PDF, YOUTUBE_VIDEO, GOOGLE_DRIVE, IMAGE, etc.)url?: string - Source URL (for URL/YouTube sources)createdAt?: string - Creation timestamp (ISO format)updatedAt?: string - Last modified timestamp (ISO format)status?: SourceStatus - Processing status (PROCESSING, READY, FAILED)metadata?: Record<string, any> - Additional metadata (file size, MIME type, etc.)Source Types:
URL - Web page URLTEXT - Text contentPDF - PDF fileYOUTUBE_VIDEO - YouTube videoGOOGLE_DRIVE - Google Drive fileIMAGE - Image fileVIDEO_FILE - Video file uploadPDF_FROM_DRIVE - PDF from Google DriveTEXT_NOTE - Text noteMIND_MAP_NOTE - Mind map notenotebooks.get())Usage:
// List all sources
const sources = await sdk.sources.list('notebook-id')
console.log(`Found ${sources.length} sources`)
// Filter by type
const pdfs = sources.filter(s => s.type === SourceType.PDF)
const urls = sources.filter(s => s.type === SourceType.URL)
// Check processing status
const ready = sources.filter(s => s.status === SourceStatus.READY)
const processing = sources.filter(s => s.status === SourceStatus.PROCESSING)
Method: sdk.sources.get(notebookId, sourceId?)
Example: source-get.ts
Parameters:
notebookId: string - The notebook ID (required)sourceId?: string - Optional source ID to get a single sourceReturns: Promise<Source | Source[]> - Single source if sourceId provided, array of all sources if omitted
Description:
Get one or all sources from a notebook. If sourceId is provided, returns a single source. If omitted, returns all sources (same as list()).
Return Fields:
Same as list() - see List Sources for field descriptions.
sourceId is omitted (same as list())sourceId is providedsourceId is providedUsage:
// Get all sources
const allSources = await sdk.sources.get('notebook-id')
// Get specific source
const source = await sdk.sources.get('notebook-id', 'source-id')
console.log(source.title)
Method: sdk.sources.add.url(notebookId, options)
Example: source-add-url.ts
Parameters:
notebookId: string - The notebook ID (required)options: AddSourceFromURLOptions
url: string - URL to add (required)title?: string - Optional custom titleReturns: Promise<string> - Source ID
Description:
Adds a web page URL as a source. Returns immediately after source is queued. Use status() to check if source is ready.
status() to check if source is readyUsage:
const sourceId = await sdk.sources.add.url('notebook-id', {
url: 'https://ai.google.dev/',
title: 'Google AI Developer',
})
// Check if ready
const status = await sdk.sources.status('notebook-id')
if (!status.processing.includes(sourceId)) {
console.log('Source is ready!')
}
Method: sdk.sources.add.text(notebookId, options)
Example: source-add-text.ts
Parameters:
notebookId: string - The notebook ID (required)options: AddSourceFromTextOptions
content: string - Text content (required)title: string - Source title (required)Returns: Promise<string | AddSourceResult> - Source ID (string) if not chunked, or AddSourceResult if auto-chunked
Description: Adds text content as a source. Useful for adding notes, research summaries, or any text-based content.
Auto-Chunking:
AddSourceResult with chunk count and source IDs when chunkedUsage:
// Small text (returns string - backward compatible)
const sourceId = await sdk.sources.add.text('notebook-id', {
title: 'Research Notes',
content: 'Key findings from research...',
})
// Large text (auto-chunked - returns AddSourceResult)
const result = await sdk.sources.add.text('notebook-id', {
title: 'Large Document',
content: veryLongText, // > 500k words
})
if (typeof result === 'string') {
console.log(`Source ID: ${result}`)
} else {
console.log(`Uploaded ${result.chunks?.length || 0} chunks`)
console.log(`Source IDs: ${result.allSourceIds?.join(', ')}`)
}
Method: sdk.sources.add.file(notebookId, options)
Parameters:
notebookId: string - The notebook ID (required)options: AddSourceFromFileOptions
content: Buffer | string - File content as Buffer or base64 string (required)fileName: string - File name (required)mimeType?: string - MIME type (optional, auto-detected if not provided)Returns: Promise<string | AddSourceResult> - Source ID (string) if not chunked, or AddSourceResult if auto-chunked
Description: Adds a file (PDF, image, video, etc.) as a source. Supports files as Buffer or base64 string.
Auto-Chunking:
Supported File Types:
Usage:
import fs from 'fs'
// Small file (returns string - backward compatible)
const fileBuffer = fs.readFileSync('document.pdf')
const sourceId = await sdk.sources.add.file('notebook-id', {
content: fileBuffer,
fileName: 'document.pdf',
mimeType: 'application/pdf',
})
// Large file (auto-chunked - returns AddSourceResult)
const largeFileBuffer = fs.readFileSync('large-document.pdf')
const result = await sdk.sources.add.file('notebook-id', {
content: largeFileBuffer, // > 200MB or > 500k words
fileName: 'large-document.pdf',
})
if (typeof result === 'string') {
console.log(`Source ID: ${result}`)
} else {
console.log(`Uploaded ${result.chunks?.length || 0} chunks`)
console.log(`Source IDs: ${result.allSourceIds?.join(', ')}`)
}
Method: sdk.sources.add.youtube(notebookId, options)
Example: source-add-youtube.ts
Parameters:
notebookId: string - The notebook ID (required)options: AddYouTubeSourceOptions
urlOrId: string - YouTube URL or video ID (required)title?: string - Optional custom titleReturns: Promise<string> - Source ID
Description: Adds a YouTube video as a source. Accepts either full YouTube URL or just the video ID.
Usage:
// From YouTube URL
const sourceId = await sdk.sources.add.youtube('notebook-id', {
urlOrId: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ',
})
// From video ID
const sourceId = await sdk.sources.add.youtube('notebook-id', {
urlOrId: 'dQw4w9WgXcQ',
})
⚠️ Experimental: This feature is experimental and may have limitations or breaking changes in future versions.
Method: sdk.sources.add.drive(notebookId, options)
Parameters:
notebookId: string - The notebook ID (required)options: AddGoogleDriveSourceOptions
fileId: string - Google Drive file ID (required)title?: string - Optional custom titlemimeType?: string - MIME type (optional, inferred if not provided)Returns: Promise<string> - Source ID
Description: Adds a Google Drive file as a source. Requires the file ID from Google Drive.
This method is deprecated. Use add.batch() with type: 'gdrive' instead.
Usage:
const sourceId = await sdk.sources.add.drive('notebook-id', {
fileId: '1a2b3c4d5e6f7g8h9i0j',
mimeType: 'application/vnd.google-apps.document',
title: 'My Document',
})
Method: sdk.sources.add.batch(notebookId, options)
Example: source-add-batch.ts
Parameters:
notebookId: string - The notebook ID (required)options: BatchAddSourcesOptions
sources: Array<...> - Array of source inputs (required)waitForProcessing?: boolean - Whether to wait for all sources to be processed (default: false)timeout?: number - Timeout in ms if waitForProcessing is true (default: 300000 = 5 minutes)pollInterval?: number - Poll interval in ms (default: 2000 = 2 seconds)onProgress?: (ready: number, total: number) => void - Progress callbackReturns: Promise<string[]> - Array of source IDs
Description: Adds multiple sources at once. Supports mixed source types (URLs, text, files, YouTube, Google Drive) in a single call.
Source Types:
{ type: 'url', url: string, title?: string } - URL source{ type: 'text', title: string, content: string } - Text source{ type: 'file', content: Buffer | string, fileName: string, mimeType?: string } - File source{ type: 'youtube', urlOrId: string, title?: string } - YouTube source{ type: 'gdrive', fileId: string, title?: string, mimeType?: string } - Google Drive source ⚠️ ExperimentalUsage:
const sourceIds = await sdk.sources.add.batch('notebook-id', {
sources: [
{ type: 'url', url: 'https://example.com', title: 'Example' },
{ type: 'text', title: 'Notes', content: 'Content here...' },
{ type: 'youtube', urlOrId: 'dQw4w9WgXcQ' },
],
waitForProcessing: true, // Optional: wait for all to be ready
timeout: 300000, // 5 minutes
onProgress: (ready, total) => {
console.log(`Progress: ${ready}/${total}`)
},
})
Method: sdk.sources.add.web.searchAndWait(notebookId, options)
Example: source-web-search.ts
Parameters:
notebookId: string - The notebook ID (required)options: SearchWebAndWaitOptions
query: string - Search query (required)sourceType?: SearchSourceType - Source type: WEB (default) or GOOGLE_DRIVEmode?: ResearchMode - Research mode: FAST (default) or DEEP (web only)timeout?: number - Max wait time in ms (default: 60000 = 60 seconds)pollInterval?: number - Poll interval in ms (default: 2000 = 2 seconds)onProgress?: (status) => void - Progress callbackReturns: Promise<WebSearchResult> - Results with sessionId, web sources, and drive sources
Description: RECOMMENDED FOR SIMPLE WORKFLOWS - One call that searches and waits for results automatically. Returns all discovered sources once available (or timeout). Perfect for automated workflows where you don't need to see intermediate steps.
Research Modes:
ResearchMode.FAST - Quick search (~10-30 seconds, default)ResearchMode.DEEP - Comprehensive research (~60-120 seconds, web only)Source Types:
SearchSourceType.WEB - Search web (default)SearchSourceType.GOOGLE_DRIVE - Search Google Drive (FAST mode only) ⚠️ ExperimentalReturn Fields:
sessionId: string - Required for adding sources (use with addDiscovered())web: DiscoveredWebSource[] - Discovered web sourcesdrive: DiscoveredDriveSource[] - Discovered Google Drive sources ⚠️ ExperimentalsessionId with addDiscovered() to add selected sourcesUsage:
import { ResearchMode, SearchSourceType } from 'notebooklm-kit'
// Simple search and wait
const result = await sdk.sources.add.web.searchAndWait('notebook-id', {
query: 'machine learning research papers 2024',
mode: ResearchMode.DEEP, // Comprehensive search
sourceType: SearchSourceType.WEB,
timeout: 120000, // Wait up to 2 minutes
onProgress: (status) => {
console.log(`Found ${status.resultCount} results so far...`)
},
})
console.log(`Found ${result.web.length} web sources`)
console.log(`Session ID: ${result.sessionId}`)
// Add selected sources
const addedIds = await sdk.sources.add.web.addDiscovered('notebook-id', {
sessionId: result.sessionId,
webSources: result.web.slice(0, 5), // Top 5
})
Method: sdk.sources.add.web.search(notebookId, options) → sdk.sources.add.web.getResults(notebookId, sessionId) → sdk.sources.add.web.addDiscovered(notebookId, options)
Example: source-web-search-advanced.ts
Description: MULTI-STEP WORKFLOW - For cases where you want to see results and make decisions at each step. Returns intermediate results so you can validate, filter, or select before adding sources.
Workflow Steps:
search() - Start search, returns sessionId immediatelygetResults(sessionId) - Get discovered sources (can call multiple times to poll)addDiscovered(sessionId, selectedSources) - Add your selected sourcesStep 1: Start Search
Method: sdk.sources.add.web.search(notebookId, options)
Parameters:
notebookId: string - The notebook ID (required)options: SearchWebSourcesOptions
query: string - Search query (required)sourceType?: SearchSourceType - WEB (default) or GOOGLE_DRIVEmode?: ResearchMode - FAST (default) or DEEP (web only)Returns: Promise<string> - Session ID (required for steps 2 and 3)
Step 2: Get Results
Method: sdk.sources.add.web.getResults(notebookId, sessionId?)
Parameters:
notebookId: string - The notebook ID (required)sessionId?: string - Session ID from step 1 (optional - if omitted, returns all results)Returns: Promise<{ web: DiscoveredWebSource[], drive: DiscoveredDriveSource[] }>
Step 3: Add Discovered Sources
Method: sdk.sources.add.web.addDiscovered(notebookId, options)
Parameters:
notebookId: string - The notebook ID (required)options: AddDiscoveredSourcesOptions
sessionId: string - Session ID from step 1 (required)webSources?: DiscoveredWebSource[] - Web sources to adddriveSources?: DiscoveredDriveSource[] - Drive sources to addReturns: Promise<string[]> - Array of added source IDs
Usage:
// Step 1: Start search
const sessionId = await sdk.sources.add.web.search('notebook-id', {
query: 'quantum computing',
mode: ResearchMode.FAST,
})
// Step 2: Poll for results (you control when/how often)
let results
do {
await new Promise(r => setTimeout(r, 2000)) // Wait 2 seconds
results = await sdk.sources.add.web.getResults('notebook-id', sessionId)
console.log(`Found ${results.web.length} sources...`)
} while (results.web.length === 0)
// Step 3: Filter and add selected sources
const relevant = results.web.filter(s => s.url.includes('arxiv.org'))
const addedIds = await sdk.sources.add.web.addDiscovered('notebook-id', {
sessionId,
webSources: relevant,
})
Method: sdk.sources.add.web.getResults(notebookId, sessionId?)
Parameters:
notebookId: string - The notebook ID (required)sessionId?: string - Optional session ID to filter resultsReturns: Promise<{ web: DiscoveredWebSource[], drive: DiscoveredDriveSource[] }>
Description:
Returns discovered sources from search sessions. If sessionId is provided, filters results to that specific session. If omitted, returns all results (may include results from other searches).
Usage:
// Get results for specific session
const results = await sdk.sources.add.web.getResults('notebook-id', sessionId)
// Get all results (no filtering)
const allResults = await sdk.sources.add.web.getResults('notebook-id')
Method: sdk.sources.add.web.addDiscovered(notebookId, options)
Parameters:
notebookId: string - The notebook ID (required)options: AddDiscoveredSourcesOptions
sessionId: string - Session ID from search (required)webSources?: DiscoveredWebSource[] - Web sources to adddriveSources?: DiscoveredDriveSource[] - Drive sources to addReturns: Promise<string[]> - Array of added source IDs
Description:
Adds selected discovered sources from search results. You decide which sources to add (from getResults() or searchAndWait()).
Usage:
// After searchAndWait()
const result = await sdk.sources.add.web.searchAndWait(...)
const addedIds = await sdk.sources.add.web.addDiscovered('notebook-id', {
sessionId: result.sessionId,
webSources: result.web.slice(0, 5), // Top 5
})
// After manual search workflow
const addedIds = await sdk.sources.add.web.addDiscovered('notebook-id', {
sessionId: sessionId,
webSources: filteredSources,
driveSources: selectedDriveSources,
})
Method: sdk.sources.update(notebookId, sourceId, updates)
Parameters:
notebookId: string - The notebook ID (required)sourceId: string - The source ID (required)updates: Partial<Source> - Fields to update
title?: string - New titleReturns: Promise<void>
Description: Updates source metadata. Currently supports updating the title.
Usage:
await sdk.sources.update('notebook-id', 'source-id', {
title: 'Updated Source Title',
})
Method: sdk.sources.delete(notebookId, sourceId)
Example: source-delete.ts
Parameters:
notebookId: string - The notebook ID (required)sourceId: string - The source ID (required)Returns: Promise<void>
Description: Deletes a source from a notebook.
Usage:
await sdk.sources.delete('notebook-id', 'source-id')
Method: sdk.sources.status(notebookId)
Example: source-status.ts
Parameters:
notebookId: string - The notebook ID (required)Returns: Promise<SourceProcessingStatus>
Description: Checks the processing status of all sources in a notebook. Returns information about which sources are still processing and whether all sources are ready.
Return Fields:
allReady: boolean - Whether all sources are readyprocessing: string[] - Array of source IDs still processingUsage:
const status = await sdk.sources.status('notebook-id')
if (status.allReady) {
console.log('All sources are ready!')
} else {
console.log(`Still processing: ${status.processing.length} sources`)
console.log('Processing IDs:', status.processing)
}
Examples: artifact-create.ts | artifact-create-subservices.ts | artifact-list.ts | artifact-get.ts | artifact-download.ts | artifact-video.ts | slide-download-test.ts | artifact-rename.ts | artifact-delete.ts | artifact-share.ts
Method: sdk.artifacts.create(notebookId, type, options) or sdk.artifacts.{type}.create(notebookId, options)
Examples:
create() methodParameters:
notebookId: string - The notebook ID (required)type: ArtifactType - Artifact type (required for main method)options: CreateArtifactOptions
title?: string - Artifact title (optional)instructions?: string - Instructions for generation (optional)sourceIds?: string[] - Source IDs to use (optional - automatically uses all sources if omitted)customization?: object - Type-specific customization options (optional)Returns: Promise<Artifact> (or Promise<VideoOverview> / Promise<AudioOverview> for video/audio)
Description:
Creates a study material artifact (quiz, flashcards, report, mind map, infographic, slide deck, audio, video). Supports customization options for 6 out of 8 artifact types. Returns immediately with artifact metadata - check state field to see if artifact is ready.
Artifact Types:
| Type | Value | Customization | Sub-Service |
|---|---|---|---|
| Quiz | ArtifactType.QUIZ | ✅ Yes | sdk.artifacts.quiz.create() |
| Flashcards | ArtifactType.FLASHCARDS | ✅ Yes | sdk.artifacts.flashcard.create() |
| Report | ArtifactType.REPORT | ❌ No | sdk.artifacts.report.create() |
| Mind Map | ArtifactType.MIND_MAP | ❌ No | sdk.artifacts.mindmap.create() |
| Infographic | ArtifactType.INFOGRAPHIC | ✅ Yes | sdk.artifacts.infographic.create() |
| Slide Deck | ArtifactType.SLIDE_DECK | ✅ Yes | sdk.artifacts.slide.create() |
| Audio | ArtifactType.AUDIO | ✅ Yes | sdk.artifacts.audio.create() |
| Video | ArtifactType.VIDEO | ✅ Yes | sdk.artifacts.video.create() |
Customization Options:
| Option | Values | Description | Default |
|---|---|---|---|
numberOfQuestions | 1, 2, 3 | 1 = Fewer (5-10 questions), 2 = Standard (10-15 questions), 3 = More (15-20+ questions) | 2 |
difficulty | 1, 2, 3 | 1 = Easy (basic recall), 2 = Medium (application), 3 = Hard (analysis/synthesis) | 2 |
language | string | Language code (e.g., 'en', 'hi', 'es'). Use NotebookLMLanguage enum for type safety. Supports 80+ languages. | 'en' |
Example:
customization: {
numberOfQuestions: 2, // Standard (10-15 questions)
difficulty: 2, // Medium difficulty
language: NotebookLMLanguage.ENGLISH,
}
| Option | Values | Description | Default |
|---|---|---|---|
numberOfCards | 1, 2, 3 | 1 = Fewer (10-15 cards), 2 = Standard (15-25 cards), 3 = More (25-40+ cards) | 2 |
difficulty | 1, 2, 3 | 1 = Easy (basic terms), 2 = Medium (concepts), 3 = Hard (complex relationships) | 2 |
language | string | Language code (e.g., 'en', 'hi', 'es'). Use NotebookLMLanguage enum for type safety. Supports 80+ languages. | 'en' |
Example:
customization: {
numberOfCards: 3, // More cards (25-40+)
difficulty: 1, // Easy (basic terms)
language: NotebookLMLanguage.ENGLISH,
}
| Option | Values | Description | Default |
|---|---|---|---|
format | 2, 3 | 2 = Presenter slides (concise, bullet points), 3 = Detailed deck (comprehensive, full content) | 2 |
length | 1, 2 | 1 = Short (5-10 slides), 2 = Default (10-15 slides) | 2 |
language | string | Language code (e.g., 'en', 'hi', 'es'). Use NotebookLMLanguage enum for type safety. Supports 80+ languages. | 'en' |
Example:
customization: {
format: 2, // Presenter slides (concise)
length: 2, // Default (10-15 slides)
language: NotebookLMLanguage.ENGLISH,
}
| Option | Values | Description | Default |
|---|---|---|---|
orientation | 1, 2, 3 | 1 = Landscape (wide format), 2 = Portrait (tall format), 3 = Square (1:1 aspect ratio) | 1 |
levelOfDetail | 1, 2, 3 | 1 = Concise (key points only), 2 = Standard (balanced detail), 3 = Detailed (comprehensive information) | 2 |
language | string | Language code (e.g., 'en', 'hi', 'es'). Use NotebookLMLanguage enum for type safety. Supports 80+ languages. | 'en' |
Example:
customization: {
orientation: 1, // Landscape (wide format)
levelOfDetail: 2, // Standard detail
language: NotebookLMLanguage.ENGLISH,
}
| Option | Values | Description | Default |
|---|---|---|---|
format | 0, 1, 2, 3 | 0 = Deep dive (comprehensive analysis), 1 = Brief (quick summary), 2 = Critique (critical analysis), 3 = Debate (multiple perspectives) | 0 |
length | 1, 2, 3 | 1 = Short (2-5 minutes), 2 = Default (5-10 minutes), 3 = Long (10-15+ minutes) | 2 |
language | string | Language code (e.g., 'en', 'hi', 'es'). Use NotebookLMLanguage enum for type safety. Supports 80+ languages. | 'en' |
Note: If sourceIds is omitted, all sources in the notebook are automatically used.
Example:
customization: {
format: 0, // Deep dive (comprehensive analysis)
length: 2, // Default (5-10 minutes)
language: NotebookLMLanguage.ENGLISH,
}
| Option | Values | Description | Default |
|---|---|---|---|
format | 1, 2 | 1 = Explainer (comprehensive explanation), 2 = Brief (quick overview) | 1 |
visualStyle | 0-10 | Visual style for the video (see table below) | 0 |
focus | string | What should the AI hosts focus on? (optional, e.g., "Key concepts and main findings") | - |
customStyleDescription | string | Custom visual style description (required when visualStyle=1) | - |
language | string | Language code (e.g., 'en', 'hi', 'es'). Use NotebookLMLanguage enum for type safety. Supports 80+ languages. | 'en' |
Visual Style Options:
| Value | Style Name | Description |
|---|---|---|
0 | Auto-select | AI chooses the best style automatically (default) |
1 | Custom | Requires customStyleDescription - describe your desired style |
2 | Classic | Traditional, professional style |
3 | Whiteboard | Hand-drawn whiteboard style |
4 | Kawaii | Cute, colorful style |
5 | Anime | Anime-inspired style |
6 | Watercolour | Watercolor painting style |
7 | Anime (alternative) | Alternative anime style |
8 | Retro print | Vintage print/poster style |
9 | Heritage | Traditional ink-wash/woodcut style |
10 | Paper-craft | Layered paper cutout style |
Note:
1) support only the focus option1) additionally requires customStyleDescriptionsourceIds is omitted, all sources in the notebook are automatically usedExample:
// Auto-select style
customization: {
format: 1, // Explainer
visualStyle: 0, // Auto-select (AI chooses)
focus: 'Key concepts and main findings',
language: NotebookLMLanguage.ENGLISH,
}
// Custom style
customization: {
format: 1, // Explainer
visualStyle: 1, // Custom
customStyleDescription: 'Modern minimalist design with blue and white colors',
focus: 'Key concepts and main findings',
language: NotebookLMLanguage.ENGLISH,
}
// Specific style (e.g., Whiteboard)
customization: {
format: 1, // Explainer
visualStyle: 3, // Whiteboard style
focus: 'Step-by-step explanation of the process',
language: NotebookLMLanguage.ENGLISH,
}
Report and Mind Map artifacts do not support customization options. They only support:
title?: string - Artifact title (optional)instructions?: string - Custom instructions for generation (optional)sourceIds?: string[] - Source IDs to use (optional - automatically uses all sources if omitted)Example:
// Create report
const report = await sdk.artifacts.report.create('notebook-id', {
title: 'Research Report',
instructions: 'Create a comprehensive research report',
sourceIds: ['source-id-1', 'source-id-2'], // Optional
})
// Create mind map
const mindMap = await sdk.artifacts.mindmap.create('notebook-id', {
title: 'Concept Map',
instructions: 'Create a visual mind map of key concepts',
sourceIds: ['source-id-1'], // Optional
})
Source Selection:
sourceIds is optional for all artifact typesReturn Fields:
artifactId: string - Unique artifact ID (required for other operations)type: ArtifactType - Artifact typestate: ArtifactState - Current state: CREATING, READY, or FAILEDtitle: string - Artifact titlesourceIds?: string[] - Source IDs used to create the artifactcreatedAt?: string - Creation timestampupdatedAt?: string - Last update timestampstate field to see if readyget(artifactId) to fetch full artifact data when state === READYAudioOverview / VideoOverview instead of Artifactsdk.artifacts.quiz.create())Usage:
import { ArtifactType } from 'notebooklm-kit'
// Generic create method
const quiz = await sdk.artifacts.create('notebook-id', ArtifactType.QUIZ, {
title: 'Chapter 1 Quiz',
instructions: 'Create questions covering key concepts',
customization: {
numberOfQuestions: 2, // Standard
difficulty: 2, // Medium
language: 'en',
},
})
// Using sub-services (type-safe)
const quiz = await sdk.artifacts.quiz.create('notebook-id', {
title: 'Chapter 1 Quiz',
instructions: 'Create questions covering key concepts',
customization: {
numberOfQuestions: 2,
difficulty: 2,
language: 'en',
},
})
const flashcards = await sdk.artifacts.flashcard.create('notebook-id', {
customization: {
numberOfCards: 3, // More cards
difficulty: 1, // Easy
},
})
const video = await sdk.artifacts.video.create('notebook-id', {
instructions: 'Create a summary video',
customization: {
format: 1,
visualStyle: 0,
language: 'en',
},
})
// Check if ready
if (quiz.state === ArtifactState.READY) {
const fullQuiz = await sdk.artifacts.get(quiz.artifactId, 'notebook-id')
}
Method: sdk.artifacts.list(notebookId, options?)
Example: artifact-list.ts
Parameters:
notebookId: string - The notebook ID (required)options?: { type?: ArtifactType; state?: ArtifactState } - Filtering options (optional)Returns: Promise<Artifact[]>
Description: Lists all artifacts in a notebook. Supports filtering by type and/or state. Returns lightweight artifact metadata for display/selection.
Return Fields:
artifactId: string - Unique artifact IDtype: ArtifactType - Artifact typestate: ArtifactState - Current state (CREATING, READY, FAILED)title: string - Artifact titlesourceIds?: string[] - Source IDs usedcreatedAt?: string - Creation timestampupdatedAt?: string - Last update timestampget() for that)state field to see if artifacts are ready before fetching contentUsage:
import { ArtifactType, ArtifactState } from 'notebooklm-kit'
// List all artifacts
const artifacts = await sdk.artifacts.list('notebook-id')
console.log(`Found ${artifacts.length} artifacts`)
// Filter by type
const quizzes = await sdk.artifacts.list('notebook-id', {
type: ArtifactType.QUIZ,
})
// Filter by state
const ready = await sdk.artifacts.list('notebook-id', {
state: ArtifactState.READY,
})
// Filter by both type and state
const readyQuizzes = await sdk.artifacts.list('notebook-id', {
type: ArtifactType.QUIZ,
state: ArtifactState.READY,
})
// Display artifacts
artifacts.forEach(artifact => {
console.log(`${artifact.title} (${artifact.type}) - ${artifact.state}`)
})
Method: sdk.artifacts.get(artifactId, notebookId?, options?)
Examples: artifact-get.ts | artifact-video.ts (video-specific) | slide-download-test.ts (slide-specific)
Parameters:
artifactId: string - The artifact ID from create() or list() (required)notebookId?: string - Optional notebook ID (helpful for audio/video if get() needs it)options?: { exportToDocs?: boolean; exportToSheets?: boolean } - Export options (for reports only)Returns: Promise<Artifact | QuizData | FlashcardData | AudioArtifact | VideoArtifact | any>
Description:
Retrieves detailed artifact information. Automatically fetches full content when artifact is READY:
download() to get audio file)videoData field (use download() to download video file)download() to download slides as PDF/PNG)download() to get report content, or get() with export options for Google Docs/Sheets)experimental: true flagReturn Types by Artifact Type:
| Type | Returns | Content |
|---|---|---|
| Quiz | QuizData | Questions, options, correct answers, explanations |
| Flashcards | FlashcardData | Flashcards array, CSV, totalCards |
| Audio | AudioArtifact | Audio metadata (use download() for audio file) |
| Video | Artifact | Video URL in videoData field (use download() to download) |
| Slides | Artifact | Slide image URLs in slideUrls array (use download() to download) |
| Report | Artifact | Basic metadata (artifactId, notebookId, state, title) - use download() for content or export options |
| Infographic | InfographicImageData | Image data, dimensions, URL |
| Mind Map | Artifact | Metadata with experimental: true |
Important Notes:
get() returns URLs/links only, not downloaded files. Use download() method to actually download files.get() returns basic metadata only. Use download() to get report content, or use export options below.get() may not include audio data. Use download() to get the audio file.Report Export Options:
exportToDocs?: boolean - Export to Google Docs and return export URLexportToSheets?: boolean - Export to Google Sheets and return export URLdownload() for content)exportToDocs, exportToSheets) can only be used with REPORT artifactsnotebookId is required for audio artifacts (must match artifactId)READY artifacts, returns full data instead of just metadataCREATING or FAILED artifacts, returns metadata onlydownload() to download files)download() for content or export options)get() - use download() to get audio fileUsage:
import { ArtifactType, ArtifactState } from 'notebooklm-kit'
// Get quiz with full data
const quiz = await sdk.artifacts.get('quiz-id', 'notebook-id')
if (quiz.state === ArtifactState.READY) {
console.log(`Quiz: ${quiz.title}`)
console.log(`Questions: ${quiz.questions?.length || 0}`)
quiz.questions?.forEach((q, i) => {
console.log(`Q${i + 1}: ${q.question}`)
})
}
// Get flashcards with full data
const flashcards = await sdk.artifacts.get('flashcard-id', 'notebook-id')
if (flashcards.state === ArtifactState.READY) {
console.log(`Total cards: ${flashcards.totalCards}`)
flashcards.flashcards?.forEach(card => {
console.log(`Q: ${card.question} | A: ${card.answer}`)
})
}
// Get video URL (use download() to download file)
const video = await sdk.artifacts.get('video-id', 'notebook-id')
console.log(`Video URL: ${video.videoData}`)
// To download: await sdk.artifacts.download('video-id', './downloads', 'notebook-id')
// Get slide URLs (use download() to download files)
const slides = await sdk.artifacts.get('slide-id', 'notebook-id')
console.log(`Slide URLs: ${slides.slideUrls?.length || 0} slides`)
slides.slideUrls?.forEach((url, i) => {
console.log(`Slide ${i + 1}: ${url}`)
})
// To download: await sdk.artifacts.download('slide-id', './downloads', 'notebook-id')
// Get report metadata (use download() for content or export options)
const report = await sdk.artifacts.get('report-id', 'notebook-id')
console.log(`Report: ${report.title} (${report.state})`)
// To get content: await sdk.artifacts.download('report-id', './downloads', 'notebook-id')
// Export report to Google Docs
const report = await sdk.artifacts.get('report-id', 'notebook-id', {
exportToDocs: true,
})
console.log(`Docs URL: ${report.exportUrl}`)
// Export report to Google Sheets
const report = await sdk.artifacts.get('report-id', 'notebook-id', {
exportToSheets: true,
})
console.log(`Sheets URL: ${report.exportUrl}`)
// Get audio metadata (use download() to get audio file)
const audioArtifact = await sdk.artifacts.audio.create('notebook-id')
const audio = await sdk.artifacts.get(audioArtifact.audioId, 'notebook-id')
console.log(`Audio: ${audio.title} (${audio.state})`)
// To download: await sdk.artifacts.download(audioArtifact.audioId, './downloads', 'notebook-id')
Method: sdk.artifacts.download(artifactId, folderPath, notebookId?)
Example: artifact-download.ts
Parameters:
artifactId: string - The artifact ID from create() or list() (required)folderPath: string - Output folder path (required)notebookId?: string - Optional notebook ID (helpful for audio/video if download needs it)Returns: Promise<{ filePath: string; data: any }>
Description: Downloads artifact content and saves to disk. Automatically determines file format and saves with appropriate filename.
Supported Artifacts:
| Type | Format | Filename | Content |
|---|---|---|---|
| Quiz | JSON | quiz_{artifactId}_{timestamp}.json | Complete quiz data with questions, options, answers, explanations |
| Flashcards | JSON | flashcard_{artifactId}_{timestamp}.json | Complete flashcard data with array, CSV, totalCards |
| Audio | Audio file | audio_{artifactId}.mp3 | Audio file (binary) |
| Video | MP4 file | <artifact-title>.mp4 | Video downloaded as MP4 using Playwright |
| Slides | PDF file | <artifact-title>.pdf or <artifact-title>/slide_*.png | Slides downloaded as PDF (default) or PNG files using Playwright |
pdf-lib package is recommended (falls back to PNG if not available)Usage:
// Download quiz
const result = await sdk.artifacts.download('quiz-id', './downloads', 'notebook-id')
console.log(`Saved to: ${result.filePath}`)
console.log(`Questions: ${result.data.questions?.length || 0}`)
// Download flashcards
const result = await sdk.artifacts.download('flashcard-id', './downloads', 'notebook-id')
console.log(`Saved to: ${result.filePath}`)
console.log(`Total cards: ${result.data.totalCards}`)
// Download audio (use audioId from create() or list())
const audio = await sdk.artifacts.audio.create('notebook-id')
const result = await sdk.artifacts.download(audio.audioId, './downloads', 'notebook-id')
console.log(`Audio saved to: ${result.filePath}`)
// Download video (use download() method)
const result = await sdk.artifacts.download('video-id', './downloads', 'notebook-id')
console.log(`Video saved to: ${result.filePath}`)
// Download slides (use download() method)
const slidesResult = await sdk.artifacts.download('slide-id', './downloads', 'notebook-id')
console.log(`Slides saved to: ${slidesResult.filePath}`)
// Download report (use download() method)
const reportResult = await sdk.artifacts.download('report-id', './downloads', 'notebook-id')
console.log(`Report saved to: ${reportResult.filePath}`)
Method: sdk.artifacts.download(videoId, outputPath, notebookId)
Example: artifact-video.ts
Parameters:
videoId (string, required): The video artifact IDoutputPath (string, required): Directory path to save the downloaded videonotebookId (string, required): The notebook ID containing the videoReturns: { filePath: string, data: Artifact } - File path and artifact data
Description: Downloads a video artifact as an MP4 file. Uses Playwright for authentication and Node's native http/https for reliable download with real-time progress tracking. The video file is saved with the artifact title as the filename.
Important Notes:
get() to get video URL: get() returns the video URL in videoData field, not a downloaded filedownload() to download: download() method downloads the video file to diskREADY state before downloading (check with get() first)GOOGLE_EMAIL and GOOGLE_PASSWORD environment variables for authentication<artifact-title>.mp4 in the specified output directoryUsage:
// Get video URL (get() returns URL only)
const video = await sdk.artifacts.get('video-id', 'notebook-id')
console.log(`Video URL: ${video.videoData}`)
// Download video (download() saves file to disk)
const result = await sdk.artifacts.download('video-id', './downloads', 'notebook-id')
console.log(`Video saved to: ${result.filePath}`)
Example Workflow:
// 1. Check video status and get URL
const video = await sdk.artifacts.get('video-id', 'notebook-id')
console.log(`Video URL: ${video.videoData}`)
if (video.state === ArtifactState.READY) {
// 2. Download video
const downloaded = await sdk.artifacts.download('video-id', './downloads', 'notebook-id')
console.log(`Downloaded: ${downloaded.filePath}`)
}
Method: sdk.artifacts.download(slideId, outputPath, notebookId, { downloadAs })
Example: slide-download-test.ts
Interactive Example (slide-download-test.ts):
The slide-download-test.ts example provides an interactive script for downloading slide decks:
Environment Variables for slide-download-test.ts:
NOTEBOOK_ID - Optional: Pre-select a notebook ID (skips notebook selection)GOOGLE_EMAIL - Required: Google account email for authenticationGOOGLE_PASSWORD - Required: Google account password for authenticationDOWNLOAD_AS - Optional: 'pdf' or 'png' (defaults to interactive prompt)OUTPUT_DIR - Optional: Output directory path (defaults to ./downloads)Usage:
# Using tsx (recommended)
NOTEBOOK_ID=your-notebook-id GOOGLE_EMAIL=your@email.com GOOGLE_PASSWORD=yourpassword DOWNLOAD_AS=pdf npx tsx examples/slide-download-test.ts
# Or set environment variables separately
export GOOGLE_EMAIL=your@email.com
export GOOGLE_PASSWORD=yourpassword
export DOWNLOAD_AS=pdf
npx tsx examples/slide-download-test.ts
SDK Method Parameters:
slideId (string, required): The slide artifact IDoutputPath (string, required): Directory path to save the downloaded slidesnotebookId (string, required): The notebook ID containing the slidesdownloadAs (string, optional): Download format - 'pdf' (default) or 'png'Returns: { filePath: string, data: Artifact } - File path and artifact data
Description: Downloads a slide deck artifact as PDF (default) or PNG files. Uses Playwright for authentication and handles the download automatically. PDF format saves all slides in a single file, while PNG format saves each slide as a separate image file.
Format Options:
PDF (default):
<artifact-title>.pdfPNG:
<artifact-title>/slide_1.png, slide_2.png, etc.Important Notes:
get() to get slide URLs: get() returns slide image URLs in slideUrls array, not downloaded filesdownload() to download: download() method downloads the slides to diskREADY state before downloading (check with get() first)pdf-lib if available, falls back to PNG if not installedUsage:
// Get slide URLs (get() returns URLs only)
const slides = await sdk.artifacts.get('slide-id', 'notebook-id')
console.log(`Slide URLs: ${slides.slideUrls?.length || 0} slides`)
slides.slideUrls?.forEach((url, i) => {
console.log(`Slide ${i + 1}: ${url}`)
})
// Download as PDF (default) - use download() method
const result = await sdk.artifacts.download('slide-id', './downloads', 'notebook-id')
console.log(`PDF saved to: ${result.filePath}`)
// Download as PNG files - use download() method
const resultPng = await sdk.artifacts.download('slide-id', './downloads', 'notebook-id', {
downloadAs: 'png'
})
console.log(`PNG files saved to: ${resultPng.filePath}`)
Example Workflow:
// 1. Check slide status and get URLs
const slides = await sdk.artifacts.get('slide-id', 'notebook-id')
console.log(`Slide URLs: ${slides.slideUrls?.length || 0} slides`)
if (slides.state === ArtifactState.READY) {
// 2. Download as PDF (default)
const downloaded = await sdk.artifacts.download('slide-id', './downloads', 'notebook-id')
console.log(`Downloaded PDF: ${downloaded.filePath}`)
// Or download as PNG files
const pngSlides = await sdk.artifacts.download('slide-id', './downloads', 'notebook-id', {
downloadAs: 'png'
})
console.log(`Downloaded PNGs: ${pngSlides.filePath}`)
}
Method: sdk.artifacts.rename(artifactId, newTitle)
Example: artifact-rename.ts
Parameters:
artifactId: string - The artifact ID (required)newTitle: string - New title (required)Returns: Promise<Artifact>
Description: Renames an artifact. Updates only the title field. Works for all artifact types.
Return Fields:
Same as get() - returns updated artifact with new title
Usage:
// Rename any artifact
const updated = await sdk.artifacts.rename('artifact-id', 'My Updated Quiz')
console.log(`New title: ${updated.title}`)
// Rename audio artifact (use audioId from create() or list())
const audio = await sdk.artifacts.audio.create('notebook-id')
const renamed = await sdk.artifacts.rename(audio.audioId, 'My Audio Overview')
Method: sdk.artifacts.delete(artifactId, notebookId?)
Example: artifact-delete.ts
Parameters:
artifactId: string - The artifact ID from create() or list() (required)notebookId?: string - Optional notebook ID (helpful if get() fails for audio/video)Returns: Promise<void>
Description: Permanently deletes an artifact. Works for all artifact types. This action cannot be undone. Automatically detects artifact type and uses the correct RPC method.
get() first[[2], artifactId] structure[artifactId] structureget() fails, tries both methods (V5N4be first, then standard delete)Usage:
// Delete any artifact (recommended - automatically detects type)
await sdk.artifacts.delete('artifact-id')
// Delete with notebook ID (helpful if get() fails)
await sdk.artifacts.delete('artifact-id', 'notebook-id')
// Note: Audio and video artifacts have their own artifactId from create() or list()
const audio = await sdk.artifacts.audio.create('notebook-id')
await sdk.artifacts.delete(audio.audioId) // Use audioId, not notebookId
Method: sdk.artifacts.share(notebookId, options)
Example: artifact-share.ts
Parameters:
notebookId: string - The notebook ID (required, artifacts are shared at notebook level)options: ShareArtifactOptions
users?: Array<{email: string, role: 2|3|4}> - Users to share with (optional)notify?: boolean - Notify users (default: true, only used when users are provided)accessType?: 1|2 - Access type: 1=anyone with link, 2=restricted (optional, default: 2)Returns: Promise<ShareArtifactResult>
Description: Shares an artifact (or the notebook containing the artifact) with specific users or makes it publicly accessible via a shareable link. Artifacts are shared at the notebook level, so sharing an artifact shares the entire notebook.
User Roles:
| Role | Value | Description |
|---|---|---|
| Editor | 2 | Can edit notebook content |
| Viewer | 3 | Can view notebook only |
| Remove | 4 | Remove user from shared list |
Access Types:
| Access Type | Value | Description |
|---|---|---|
| Anyone with link | 1 | Public access via share link |
| Restricted | 2 | Only specified users can access |
Return Fields:
shareUrl: string - Share URL (always present)success: boolean - Whether the share operation succeedednotebookId: string - The notebook ID that was sharedaccessType: 1|2 - Access typeisShared: boolean - Whether the notebook is sharedusers?: Array<{email: string, role: 2|3}> - Users with access (if users were shared)APIError if any email is invalidusers array or accessType=1 (anyone with link)notify is only used when users are providedtrue (users are notified when permissions change)false to share silentlyshareUrl is always returned (constructed from notebook ID if not explicitly shared)Usage:
// Share with users (restricted access, notify enabled by default)
const result = await sdk.artifacts.share('notebook-id', {
users: [
{ email: 'user1@example.com', role: 2 }, // editor
{ email: 'user2@example.com', role: 3 }, // viewer
],
notify: true,
accessType: 2, // restricted
})
// Share with users (silent, no notification)
const result = await sdk.artifacts.share('notebook-id', {
users: [
{ email: 'user@example.com', role: 2 },
],
notify: false,
accessType: 2,
})
// Enable link sharing (anyone with link)
const result = await sdk.artifacts.share('notebook-id', {
accessType: 1, // anyone with link
})
console.log(`Share URL: ${result.shareUrl}`)
// Remove user (role: 4)
const result = await sdk.artifacts.share('notebook-id', {
users: [
{ email: 'user@example.com', role: 4 }, // remove
],
accessType: 2,
})
Examples: chat-basic.ts | chat-conversation.ts | generation-set-chat-config.ts
Key Features:
chatStream()) or complete responses (chat())ChatConfig interfaceChoosing Between chat() and chatStream():
chat() when you want the complete response at once (simpler, blocking)
--no-stream flag in the example scriptchatStream() when you want real-time updates (better UX, progressive display)
Method: sdk.generation.chat(notebookId, prompt, options?)
Examples:
Note: The SDK now uses an improved parser based on the Python SDK implementation, providing better error handling, citation extraction, and streaming support. The chat request format has been restructured to match the official API format for better compatibility.
Parameters:
notebookId: string - The notebook ID (required)prompt: string - Chat message/prompt (required)options?: object - Optional chat options:
sourceIds?: string[] - Specific source IDs to query (uses all sources if omitted)conversationHistory?: Array<{ message: string; role: 'user' | 'assistant' }> - Conversation history for follow-up messagesconversationId?: string - Conversation ID for continuing a conversation (auto-generated if not provided)Returns: Promise<ChatResponseData> - Complete AI response with metadata
Type Import:
import type { ChatResponseData } from 'notebooklm-kit';
ChatResponseData Interface:
interface ChatResponseData {
text: string; // Complete response text (thinking headers removed)
rawData?: any; // Raw response data from API
chunks?: StreamChunk[]; // All chunks received during streaming
conversationId?: string; // Conversation ID for continuing the conversation
citations: number[]; // Citation numbers extracted from response
}
Description:
Chat with your notebook content using AI. Returns the complete response after all chunks are received. This is the non-streaming mode - use chatStream() for real-time streaming responses.
Response Format:
text: The complete response text with thinking headers automatically removedrawData: Raw response structure from the API (for advanced use cases)chunks: Array of all stream chunks received (if you need to inspect individual chunks)conversationId: ID for continuing the conversationcitations: Array of citation numbers found in the response (e.g., [1, 2, 3])Features:
ChatResponseData object with text, citations, conversationId, etc.text field contains the complete response with thinking headers automatically removedcitations arraysourceIds is provided, only those sources are used for contextsourceIds is omitted, all sources in the notebook are automatically fetched and usedconversationHistory for follow-up messages to maintain contextchatStream() for real-time streaming responsesUsage:
// Chat with all sources (non-streaming)
const response = await sdk.generation.chat(
'notebook-id',
'What are the main findings from the research?'
)
console.log(response.text) // Complete response text
console.log(`Citations: [${response.citations.join(', ')}]`)
console.log(`Conversation ID: ${response.conversationId}`)
// Chat with specific sources
const response = await sdk.generation.chat(
'notebook-id',
'Summarize the methodology section',
{ sourceIds: ['source-id-1', 'source-id-2'] }
)
console.log(response.text)
// Follow-up message with conversation history
const response1 = await sdk.generation.chat('notebook-id', 'What is machine learning?')
const response2 = await sdk.generation.chat('notebook-id', 'Tell me more', {
conversationId: response1.conversationId,
conversationHistory: [
{ message: 'What is machine learning?', role: 'user' },
{ message: response1.text, role: 'assistant' }
]
})
console.log(response2.text)
Method: sdk.generation.chatStream(notebookId, prompt, options?)
Examples:
Parameters:
notebookId: string - The notebook ID (required)prompt: string - Chat message/prompt (required)options?: object - Optional chat options:
sourceIds?: string[] - Specific source IDs to query (uses all sources if omitted)conversationHistory?: Array<{ message: string; role: 'user' | 'assistant' }> - Conversation history for follow-up messagesconversationId?: string - Conversation ID for continuing a conversation (auto-generated if not provided)onChunk?: (chunk: StreamChunk) => void - Callback function called for each chunkshowThinking?: boolean - Whether to include thinking process in output (default: false)Returns: AsyncGenerator<StreamChunk, void, unknown> - Async generator yielding stream chunks
Type Import:
import type { StreamChunk } from 'notebooklm-kit';
Description: Stream chat responses in real-time. Returns an async generator that yields chunks as they arrive from the API. Each chunk contains the full accumulated text up to that point (snapshot-based, not delta-based), allowing you to display responses as they're generated.
Key Behaviors:
chunk.text contains the complete response accumulated so far, not just the new portion**Header**) is automatically buffered and only the final response is displayedStreamChunk Interface:
interface StreamChunk {
chunkNumber: number; // Chunk number (1-based, increments with each chunk)
byteCount: number; // Byte count for this chunk
text: string; // FULL accumulated text from start to this point (snapshot-based)
thinking: string[]; // Thinking headers extracted from text (e.g., ["Analyzing sources", "Formulating response"])
response: string; // Response text with thinking headers removed
metadata?: [string, string, number]; // [conversationId, messageId, timestamp]
messageIds?: [string, string]; // Message IDs: [conversationId, messageId]
timestamp?: number; // Timestamp from metadata
citations?: number[]; // Citation numbers extracted from text (e.g., [1, 2, 3])
rawData?: any; // Raw parsed data from API
isError?: boolean; // Is this an error chunk?
errorCode?: number; // Error code if isError is true
isThinking?: boolean; // Does this chunk contain thinking content?
isResponse?: boolean; // Does this chunk contain response content?
formatting?: any; // Formatting information
}
Important Notes:
chunk.text contains the complete accumulated response (not incremental)chunk.text.length with previous chunk length[1], [2], etc.chunk.metadata[0] or chunk.messageIds[0]Features:
onChunkchunk.text contains the complete accumulated response from the start, not just the new portionchunk.text.length with previous chunk length and extract the new portion**Header**) is automatically buffered and filtered - only the final response is displayed[1], [2], etc.chunk.metadata or chunk.messageIdsonChunk callback for real-time processing of each chunkchunk.isError and chunk.errorCodeUsage:
Basic Streaming (Snapshot-based):
// Since chunk.text contains the FULL accumulated response, display incrementally
let lastDisplayedLength = 0;
for await (const chunk of sdk.generation.chatStream('notebook-id', 'What is this about?')) {
// chunk.text contains complete response so far - extract only new portion
if (chunk.text.length > lastDisplayedLength) {
const newText = chunk.text.substring(lastDisplayedLength);
process.stdout.write(newText);
lastDisplayedLength = chunk.text.length;
}
}
Simple Streaming (Using response field):
// Use chunk.response which has thinking headers already removed
let lastResponseLength = 0;
for await (const chunk of sdk.generation.chatStream('notebook-id', 'Explain this')) {
if (chunk.response && chunk.response.length > lastResponseLength) {
const newText = chunk.response.substring(lastResponseLength);
process.stdout.write(newText);
lastResponseLength = chunk.response.length;
}
}
Streaming with Callback:
for await (const chunk of sdk.generation.chatStream('notebook-id', 'Explain this', {
onChunk: (chunk) => {
console.log(`Chunk ${chunk.chunkNumber}: ${chunk.byteCount} bytes`);
if (chunk.citations && chunk.citations.length > 0) {
console.log(`Citations: ${chunk.citations.join(', ')}`);
}
}
})) {
// Display incrementally
if (chunk.text.length > lastDisplayedLength) {
const newText = chunk.text.substring(lastDisplayedLength);
process.stdout.write(newText);
lastDisplayedLength = chunk.text.length;
}
}
Streaming with Specific Sources:
let lastDisplayedLength = 0;
for await (const chunk of sdk.generation.chatStream(
'notebook-id',
'Compare these sources',
{ sourceIds: ['source-id-1', 'source-id-2'] }
)) {
if (chunk.text.length > lastDisplayedLength) {
const newText = chunk.text.substring(lastDisplayedLength);
process.stdout.write(newText);
lastDisplayedLength = chunk.text.length;
}
}
Collect Citations and Metadata:
const citations = new Set<number>();
let conversationId: string | undefined;
let messageIds: [string, string] | undefined;
let lastDisplayedLength = 0;
for await (const chunk of sdk.generation.chatStream('notebook-id', 'What are the key findings?')) {
// Track conversation ID and message IDs
if (chunk.metadata) {
conversationId = chunk.metadata[0];
messageIds = chunk.metadata.slice(0, 2) as [string, string];
}
// Collect citations
if (chunk.citations) {
chunk.citations.forEach(citation => citations.add(citation));
}
// Display response incrementally
if (chunk.text.length > lastDisplayedLength) {
const newText = chunk.text.substring(lastDisplayedLength);
process.stdout.write(newText);
lastDisplayedLength = chunk.text.length;
}
}
console.log(`\nConversation ID: ${conversationId}`);
console.log(`Message IDs: ${messageIds?.join(', ')}`);
console.log(`Citations: [${Array.from(citations).sort((a, b) => a - b).join(', ')}]`);
Example Output Format:
The provided sources primarily discuss the field of Artificial Intelligence (AI)
and the transformative research paper "Attention Is All You Need" [1, 2].
The transformer architecture introduced in 2017 revolutionized AI by enabling
parallel processing and becoming the foundation for modern large language models [3, 4].
Note: The streaming client automatically:
Method: sdk.generation.setChatConfig(notebookId, config)
Example: generation-set-chat-config.ts
Parameters:
notebookId: string - The notebook ID (required)config: ChatConfig - Chat configuration object (type-safe):
type: 'default' | 'custom' | 'learning-guide' - Configuration type (required)customText?: string - Custom prompt text (required if type is 'custom')responseLength: 'default' | 'shorter' | 'longer' - Response length (required)Returns: Promise<any> - Configuration result
Type Import:
import type { ChatConfig } from 'notebooklm-kit';
Description: Configure chat behavior before sending messages. Set the chat mode (default, custom prompt, or learning guide) and response length. This configuration affects all subsequent chat messages until changed. This is optional - you can use chat without setting configuration.
Configuration Types:
default: Standard conversational responses - balanced and informativecustom: Responses following a custom instruction/persona (requires customText)learning-guide: Educational responses optimized for learning and understandingResponse Lengths:
default: Standard length responses - balanced detailshorter: Concise, brief responses - quick summarieslonger: Detailed, comprehensive responses - in-depth explanationsUsage:
// Set default configuration
await sdk.generation.setChatConfig('notebook-id', {
type: 'default',
responseLength: 'default'
})
// Set custom configuration with custom prompt
await sdk.generation.setChatConfig('notebook-id', {
type: 'custom',
customText: 'respond as a PhD student explaining complex concepts simply',
responseLength: 'longer'
})
// Set learning guide configuration
await sdk.generation.setChatConfig('notebook-id', {
type: 'learning-guide',
responseLength: 'shorter'
})
// Example: Configure for academic writing style
await sdk.generation.setChatConfig('notebook-id', {
type: 'custom',
customText: 'respond in an academic writing style with citations',
responseLength: 'longer'
})
Note: Configuration persists for the notebook until changed. You can update it anytime by calling setChatConfig() again with new values.
Examples: note-list.ts | note-create.ts | note-update.ts | note-delete.ts
Method: sdk.notes.list(notebookId)
Example: note-list.ts
Parameters:
notebookId: string - The notebook ID (required)Returns: Promise<Note[]>
Description: Lists all notes in a notebook. Returns an array of notes with their titles, content, and metadata.
Return Fields:
noteId: string - Unique note ID (required for update/delete operations)title: string - Note titlecontent: string - Note contenttags?: string[] - Note tags (if any)createdAt?: string - Creation timestamp (if available)updatedAt?: string - Last modified timestamp (if available)Usage:
const notes = await sdk.notes.list('notebook-id')
console.log(`Found ${notes.length} notes`)
notes.forEach((note, index) => {
console.log(`${index + 1}. ${note.title}`)
console.log(` ID: ${note.noteId}`)
if (note.tags && note.tags.length > 0) {
console.log(` Tags: ${note.tags.join(', ')}`)
}
})
Method: sdk.notes.create(notebookId, options)
Example: note-create.ts
Parameters:
notebookId: string - The notebook ID (required)options: CreateNoteOptions
title: string - Note title (required)content?: string - Note content (optional, defaults to empty string)tags?: string[] - Note tags (optional)noteType?: NoteType[] - Note type (optional, defaults to [NoteType.REGULAR])Returns: Promise<Note>
Description: Creates a new note in the notebook. Notes are useful for adding your own annotations, reminders, or summaries alongside the notebook content.
Return Fields:
noteId: string - Unique note ID (use this for update/delete operations)title: string - Note title (as provided)content: string - Note content (may be empty initially)update()REGULAR (1) - use GENERATED (2) for AI-generated notesUsage:
// Create a simple note
const note = await sdk.notes.create('notebook-id', {
title: 'Meeting Notes',
content: 'Key points from today\'s meeting...',
})
console.log(`Created note: ${note.noteId}`)
// Create a note with tags
const note = await sdk.notes.create('notebook-id', {
title: 'Research Findings',
content: 'Important findings...',
tags: ['research', 'findings'],
})
// Create a note with just a title (add content later)
const note = await sdk.notes.create('notebook-id', {
title: 'Quick Reminder',
})
Method: sdk.notes.update(notebookId, noteId, options)
Example: note-update.ts
Parameters:
notebookId: string - The notebook ID (required)noteId: string - The note ID (required)options: UpdateNoteOptions
title?: string - New title (optional, defaults to empty string)content?: string - New content (optional, defaults to empty string)tags?: string[] - New tags (optional, defaults to empty array)Returns: Promise<Note>
Description: Updates a note's title, content, and tags. All fields can be updated together or individually. If a field is not provided, it defaults to an empty value.
title and content are always sent (defaults to empty string if not provided)Usage:
// Update title and content
const updated = await sdk.notes.update('notebook-id', 'note-id', {
title: 'Updated Meeting Notes',
content: 'Updated content with new information...',
})
// Update all fields including tags
const updated = await sdk.notes.update('notebook-id', 'note-id', {
title: 'Final Meeting Notes',
content: 'Final content...',
tags: ['meeting', 'final'],
})
// Update both title and content
const updated = await sdk.notes.update('notebook-id', 'note-id', {
title: 'Final Meeting Notes',
content: 'Final content...',
tags: ['meeting', 'final'],
})
Method: sdk.notes.delete(notebookId, noteIds)
Example: note-delete.ts
Parameters:
notebookId: string - The notebook ID (required)noteIds: string | string[] - Single note ID or array of note IDs (required)Returns: Promise<void>
Description: Deletes one or more notes from the notebook. This action cannot be undone.
Usage:
// Delete single note
await sdk.notes.delete('notebook-id', 'note-id')
// Delete multiple notes
await sdk.notes.delete('notebook-id', [
'note-id-1',
'note-id-2',
'note-id-3',
])
NotebookLM supports 80+ languages for artifacts, chat responses, and all notebook operations.
Each notebook has a default output language that is used for:
If you don't specify a language when creating an artifact, it will use the notebook's default language.
// Get the current default language for a notebook
const language = await sdk.notebookLanguage.get('notebook-id');
console.log(`Default language: ${language}`); // 'en', 'de', 'ja', etc.
import { NotebookLMLanguage } from 'notebooklm-kit';
// Set the notebook's default language to German
await sdk.notebookLanguage.set('notebook-id', NotebookLMLanguage.GERMAN);
// Set to Japanese
await sdk.notebookLanguage.set('notebook-id', NotebookLMLanguage.JAPANESE);
// Set using language code directly
await sdk.notebookLanguage.set('notebook-id', 'de');
// Get language info with metadata
const info = await sdk.notebookLanguage.getInfo('notebook-id');
console.log(`Language: ${info.language}`); // 'de'
console.log(`Name: ${info.name}`); // 'German'
console.log(`Native Name: ${info.nativeName}`); // 'Deutsch'
Use the NotebookLMLanguage enum for type safety:
import { NotebookLMLanguage } from 'notebooklm-kit'
NotebookLMLanguage.ENGLISH // 'en'
NotebookLMLanguage.HINDI // 'hi'
NotebookLMLanguage.SPANISH // 'es'
NotebookLMLanguage.FRENCH // 'fr'
NotebookLMLanguage.GERMAN // 'de'
NotebookLMLanguage.JAPANESE // 'ja'
NotebookLMLanguage.CHINESE_SIMPLIFIED // 'zh'
NotebookLMLanguage.ARABIC // 'ar'
NotebookLMLanguage.TURKISH // 'tr'
// ... and 70+ more languages
Artifacts that support language customization:
customization.languagecustomization.languagecustomization.languagecustomization.languageArtifacts that don't support language customization:
// Set notebook default language once
await sdk.notebookLanguage.set('notebook-id', NotebookLMLanguage.GERMAN);
// All artifacts will use German by default
const audio = await sdk.artifacts.create('notebook-id', ArtifactType.AUDIO, {
// No language specified - uses notebook default (German)
});
const video = await sdk.artifacts.create('notebook-id', ArtifactType.VIDEO, {
// No language specified - uses notebook default (German)
});
// Set notebook default to German
await sdk.notebookLanguage.set('notebook-id', NotebookLMLanguage.GERMAN);
// Most artifacts use German
const audio = await sdk.artifacts.create('notebook-id', ArtifactType.AUDIO, {
// Uses German (notebook default)
});
// But this one uses Japanese
const video = await sdk.artifacts.create('notebook-id', ArtifactType.VIDEO, {
customization: {
language: NotebookLMLanguage.JAPANESE, // Overrides notebook default
},
});
// Always specify language explicitly
const audio = await sdk.artifacts.create('notebook-id', ArtifactType.AUDIO, {
customization: {
language: NotebookLMLanguage.SPANISH,
},
});
const video = await sdk.artifacts.create('notebook-id', ArtifactType.VIDEO, {
customization: {
language: NotebookLMLanguage.FRENCH,
},
});
import { NotebookLMLanguage } from 'notebooklm-kit';
// Create artifacts in different languages
const germanAudio = await sdk.artifacts.create('notebook-id', ArtifactType.AUDIO, {
customization: {
language: NotebookLMLanguage.GERMAN, // 'de'
},
});
const japaneseVideo = await sdk.artifacts.create('notebook-id', ArtifactType.VIDEO, {
customization: {
language: NotebookLMLanguage.JAPANESE, // 'ja'
},
});
const hindiSlides = await sdk.artifacts.create('notebook-id', ArtifactType.SLIDE_DECK, {
customization: {
language: NotebookLMLanguage.HINDI, // 'hi'
},
});
const spanishInfographic = await sdk.artifacts.create('notebook-id', ArtifactType.INFOGRAPHIC, {
customization: {
language: NotebookLMLanguage.SPANISH, // 'es'
},
});
const sdk = new NotebookLMClient({
authToken: process.env.NOTEBOOKLM_AUTH_TOKEN!,
cookies: process.env.NOTEBOOKLM_COOKIES!,
// Enable debug logging
debug: true,
// Auto-refresh configuration
autoRefresh: {
enabled: true,
interval: 5 * 60 * 1000, // 5 minutes
},
// Retry configuration
maxRetries: 3,
// Custom headers
headers: {
'User-Agent': 'My Custom Agent',
},
})
// Manually refresh
await sdk.refreshCredentials()
// Get RPC client
const rpcClient = sdk.getRPCClient()
import {
NotebookLMError,
NotebookLMAuthError,
RateLimitError,
APIError,
} from 'notebooklm-kit'
try {
const quiz = await sdk.artifacts.create('notebook-id', ArtifactType.QUIZ, {
instructions: 'Create a quiz',
customization: { numberOfQuestions: 2, difficulty: 2, language: 'en' },
})
} catch (error) {
if (error instanceof NotebookLMAuthError) {
console.error('Authentication failed - refresh your cookies')
} else if (error instanceof RateLimitError) {
console.error('Rate limit exceeded:', error.message)
} else if (error instanceof APIError) {
console.error('API error:', error.message)
} else if (error instanceof NotebookLMError) {
console.error('NotebookLM error:', error.message)
}
}
Always dispose of the SDK when done to prevent memory leaks and stop background processes:
import { NotebookLMClient } from 'notebooklm-kit';
async function main() {
const sdk = new NotebookLMClient();
try {
await sdk.connect();
// ... use SDK ...
} finally {
await sdk.dispose(); // Always cleanup
}
}
Graceful shutdown for long-running applications:
// Handle process termination gracefully
process.on('SIGINT', async () => {
console.log('Shutting down...');
await sdk.dispose();
process.exit(0);
});
process.on('SIGTERM', async () => {
console.log('Terminating...');
await sdk.dispose();
process.exit(0);
});
Use specific error types for better error handling:
import {
NotebookLMError,
NotebookLMAuthError,
RateLimitError,
APIError,
} from 'notebooklm-kit';
try {
const notebook = await sdk.notebooks.create({ title: 'My Notebook' });
} catch (error) {
if (error instanceof NotebookLMAuthError) {
// Authentication failed - credentials expired or invalid
console.error('Please refresh your credentials');
// Optionally: Force re-authentication
// Set FORCE_REAUTH=true in .env or delete credentials.json
} else if (error instanceof RateLimitError) {
// Rate limit exceeded - wait before retrying
console.error('Rate limit exceeded. Please wait before retrying.');
// Implement exponential backoff
} else if (error instanceof APIError) {
// API error - check error code and message
if (error.code === 143) {
console.error('Resource not found');
} else if (error.isRetryable()) {
// Retry transient errors
console.error('Transient error, will retry:', error.message);
} else {
console.error('API error:', error.message);
}
} else if (error instanceof NotebookLMError) {
// Generic NotebookLM error
console.error('NotebookLM error:', error.message);
} else {
// Unexpected error
console.error('Unexpected error:', error);
}
}
Retry logic for transient errors:
async function createNotebookWithRetry(title: string, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await sdk.notebooks.create({ title });
} catch (error) {
if (error instanceof APIError && error.isRetryable() && attempt < maxRetries - 1) {
const delay = Math.pow(2, attempt) * 1000; // Exponential backoff
console.log(`Retry ${attempt + 1}/${maxRetries} after ${delay}ms...`);
await new Promise(resolve => setTimeout(resolve, delay));
continue;
}
throw error; // Re-throw if not retryable or max retries reached
}
}
}
Use saved credentials for faster startup (credentials are automatically saved after first auto-login):
// First run: Auto-login saves credentials to credentials.json
const sdk = new NotebookLMClient({
auth: {
email: process.env.GOOGLE_EMAIL!,
password: process.env.GOOGLE_PASSWORD!,
},
});
await sdk.connect(); // Saves credentials for future use
// Subsequent runs: Credentials loaded automatically from credentials.json
// No need to provide email/password again
const sdk2 = new NotebookLMClient();
await sdk2.connect(); // Uses saved credentials
Force re-authentication when needed:
// Option 1: Set environment variable
// FORCE_REAUTH=true
// Option 2: Delete credentials.json file
// rm credentials.json
// Option 3: Explicitly provide new credentials
const sdk = new NotebookLMClient({
auth: {
email: process.env.GOOGLE_EMAIL!,
password: process.env.GOOGLE_PASSWORD!,
},
});
Check quota before operations to avoid rate limit errors:
// Check remaining quota
const remainingChats = sdk.getRemaining('chats');
if (remainingChats <= 0) {
console.warn('No chat quota remaining. Please wait or upgrade plan.');
return;
}
// Use chat
const response = await sdk.generation.chat(notebookId, 'Your question');
Monitor usage:
const usage = sdk.getUsage();
console.log(`Notebooks: ${usage.notebooks.used}/${usage.notebooks.limit}`);
console.log(`Chats: ${usage.chats.used}/${usage.chats.limit}`);
console.log(`Sources: ${usage.sources.used}/${usage.sources.limit}`);
Wait for sources to process before creating artifacts:
// Add source
const sourceId = await sdk.sources.add.url(notebookId, {
url: 'https://example.com/article',
});
// Wait for processing to complete
let status = await sdk.sources.status(notebookId);
while (status.sources.find(s => s.sourceId === sourceId)?.state !== 'READY') {
await new Promise(resolve => setTimeout(resolve, 2000)); // Wait 2 seconds
status = await sdk.sources.status(notebookId);
}
// Now create artifact (source is ready)
const quiz = await sdk.artifacts.create(notebookId, ArtifactType.QUIZ);
Check artifact state before downloading or using:
// Create artifact
const artifact = await sdk.artifacts.create(notebookId, ArtifactType.QUIZ);
// Wait for artifact to be ready
let currentArtifact = artifact;
while (currentArtifact.state !== ArtifactState.READY) {
if (currentArtifact.state === ArtifactState.FAILED) {
throw new Error('Artifact generation failed');
}
await new Promise(resolve => setTimeout(resolve, 2000)); // Wait 2 seconds
currentArtifact = await sdk.artifacts.get(artifact.artifactId, notebookId);
}
// Now download or use the artifact
const quizData = await sdk.artifacts.download(artifact.artifactId, './downloads', notebookId);
Use async/await consistently for better error handling:
// ✅ Good: Proper async/await
async function processNotebook(notebookId: string) {
try {
const sources = await sdk.sources.list(notebookId);
const artifacts = await sdk.artifacts.list(notebookId);
return { sources, artifacts };
} catch (error) {
console.error('Failed to process notebook:', error);
throw error;
}
}
// ❌ Avoid: Mixing promises and async/await
function processNotebookBad(notebookId: string) {
sdk.sources.list(notebookId).then(sources => {
// Error handling is harder here
});
}
Connect once, reuse the SDK instance:
// ✅ Good: Connect once, reuse
const sdk = new NotebookLMClient();
await sdk.connect();
// Use SDK multiple times
const notebooks = await sdk.notebooks.list();
const sources = await sdk.sources.list(notebookId);
const artifacts = await sdk.artifacts.list(notebookId);
await sdk.dispose(); // Cleanup when done
// ❌ Avoid: Connecting multiple times
// Don't call connect() multiple times - reuse the same instance
Enable debug mode for troubleshooting:
// In development
const sdk = new NotebookLMClient({
debug: process.env.NODE_ENV === 'development',
});
// Or via environment variable
// NOTEBOOKLM_DEBUG=true
// Debug mode shows:
// - RPC call details
// - Authentication flow
// - Auto-refresh operations
// - Error details
// - Response parsing
Use batch operations when adding multiple sources:
// ✅ Good: Batch add sources
await sdk.sources.add.batch(notebookId, {
sources: [
{ type: 'url', url: 'https://example.com/1' },
{ type: 'url', url: 'https://example.com/2' },
{ type: 'url', url: 'https://example.com/3' },
],
});
// ❌ Avoid: Adding sources one by one
// This is slower and uses more API calls
Use streaming for long responses to show progress:
// Stream chat response for better UX
const stream = await sdk.generation.chatStream(notebookId, 'Long question...');
for await (const chunk of stream) {
if (chunk.type === 'content') {
process.stdout.write(chunk.content); // Show progress
} else if (chunk.type === 'citations') {
console.log('\nCitations:', chunk.citations);
}
}
Use environment variables for configuration:
// ✅ Good: Use .env file
// .env
NOTEBOOKLM_AUTH_TOKEN=...
NOTEBOOKLM_COOKIES=...
NOTEBOOKLM_DEBUG=true
// ❌ Avoid: Hardcoding credentials
const sdk = new NotebookLMClient({
authToken: 'hardcoded-token', // Don't do this!
});
Complete example with best practices:
import { NotebookLMClient, ArtifactType, ArtifactState } from 'notebooklm-kit';
import dotenv from 'dotenv';
dotenv.config();
async function main() {
const sdk = new NotebookLMClient({
debug: process.env.NOTEBOOKLM_DEBUG === 'true',
});
try {
await sdk.connect();
// Check quota before operations
const remaining = sdk.getRemaining('chats');
if (remaining <= 0) {
console.warn('No chat quota remaining');
return;
}
// Create notebook
const notebook = await sdk.notebooks.create({
title: 'Research Project',
emoji: '📚',
});
// Add source and wait for processing
const sourceId = await sdk.sources.add.url(notebook.projectId, {
url: 'https://example.com/article',
});
// Wait for source to be ready
let status = await sdk.sources.status(notebook.projectId);
while (status.sources.find(s => s.sourceId === sourceId)?.state !== 'READY') {
await new Promise(resolve => setTimeout(resolve, 2000));
status = await sdk.sources.status(notebook.projectId);
}
// Create artifact and wait for completion
const artifact = await sdk.artifacts.create(
notebook.projectId,
ArtifactType.QUIZ,
{
customization: {
numberOfQuestions: 10,
difficulty: 2,
},
}
);
// Wait for artifact to be ready
let currentArtifact = artifact;
while (currentArtifact.state !== ArtifactState.READY) {
if (currentArtifact.state === ArtifactState.FAILED) {
throw new Error('Artifact generation failed');
}
await new Promise(resolve => setTimeout(resolve, 2000));
currentArtifact = await sdk.artifacts.get(
artifact.artifactId,
notebook.projectId
);
}
// Download artifact
const quizData = await sdk.artifacts.download(
artifact.artifactId,
'./downloads',
notebook.projectId
);
console.log('Success!', quizData);
} catch (error) {
console.error('Error:', error);
process.exit(1);
} finally {
await sdk.dispose(); // Always cleanup
}
}
// Graceful shutdown
process.on('SIGINT', async () => {
console.log('\nShutting down...');
process.exit(0);
});
main();
FAQs
TypeScript SDK for NotebookLM API
We found that notebooklm-kit demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.