File: README.md
notebooklm-py
A Comprehensive Google Gemini Notebook Skill & Unofficial Python API. Full programmatic access to NotebookLM's features—including capabilities the web UI doesn't expose—via Python, CLI, and AI agents like Claude Code, Codex, and OpenClaw.
Note (July 2026): Google rebranded NotebookLM to Gemini Notebook. It remains the same standalone product (now also reachable inside the Gemini app), existing links redirect automatically, and this library drives the same underlying service and works unchanged. The package keeps the
notebooklm-pyname.
Source & Development: https://github.com/teng-lin/notebooklm-py
⚠️ Unofficial Library - Use at Your Own Risk
This library uses undocumented Google APIs that can change without notice.
- Not affiliated with Google - This is a community project
- APIs may break - Google can change internal endpoints anytime
- Rate limits apply - Heavy usage may be throttled
Best for prototypes, research, and personal projects. See Troubleshooting for debugging tips.
What You Can Build
🤖 AI Agent Tools - Integrate NotebookLM into Claude Code, Codex, and other LLM agents. Ships with a root NotebookLM skill for GitHub and npx skills add discovery, local notebooklm skill install support for Claude Code and .agents skill directories, and repo-level Codex guidance in AGENTS.md.
📚 Research Automation - Bulk-import sources (URLs, PDFs, YouTube, Google Drive), run web/Drive research queries with auto-import, and extract insights programmatically. Build repeatable research pipelines.
🎙️ Content Generation - Generate Audio Overviews (podcasts), videos, slide decks, quizzes, flashcards, infographics, data tables, mind maps, and study guides. Full control over formats, styles, and output.
📥 Downloads & Export - Download all generated artifacts locally (MP3, MP4, PDF, PNG, CSV, JSON, Markdown). Export to Google Docs/Sheets. Features the web UI doesn't offer: batch downloads, quiz/flashcard export in multiple formats, mind map JSON extraction.
Use Cases & Recipes
NotebookLM is a grounded engine: Gemini does the heavy reading and answers from your sources with citations. The winning pattern is to let it do the expensive analysis while your agent (Claude Code, Codex, …) orchestrates and handles the final mile — using NotebookLM as a zero-token synthesis + memory layer an agent drives in a loop, and pulling structured artifacts out in bulk and in richer, scriptable formats. Recipes people build on top of this library, grouped by what they use NotebookLM as:
Spend fewer tokens — let NotebookLM do the expensive thinking:
- 🪙 Zero-token research offload — Throw 30 documents into a notebook, let Gemini do the heavy analysis, and have your agent spend tokens only on the final polish. The agent just orchestrates (
create→source add→ask); the reasoning happens server-side. In the wild: a four-workflow guide to stop Claude Code burning tokens on NotebookLM. - 🧠 Knowledge distillation → a permanent skill — Run Deep Research (
source add-research "your topic" --mode deep) or load a doc corpus, let NotebookLM's Gemini condense it, and bake the result into aSKILL.mdyour agent loads at startup — build once, reuse with zero runtime tokens or network calls, git-versioned and immune to UI drift. A packaged domain expert without hand-curating sources. (Dumping raw docs into a skill flattens the hierarchy; NotebookLM condensing first is what makes it work.) - ✅ Self-validating skills — Have NotebookLM generate the eval set — a quiz straight from your sources — to grade an agent skill against ground truth instead of test questions you'd bias yourself. Build the skill, run it against the NotebookLM-authored evals, iterate to a pass. In the wild: a skill that scored 4/10 on the first pass and 10/10 after one iteration, graded by a NotebookLM-generated quiz.
Give your agent memory — persistent, grounded recall:
- 💾 Persistent cross-session memory — Keep a "Master Brain" notebook; a wrap-up step appends each session's decisions and fixes as notes (
note create/ask --save-as-note), and a line in yourCLAUDE.mdqueries it (ask) at the start of the next session. Storage and recall live on Google's infrastructure. - 🧩 Grounded memory for coding agents — Expose a notebook of your internal docs/RFCs/architecture over the MCP server (or plain
ask) so an agent answers from your code with citations rather than plausible-sounding guesses — a zero-infra alternative to standing up your own vector DB and embedding pipeline. In the wild: turning a notebook into the source-grounded "project brain" a coding agent consults before it writes code. - 🪞 Query your own notes / journal — Load years of daily notes, meeting logs, or a journal and
askfor cited answers across your own history — surfacing long-term patterns a keyword search can't (e.g. a weekly summary synthesized from 282 daily notes, every claim linked back to the entry it came from). In the wild: chatting with a year of daily notes as a cited knowledge base.
Turn your sources into answers & artifacts — cited responses, generated media, and exports:
- 📞 Grounded knowledge base / troubleshooting oracle (RAG) — Load product docs, FAQs, RFCs, and past tickets, then
ask --jsonfor source-grounded, cited answers for support, on-call, or internal Q&A. Or have an agent point it at an entire fast-moving tool's docs — more than the agent can hold in context — as a troubleshooting oracle it queries the moment it hits an error. In the wild: OpenClaw drove the library to scrape all 524 pages ofdocs.openclaw.ai, dedupe the duplicate translations, and audit it down to 269 clean sources (missing/extra/duplicate = 0). - 🔁 Multi-format content repurposing — One source set, every format:
generate audio(podcast),generate video,generate slide-deck, plus agenerate reportblog draft,generate quiz, andgenerate flashcards— fan a single notebook out across channels. - 📤 Bulk, scriptable exports — Pull mind maps as JSON, flashcards/quizzes as JSON/Markdown/HTML, data tables as CSV, and reports as Markdown — in bulk, to local files, straight into Anki, your mind-mapping tool, or a repo (
download <type>/download <type> --all). The programmatic "get data out" half of the library, not just "put sources in." - 🕸️ Obsidian / knowledge-graph sync — Run the CLI from your vault root so downloaded artifacts (reports, mind-map JSON, transcripts) land as files in your knowledge graph; community skills built on this library even resolve NotebookLM's citation markers into Obsidian
[[wikilinks]]. Pair with a podcast overview for an audio digest of your notes. In the wild: "Claude Code + NotebookLM + Obsidian = GOD MODE".
Run it unattended, at scale, or on the go — scheduled, headless, and remote:
- 🚨 Incident runbook generator — On an alert, spin up a notebook of the relevant docs, ask targeted diagnostic questions, and generate a briefing-doc report (
generate report --format briefing-doc --wait, thendownload report) as an automated runbook. - 📚 Curriculum / study-set builder — Scrape a syllabus or developer roadmap, create one notebook per topic (with deliberate pacing to dodge rate limits), and bulk-generate podcasts, quizzes, and flashcards for each.
- 📰 Scheduled audio briefings — Pair
auth refresh --quiet(cron/launchd/systemd) withgenerate audioto publish a fresh personalized briefing to a podcast feed on a schedule. - 📱 NotebookLM from your phone, agent-driven — Self-host the remote MCP connector behind a Cloudflare/Tailscale tunnel and add it as a custom connector on the web (claude.ai Connectors, or ChatGPT with Developer Mode). Then drive the full toolset — deep research, source ingestion, studio generation, cited Q&A — from the claude.ai mobile app on the go (ChatGPT's MCP connectors are web-only), chained with your other MCP tools, instead of app-hopping.
These combine ordinary library primitives — see the CLI Reference and Python API. The agent-side glue (skills, scheduling, vault layout) lives in your own setup, not this package. Per-notebook source counts depend on your Google account tier — split across notebooks if you hit a cap.
New here? Start with a walkthrough: Claude Code + NotebookLM = CHEAT CODE (video) · 5 demos + 50 use cases, with prompts.
Ways to Use
| Method | Best For |
|---|---|
| Python API | Application integration, async workflows, custom pipelines |
| CLI | Shell scripts, quick tasks, CI/CD automation |
| MCP Server | Claude Desktop/Code, Codex, etc. — locally via stdio, or as a self-hosted remote connector (behind a Cloudflare/Tailscale tunnel) reachable from claude.ai and ChatGPT, mobile included. |
| REST Server | Local automation over guarded HTTP routes without spawning a CLI process per call |
| Agent Integration | Claude Code, Codex, LLM agents, natural language automation |
Features
Complete NotebookLM Coverage
| Category | Capabilities |
|---|---|
| Notebooks | Create, list, rename, delete |
| Sources | URLs, YouTube, files (PDF, text, Markdown, Word, EPUB, audio, video, images), Google Drive, pasted text; refresh, get guide/fulltext |
| Chat | Questions, conversation history, custom personas, suggested starter prompts |
| Notes | Create, list, rename, delete, save chat answers, save conversation history |
| Source Labels | AI-generated or manual topic labels; add/remove source membership; filter sources by label |
| Research | Web and Drive research agents (fast/deep modes) with auto-import |
| Sharing | Public/private links, user permissions (viewer/editor), view level control |
Content Generation (All Artifact Types)
| Type | Options | Download Format |
|---|---|---|
| Audio Overview | 4 formats (deep-dive, brief, critique, debate), 3 lengths, 50+ languages | MP3 |
| Video Overview | 4 formats (explainer, brief, cinematic, short), 8 visual styles (+ auto/custom), plus a dedicated cinematic-video CLI alias |
MP4 |
| Slide Deck | Detailed or presenter format, adjustable length; individual slide revision | PDF, PPTX |
| Infographic | 3 orientations, 3 detail levels | PNG |
| Quiz | Configurable quantity and difficulty | JSON, Markdown, HTML |
| Flashcards | Configurable quantity and difficulty | JSON, Markdown, HTML |
| Report | Briefing doc, study guide, blog post, or custom prompt | Markdown |
| Data Table | Custom structure via natural language | CSV |
| Mind Map | Hierarchical node tree — two kinds: note-backed JSON or the newer interactive studio map (--kind / MindMapKind) |
JSON |
Beyond the Web UI
Programmatic, batch, and local-file capabilities the API/CLI make easy — several in richer formats, or at a scale, than clicking through the web app:
- Batch downloads - Download all artifacts of a type at once
- Quiz/Flashcard export - Get structured JSON, Markdown, or HTML files
- Mind map data extraction - Export hierarchical JSON for visualization tools
- Data table CSV export - Download structured tables as spreadsheets
- Slide deck as PPTX or PDF - Download editable PowerPoint or PDF files
- Slide revision - Modify individual slides with natural-language prompts
- Report template customization - Append extra instructions to built-in format templates
- Save chat history to notes - Persist a whole Q&A conversation (not just a single answer) as a notebook note
- Source fulltext access - Retrieve the indexed text content of any source
- Programmatic sharing - Manage permissions without the UI
Installation
The full install guide — six personas (agent, end-user, library, headless, contributor, power-user), optional extras matrix, platform notes — lives in docs/installation.md.
Quickest start (CLI users and AI agents) — install the CLI with uv tool (recommended) or pipx:
uv tool install "notebooklm-py[browser]" # or: pipx install "notebooklm-py[browser]"
notebooklm login # first run auto-downloads Chromium (~170 MB), then Google sign-in
notebooklm auth check --test --json # verify: expect "status": "ok"Why uv tool / pipx? They install the CLI into its own isolated environment and put notebooklm on your PATH — no dependency clashes with other tools, a one-line upgrade (uv tool upgrade notebooklm-py) or uninstall, and, crucially, they work on modern macOS (Homebrew Python) and Debian/Ubuntu where a system-wide pip install is blocked with error: externally-managed-environment (PEP 668). No uv yet? curl -LsSf https://astral.sh/uv/install.sh | sh (or brew install uv / winget install astral-sh.uv).
Prefer plain pip? It works the same inside a virtualenv (and directly on Windows, where Python isn't externally-managed):
python3 -m venv .venv && source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install "notebooklm-py[browser]"As a library (embedded in your app — no Playwright, no Chromium):
uv add notebooklm-py # or, inside a virtualenv: pip install notebooklm-pyIf playwright install chromium fails on Linux with TypeError: onExit is not a function, see the Linux workaround. Contributors: see CONTRIBUTING.md.
Authentication & Access
Flexible auth for local dev, headless servers, and multi-tenant setups:
- Three ways to get cookies - Interactive Playwright login (default), import from an already-signed-in browser (
login --browser-cookies chrome, no Playwright), or a durable master token. - Master-token auth - Mints fresh web cookies on demand with no per-session browser (
login --master-token --account [email protected]), so it self-heals expired sessions unattended — the auth model for servers, CI, and the remote MCP connector (claude.ai / ChatGPT). - Multi-account profiles - Switch between Google accounts without re-authenticating.
Agent Setup
Option 1 — CLI install:
notebooklm skill installInstalls the skill into ~/.claude/skills/notebooklm and ~/.agents/skills/notebooklm.
Option 2 — npx install (via the open skills ecosystem):
npx skills add teng-lin/notebooklm-pyFetches the canonical SKILL.md directly from GitHub.
Quick Start
CLI
/* Detailed source-code truncated for AI context efficiency. */Other useful CLI commands:
notebooklm auth check --test # Diagnose auth/cookie issues
notebooklm auth refresh --quiet # One-shot cookie keepalive (for cron / launchd / systemd)
notebooklm auth refresh --browser-cookies chrome # Re-extract and repair account routing
notebooklm auth inspect --browser 'chrome::Profile 1' # Preview one Chromium profile
notebooklm agent show codex # Print bundled Codex instructions
notebooklm agent show claude # Print bundled Claude Code skill template
notebooklm language list # List supported output languages
notebooklm metadata --json # Export notebook metadata and sources
notebooklm share status # Inspect sharing state
notebooklm source add-research "AI" --import-all # web research + import found sources
notebooklm skill status # Check local agent skill installation
notebooklm profile list # List all Google account profiles
notebooklm profile switch work # Switch active account profileUse --prompt-file PATH with ask, prompt-based generate commands, and source add-research when the text is too long for the shell command line. This reads prompt/query text from a file and is separate from source add ./file.pdf, which still uploads that file as a NotebookLM source.
Python API
import asyncio
from notebooklm import NotebookLMClient, MindMapKind
async def main():
async with NotebookLMClient.from_storage() as client:
# Create notebook and add sources
nb = await client.notebooks.create("Research")
await client.sources.add_url(nb.id, "https://example.com", wait=True)
# Chat with your sources
result = await client.chat.ask(nb.id, "Summarize this")
print(result.answer)
# Generate content (podcast, video, quiz, etc.)
status = await client.artifacts.generate_audio(nb.id, instructions="make it fun")
await client.artifacts.wait_for_completion(nb.id, status.task_id)
await client.artifacts.download_audio(nb.id, "podcast.m4a")
# Generate quiz and download as JSON
status = await client.artifacts.generate_quiz(nb.id)
await client.artifacts.wait_for_completion(nb.id, status.task_id)
await client.artifacts.download_quiz(nb.id, "quiz.json", output_format="json")
# Generate a mind map via the unified client.mind_maps API (issue #1256) —
# two kinds: the newer MindMapKind.INTERACTIVE studio map (shown; polled to
# completion by default) or MindMapKind.NOTE_BACKED JSON. Both export via:
mm = await client.mind_maps.generate(nb.id, kind=MindMapKind.INTERACTIVE)
await client.artifacts.download_mind_map(nb.id, "mindmap.json", mm.id)
asyncio.run(main())Documentation
- CLI Reference - Complete command documentation
- Python API - Full API reference
- MCP Guide - MCP server setup, transports, and tool reference
- REST API Server - Experimental localhost FastAPI server
- Configuration - Storage and settings
- Quota & Tier Limits - Per-tier notebook/source/studio limits and how they map to
AccountLimits.tier - Release Guide - Release checklist and packaging verification
- Troubleshooting - Common issues and solutions
- API Stability - Versioning policy and stability guarantees
- Upgrading to v0.8.0 - Breaking-change migration guide for the v0.8.0 error-and-return contract
For Contributors
- Architecture - Architectural overview and design principles
- Development Guide - Architecture, testing, and releasing
- RPC Development - Protocol capture and debugging
- RPC Reference - Payload structures
- Changelog - Version history and release notes
- Security - Security policy and credential handling
License
MIT License. See LICENSE for details.
File: docs/cli-reference.md
CLI Reference
Status: Active
Last Updated: 2026-08-12
Complete command reference for the notebooklm CLI—providing full programmatic access to all NotebookLM features, including capabilities not exposed in the web UI.
Exit codes: every command follows the convention documented in CLI Exit-Code Convention (
0success,1user/app error,2system/unexpected,130SIGINT). Two commands intentionally deviate for shell control-flow use (source stale --exit-on-staleopts into an inverted predicate;source waitis three-way); see the doc for details.
Command Structure
notebooklm [-p PROFILE] [--storage PATH] [--version] [-v|--quiet] <command> [OPTIONS] [ARGS]Global Options
-p, --profile NAME- Use a named profile (overridesNOTEBOOKLM_PROFILEenv var)--storage PATH- Override the default storage location-v, --verbose- Increase verbosity (-vfor INFO,-vvfor DEBUG)--quiet- Suppress status output and INFO/WARN log records (only errors survive). Structured--jsonpayloads are still emitted. Mutually exclusive with-v/-vv; combining the two raisesUsageError(exit2).--version- Show version and exit--help- Show help message
Environment Variables
NOTEBOOKLM_HOME- Base directory for all config files (default:~/.notebooklm)NOTEBOOKLM_PROFILE- Active profile name (default:default)NOTEBOOKLM_NOTEBOOK- Default notebook ID for any command exposing-n/--notebook. Resolution order:-n/--notebookflag >NOTEBOOKLM_NOTEBOOKenv > active context (notebooklm use) > error. Empty/whitespace values are treated as unset.NOTEBOOKLM_AUTH_JSON- Inline authentication JSON (for CI/CD, no file writes needed)NOTEBOOKLM_HL- Default output language for artifact generation (overridden by--language)NOTEBOOKLM_LOG_LEVEL- Override the package logger level (e.g.DEBUG,INFO,WARNING,ERROR)NOTEBOOKLM_DEBUG_RPC- Enable RPC debug logging (1to enable)NOTEBOOKLM_QUIET_DEPRECATIONS- Suppress deprecation notices (1to enable)
See Configuration for full env-var precedence and CI/CD setup.
Command Organization
- Session commands - Authentication and context management
- Notebook commands - CRUD operations on notebooks
- Chat commands - Querying and conversation management
- Grouped commands -
source,label,collection,artifact,agent,generate,download,note,share,research,language,skill,auth,profile,mcp - Utility commands -
metadata,doctor
Quick Reference
Session Commands
| Command | Description | Example |
|---|---|---|
login |
Authenticate via browser | notebooklm login / notebooklm login --browser msedge |
login --master-token |
Headless auth: mint cookies from a durable master token ([headless]) |
notebooklm login --master-token --account [email protected] |
use |
Set active notebook | notebooklm use abc123 |
status |
Show current context | notebooklm status |
status --paths |
Show configuration paths | notebooklm status --paths |
status --json |
Output status as JSON | notebooklm status --json |
clear |
Clear current context | notebooklm clear |
auth check |
Diagnose authentication issues | notebooklm auth check |
auth check --test |
Validate with network test | notebooklm auth check --test |
auth check --json |
Output as JSON | notebooklm auth check --json |
auth inspect |
List Google accounts visible to a browser cookie store (read-only) | notebooklm auth inspect --browser chrome |
auth import-cookies |
Import auth cookies from a JSON file (or stdin) into the active profile | notebooklm auth import-cookies cookies.json |
auth logout |
Clear saved cookies and cached browser profile | notebooklm auth logout |
auth refresh |
One-shot SIDTS rotation poke (for OS schedulers) | notebooklm auth refresh |
auth refresh --quiet |
Refresh; suppress success output | notebooklm auth refresh --quiet |
doctor |
Check environment health | notebooklm doctor |
doctor --fix |
Auto-fix detected issues | notebooklm doctor --fix |
doctor --json |
Output diagnostics as JSON | notebooklm doctor --json |
completion <shell> |
Print shell completion script (bash/zsh/fish) |
notebooklm completion zsh > ~/.zfunc/_notebooklm |
Profile Commands (`notebooklm profile `)
| Command | Description | Example |
|---|---|---|
list |
List all profiles | notebooklm profile list |
create <name> |
Create a new profile | notebooklm profile create work |
switch <name> |
Set the active profile | notebooklm profile switch work |
delete <name> |
Delete a profile | notebooklm profile delete old |
rename <old> <new> |
Rename a profile | notebooklm profile rename old new |
Language Commands (`notebooklm language `)
| Command | Description | Example |
|---|---|---|
list |
List all supported languages | notebooklm language list |
get |
Show current language setting | notebooklm language get |
get --local |
Show local config only (skip server sync) | notebooklm language get --local |
set <code> |
Set language for artifact generation | notebooklm language set zh_Hans |
set <code> --local |
Set local config only (skip server sync) | notebooklm language set ja --local |
Note: Language is a GLOBAL setting that affects all notebooks in your account.
Notebook Commands
| Command | Description | Example |
|---|---|---|
list |
List all notebooks | notebooklm list |
list --json |
Output as JSON | notebooklm list --json |
list --limit N |
Show at most N notebooks (default: unlimited) | notebooklm list --limit 10 |
list --no-truncate |
Do not truncate the Title column | notebooklm list --no-truncate |
use |
Set active notebook (verifies existence by default) | notebooklm use abc123 |
use --force |
Skip the existence check (offline / debugging) | notebooklm use abc123 --force |
use --json |
Emit {active_notebook_id, success, verified, notebook} envelope |
notebooklm use abc --json |
create <title> |
Create notebook (does not change active context) | notebooklm create "Research" |
create <title> --use |
Create notebook and make it the active context | notebooklm create "Research" --use |
create <title> --json |
JSON envelope; with --use includes active_notebook_id |
notebooklm create "X" --use --json |
delete -n |
Delete notebook (uses current notebook if -n omitted) |
notebooklm delete -n abc123 |
delete -n -y |
Skip confirmation | notebooklm delete -n abc123 -y |
delete -n --json |
Emit {notebook_id, success} envelope (plus context_cleared: true when deleting the active notebook); requires -y (refuses to prompt in JSON mode) |
notebooklm delete -n abc123 -y --json |
rename <title> |
Rename current notebook | notebooklm rename "New Title" |
summary |
Get AI summary | notebooklm summary |
summary --topics |
Include suggested topics | notebooklm summary --topics |
Chat Commands
| Command | Description | Example |
|---|---|---|
ask <question> |
Ask a question (auto-resumes the last conversation) | notebooklm ask "What is this about?" |
ask - |
Read question from stdin (Unix - convention) |
echo "what is X?" | notebooklm ask - |
ask --prompt-file PATH |
Read question from file (use - for stdin) |
notebooklm ask --prompt-file q.txt |
ask -c |
Continue a specific conversation | notebooklm ask -c conv_abc "..." |
ask --new |
DESTRUCTIVE. Permanently delete the notebook's current server-side conversation (turns are not recoverable) and start fresh on the next ask. Prompts for confirmation with the conversation's short id; pass --yes/-y to skip the prompt. --json implies --yes so scripted callers don't hang on stdin. Closes the workaround documented in #659. |
notebooklm ask --new -y "start fresh" |
ask --new --yes (alias -y) |
Skip the --new destructive-delete confirmation prompt. |
notebooklm ask --new --yes "..." |
ask -s |
Limit to specific source IDs (repeatable) | notebooklm ask "Summarize" -s src1 -s src2 |
ask --json |
Get answer with source references | notebooklm ask "Explain X" --json |
ask --request-timeout N |
Per-invocation HTTP request/read timeout in seconds for chat. Defaults to the library chat timeout. --timeout is a back-compat alias for the same flag. |
notebooklm ask "long prompt" --request-timeout 120 |
ask --save-as-note |
Save response as a note. When the answer contains [N] citations, the saved note preserves interactive hover-anchored citation links matching the NotebookLM web UI's "Save to note" behavior (issue #660). Answers without citations fall back to a plain-text note. |
notebooklm ask "Explain X" --save-as-note |
ask --save-as-note --note-title |
Save response with custom note title. The NotebookLM server may apply smart-title generation for citation-rich saves and override the requested title; the success message reflects what the server actually stored. | notebooklm ask "Explain X" --save-as-note --note-title "Title" |
suggest-prompts |
Get AI-suggested prompts for the notebook (each a title plus a ready-to-send instruction for ask) |
notebooklm suggest-prompts |
suggest-prompts --mode N |
Select the suggestion surface (1-10, default 4): 1=audio deep-dive, 2=audio brief, 3=video explainer, 4=chat questions, 5=audio critique, 6=audio debate, 8=quiz, 9=flashcards, 10=video short. Out-of-range exits 1. | notebooklm suggest-prompts --mode 8 |
suggest-prompts --query TEXT |
Free-text steer for the kind of prompts to suggest | notebooklm suggest-prompts --query "key risks" |
suggest-prompts -s |
Limit to specific source IDs (repeatable; defaults to all sources) | notebooklm suggest-prompts -s src1 -s src2 |
suggest-prompts --json |
Machine-readable output ({notebook_id, suggestions, count}) |
notebooklm suggest-prompts --json |
configure --mode |
Set predefined chat mode (default, learning-guide, concise, detailed) |
notebooklm configure --mode learning-guide |
configure --persona |
Set custom persona prompt (up to 10,000 chars) | notebooklm configure --persona "Act as a tutor" |
configure --response-length |
Response verbosity (default, longer, shorter) |
notebooklm configure --response-length longer |
configure --json |
Machine-readable output | notebooklm configure --mode concise --json |
history |
View conversation history | notebooklm history |
history -l N |
Maximum number of Q&A turns to show | notebooklm history -l 5 |
history --json |
Machine-readable output | notebooklm history --json |
history --clear |
Clear local conversation cache | notebooklm history --clear |
history --save |
Save history as a note | notebooklm history --save |
history --save -t "Title" |
Save history with custom title | notebooklm history --save -t "Summary" |
history --show-all |
Show full Q&A content (not preview) | notebooklm history --show-all |
history --no-truncate |
Disable the 50-char preview cap on the Question/Answer columns in the table view (the existing -l/--limit flag is unchanged: it caps the number of Q&A turns fetched server-side) |
notebooklm history --no-truncate |
Setting just one of configure --persona / --response-length merges with the current settings — the field you omit is preserved, not reset. A bare configure with no flags resets all custom chat settings to their defaults. (A --mode preset cannot be combined with --persona/--response-length.)
Source Commands (`notebooklm source `)
Supported source types: URLs, YouTube videos, files (PDF, text, Markdown, Word, audio, video, images), Google Drive documents, and pasted text.
| Command | Arguments | Options | Example |
|---|---|---|---|
list |
- | --json, --limit N, --no-truncate, --label, --status |
source list --limit 20 --no-truncate |
add <content> |
URL/file/text (use - for stdin) |
--title, --type, --timeout, --follow-symlinks, --allow-internal (URL sources only), --json (file-source --mime-type overrides extension inference — see detailed section) |
source add "https://..." --timeout 90 |
add-drive <title> |
Drive file ID, title | --mime-type [google-doc|google-slides|google-sheets|pdf], --json |
source add-drive abc123 "Doc" --mime-type google-slides |
add-drive-file |
Drive file ID or share URL | --title, --wait, --json |
source add-drive-file abc123 --title "Notes" --wait |
add-research [query] |
Search query (or --prompt-file - for stdin) |
--mode [fast|deep], --from [web|drive], --import-all, --cited-only, --no-wait, --timeout, --prompt-file PATH |
source add-research "AI" --mode deep --no-wait |
get |
Source ID | --json |
source get src123 |
fulltext |
Source ID | --json, -o FILE, --force, --no-clobber, -f [text|markdown] |
source fulltext src123 -f markdown -o out.md (-f markdown requires the markdown extra: pip install "notebooklm-py[markdown]" — full extras matrix: docs/installation.md#optional-extras-matrix) |
guide |
Source ID | --json |
source guide src123 |
stale |
Source ID | --exit-on-stale, --json |
source stale src123 (exit 0 on success; pass --exit-on-stale for the back-compat inverted predicate — see exit codes) |
wait |
Source ID | --timeout, --interval, --json |
source wait src123 --timeout 300 --interval 5 |
clean |
- | --dry-run, -y/--yes, --json |
source clean --dry-run |
rename <title> |
Source ID, new title | --json |
source rename src123 "New Name" |
refresh |
Source ID | --json |
source refresh src123 |
delete |
Source ID | -y/--yes, --json |
source delete src123 -y |
delete-by-title <title> |
Exact source title | -y/--yes, --json |
source delete-by-title "My Source" |
All source subcommands also accept -n/--notebook ID (resolves via flag > NOTEBOOKLM_NOTEBOOK env > active context).
source delete accepts only full source IDs or unique partial-ID prefixes. To delete by exact source title, use source delete-by-title "<title>".
source clean automatically removes duplicate, error, and access-blocked sources; combine with --dry-run to preview the candidate set first.
source fulltext -o FILE auto-renames an existing output path by default (FILE -> FILE (2), etc.) instead of overwriting it. Pass --force to overwrite intentionally, or --no-clobber to fail if the path exists.
source stale reports whether a URL/Drive source needs a refresh. By default it follows the standard CLI exit convention (0 on success, 1 on error); branch on the JSON stale/fresh fields (or stdout text) for the freshness verdict. Pass --exit-on-stale to opt into the back-compat inverted predicate (0 = stale, 1 = fresh) for shell idioms like if notebooklm source stale --exit-on-stale ID; then refresh; fi.
source list also accepts --label to list only the sources in a given label (a saved selection). The selector resolves a label id (or partial prefix) or an exact label name; see Label Commands.
source list --status <state> restricts the listing to one ingestion status — ready, processing, error, preparing, or unknown. The choices are derived from SourceStatus, so they cannot drift from the labels the Status column renders, and the filter is applied inside the fetch, so count in --json always matches the rows shown. It composes with --label.
Finding orphaned sources (`--status preparing`)
A file add that fails after its source row is registered leaves that row in place deliberately — it is the evidence of what happened, and it still counts against the notebook's source quota. The row sits at preparing, not error, so the status a caller reaches for first finds nothing (#2138):
notebooklm source list --status preparing # the reconciliation query
notebooklm source delete # once you have confirmed it is stuckRows that are genuinely mid-upload also report preparing, so this filter cannot by itself tell "abandoned" from "in flight" — re-run it a minute apart and act only on rows that persist. Nothing is deleted automatically; that posture is deliberate (see #2110).
When the failing add raised in your own process, you do not need to search at all: the exception carries the retained row's id directly, via getattr(exc, "source_id", None).
Drive-backed sources in `source list` / `source get`
Both commands emit the same --json source row:
{
"id": "ef72c03c-…", "title": "Rubisco Research", "type": "google_docs", "url": null,
"status": "ready", "status_id": 2, "created_at": "2026-01-23T18:42:00",
"drive_document_id": "1oAk_INJ…", "drive_status": "deleted", "is_drive_degraded": true
}(source list prefixes each row with a 1-based index.)
drive_document_id— the Google Drive file id. A Drive source carries nourl, so this is the only field tying it back to the file it was created from;source add-drivematches on it to stay idempotent.drive_status— Drive-side health:inaccessible/syncing/active/deleted/gen_ai_access_denied, orunknownfor a code this client cannot map.nullmeans the row made no Drive-health claim at all, which is a different answer from"unknown".is_drive_degraded—trueonly when the backend explicitly reported a non-healthy Drive state.falsemeans "nothing degraded was reported" — equally true for a non-Drive source, foractive, and for an unreadable code — not "the file is confirmed present".
All three keys are present on every row (null/false on non-Drive sources), so jq '.sources[] | select(.drive_status == "deleted")' needs no // empty guard.
Which rows are Drive-backed? drive_document_id and drive_status are decoded from structurally independent parts of the response, so either one being non-null means the row is Drive-backed — neither alone is the authoritative test. In particular, the common case is an id with no health claim ({"drive_document_id": "1oAk…", "drive_status": null}), so filtering on drive_status != null will miss most Drive sources.
Unlike the MCP/REST surfaces, the CLI ships no raw Drive status code beside the label. It would carry no extra information (an unmappable code is replaced by a client-side sentinel before it reaches output), and its code space collides adversarially with status_id's — 2/3 mean ready/error for ingestion but syncing/active for Drive, so a consumer reasoning by analogy would select exactly the wrong rows.
`status` and `drive_status` are different axes
status reports NotebookLM's own ingestion, which completes and stays complete after the Drive file is deleted or unshared. A source therefore reads "status": "ready" while "drive_status": "deleted" — answers grounded on it may be stale.
Human (non---json) output reflects this without adding a column:
source listappends the Drive verdict to the Status cell —ready (drive: deleted)— but only for a row the backend reports as degraded (is_drive_degraded), whatever that row's ingestion status is. An unreadable code (unknown) is not flagged here; it is not evidence of degradation, and the table should not cry wolf on protocol drift. Note that Rich sizes columns table-wide, so a single annotated row does re-flow the whole table; that cost is only paid when something is actually wrong.source getaddsDrive File ID:andDrive Status:lines, each shown only when that field is present.
Cross-surface naming. MCP and REST spell this axis
drive_status(raw code) +drive_status_label(string); the CLI usesdrive_statusfor the string, matching its own long-standingstatus(label) /status_id(code) pairing. Soselect(.drive_status == "deleted")is right for the CLI and wrong for MCP/REST, where that key holds an integer. Both surfaces resolve the label through the same mapping helper, so the vocabulary never diverges — only the key name.
Label Commands (`notebooklm label `)
Source labels group a notebook's sources into topic buckets. A `` argument accepts a label id (or partial prefix) or an exact label name; an ambiguous name lists the matching ids so you can disambiguate.
| Command | Arguments | Options | Example |
|---|---|---|---|
list |
- | --json |
label list -n nb123 |
sources |
Label id or name | --json |
label sources Papers |
generate |
- | --scope [all|unlabeled], -y/--yes, --json |
label generate --scope all -y |
create <name> |
Label name | --emoji 📄, --json |
label create "Papers" --emoji 📄 |
rename <new_name> |
Label ref, new name | --json |
label rename Papers "Research Papers" |
emoji |
Label ref, emoji | --json |
label emoji Papers 📚 |
add ... |
Label ref, one+ source ids | --json |
label add Papers src123 src456 |
remove ... |
Label ref, one+ source ids | --json |
label remove Papers src123 |
delete ... |
One+ label refs | -y/--yes, --json |
label delete Papers -y |
All label subcommands accept -n/--notebook ID (resolves via flag > NOTEBOOKLM_NOTEBOOK env > active context).
label generate --scope all (wipes and regenerates every label with new ids) and label delete are destructive and require -y/--yes to confirm (or an interactive prompt). label generate --scope unlabeled (the default) only labels currently-unlabeled sources and needs no confirmation. label add appends sources (existing members survive; labels may overlap) and label remove un-assigns sources from the label only — the sources stay in the notebook (and in any other label). label delete removes the label only — its sources become unlabeled, not deleted. source_id arguments to label add/label remove accept partial-prefix matching like every other source-id command.
Collection Commands (`notebooklm collection `)
Collections group whole notebooks into named, account-level buckets (playlist-style) — the sibling of label, which groups sources within a notebook. A notebook can belong to multiple collections. A `` argument accepts a collection id (or partial prefix) or an exact collection name; an ambiguous name lists the matching ids so you can disambiguate.
| Command | Arguments | Options | Example |
|---|---|---|---|
list |
- | --json |
collection list |
notebooks |
Collection id or name | --json |
collection notebooks "Research Q3" |
create <name> |
Collection name | --json |
collection create "Research Q3" |
rename <new_name> |
Collection ref, new name | --json |
collection rename "Research Q3" "Research Q4" |
add <notebook_id>... |
Collection ref, one+ notebook ids | --json |
collection add "Research Q3" nb123 nb456 |
remove <notebook_id>... |
Collection ref, one+ notebook ids | --json |
collection remove "Research Q3" nb123 |
delete ... |
One+ collection refs | -y/--yes, --json |
collection delete "Research Q3" -y |
Collections are account-level, so — unlike label — the collection commands take no -n/--notebook option; their membership arguments are notebooks. collection add appends notebooks (existing members survive; a notebook may belong to multiple collections) and collection remove un-assigns a notebook from that collection only — the notebook is not deleted and stays in any other collection. collection delete (destructive, requires -y/--yes) removes the collection only, never its member notebooks. notebook_id arguments to collection add/collection remove accept partial-prefix matching like every other notebook-id command.
Research Commands (`notebooklm research `)
| Command | Arguments | Options | Example |
|---|---|---|---|
status |
- | -n/--notebook, --json |
research status |
wait |
- | -n/--notebook, --timeout, --interval, --import-all, --cited-only, --json |
research wait --import-all --cited-only |
import |
- | -n/--notebook, --run-id, --cited-only, --timeout, --max-sources, --allow-duplicate, --json |
research import |
cancel |
RUN_ID |
-n/--notebook, --json |
research cancel <run_id> |
Generate Commands (`notebooklm generate `)
All generate commands (except the synchronous mind-map) accept the same uniform polling-flag surface:
-n, --notebook IDto target a specific notebook (also resolved fromNOTEBOOKLM_NOTEBOOK/ active context)-s, --source IDto select specific sources (repeatable)--jsonfor machine-readable output (returnstask_idandstatus)--wait / --no-waitto block until completion (default:--no-wait— returns immediately with atask_id)--timeout SECONDSto cap the--waitbudget (defaults: 1200s for audio, 1800s for video, 3600s for cinematic-video, and 300s for quiz/flashcards/slide-deck/infographic/data-table/report/revise-slide). No-op without--wait.--interval SECONDSto tune polling cadence (default: 2). No-op without--wait.--retry Nto automatically retry on rate limits with exponential backoff--prompt-file PATHto read the description/query from a file (or-for stdin) instead of the command line. Mutually exclusive with the positional argument; useful for long prompts that exceed shell length limits.
Language-aware generate commands (audio, video, cinematic-video, report, infographic, slide-deck, data-table, mind-map) also support:
--language LANGto override output language (precedence:--language>NOTEBOOKLM_HLenv > config >'en')
quiz, flashcards, and revise-slide do not accept --language.
| Command | Arguments | Type-specific options | Example |
|---|---|---|---|
audio [description] |
Instructions | --format [deep-dive|brief|critique|debate], --length [short|default|long] |
generate audio "Focus on history" |
video [description] |
Instructions | --format [explainer|brief|cinematic|short], --style [auto|custom|classic|whiteboard|kawaii|anime|watercolor|retro-print|heritage|paper-craft] (not for --format cinematic/short), --style-prompt TEXT (required with --style custom; rejected with --format cinematic/short) |
generate video "Explainer for kids" |
cinematic-video [description] |
Instructions | Alias for video --format cinematic |
generate cinematic-video "Documentary about quantum physics" |
slide-deck [description] |
Instructions | --format [detailed|presenter], --length [default|short] |
generate slide-deck |
revise-slide <description> |
Revision instructions | -a/--artifact (required), --slide N (required) |
generate revise-slide "Move title up" --artifact --slide 0 |
quiz [description] |
Instructions | --difficulty [easy|medium|hard], --quantity [fewer|standard|more] |
generate quiz --difficulty hard |
flashcards [description] |
Instructions | --difficulty [easy|medium|hard], --quantity [fewer|standard|more] |
generate flashcards |
infographic [description] |
Instructions | --orientation [landscape|portrait|square], --detail [concise|standard|detailed], --style [auto|sketch-note|professional|bento-grid|editorial|instructional|bricks|clay|anime|kawaii|scientific] |
generate infographic |
data-table <description> |
Instructions | (uniform options only) | generate data-table "compare concepts" |
mind-map |
- | --kind [interactive|note-backed], --instructions TEXT (no --wait / --timeout / --interval / --retry / --prompt-file) |
generate mind-map --kind interactive |
report [description] |
Instructions | --format [briefing-doc|study-guide|blog-post|custom], --append TEXT (no effect with --format custom) |
generate report --format study-guide |
Two kinds of mind map (issue #1256). NotebookLM has two distinct mind-map objects.
generate mind-map --kind interactive(the default) builds the interactive kind — a studio artifact (the one the web app now creates) that is polled to completion.generate mind-map --kind note-backedbuilds the note-backed kind — a JSON node tree stored as a note, generated synchronously. Both produce the same{mind_map, note_id, kind}output, appear inartifact list --type mind-map, and download viadownload mind-map.--instructionsis a free-text prompt that steers generation: the interactive kind applies it reliably (it travels in theCREATE_ARTIFACTprompt slot the server honors and thatartifact get-promptreads back); the note-backed kind passes it through toGENERATE_MIND_MAP, but the server may not always act on it.
Artifact Commands (`notebooklm artifact `)
| Command | Arguments | Options | Example |
|---|---|---|---|
list |
- | --type [all|audio|video|slide-deck|quiz|flashcard|infographic|data-table|mind-map|report|fantasy-map|file], --limit N, --no-truncate, --json |
artifact list --type audio --limit 5 |
get |
Artifact ID | --json |
artifact get art123 |
get-prompt |
Artifact ID | --json |
artifact get-prompt art123 |
rename <title> |
Artifact ID, title | --json |
artifact rename art123 "Title" |
delete |
Artifact ID | -y/--yes, --json |
artifact delete art123 -y |
export |
Artifact ID | --title TEXT (required), --type [docs|sheets], --json |
artifact export art123 --title "My Doc" --type sheets |
poll <task_id> |
Task ID (from generate <type>) |
--json |
artifact poll task123 |
wait |
Artifact ID (from artifact list) |
--timeout (default: 300), --interval (default: 2), --json |
artifact wait art123 --timeout 600 |
retry |
Artifact ID (from artifact list) |
--wait, --timeout (default: 300), --interval (default: 2), --json |
artifact retry art123 --wait |
suggestions |
- | --json |
artifact suggestions |
All artifact subcommands also accept -n/--notebook ID.
Note:
artifact deleteon a Mind Map clears its content rather than removing the artifact entry — Mind Maps are stored alongside notes and Google may garbage-collect cleared entries later. The CLI prints aCleared mind map:message instead ofDeleted artifact:when this happens.
Common confusion:
pollvswaitID kind. Both commands accept the same identifier — the API returns one ID that serves as both the generationtask_id(during creation) and theartifact_id(once the row appears inartifact list). The split is operational, not semantic:
artifact poll <task_id>— single non-blocking status check. Pass the rawtask_idreturned bynotebooklm generate <type>straight through.polldoes not prefix-match againstartifact list, so it works immediately after ageneratecall returns, before the artifact has appeared in any list response.artifact wait <artifact_id>— blocks (with exponential backoff) until the artifact iscompleted,failed, or--timeoutelapses. Accepts a full UUID or a unique prefix that resolves againstartifact listvia the standard partial-ID resolver.Rule of thumb: just generated something? use
poll. Found it inartifact list? usewait. You can pass the same string to both — the kind label is about lifecycle stage, not about a different identifier format.
artifact retry <artifact_id>re-runs generation for a failed artifact in place (the web UI "Retry" button) — the artifact is not deleted first and the same ID is preserved. Accepts a full UUID or a unique prefix. By default it kicks off the retry and returns immediately (Retry started:); pass--waitto block until the retried generation reaches a terminal state. A synchronous refusal (rate limit / quota / not-retryable) exits non-zero with a typed error rather than reporting a started task. A retry may itself fail again provider-side, in which case you can runartifact retryagain against the same ID.
Download Commands (`notebooklm download `)
Every download subcommand accepts the same selection / safety / output flag set: -n/--notebook ID, -a/--artifact ID, --all, --latest (default), --earliest, --name TEXT (fuzzy title match), --dry-run, --force, --no-clobber (don't overwrite existing files: a single download fails, --all skips them; default is auto-rename), and --json.
| Command | Arguments | Type-specific options | Example |
|---|---|---|---|
audio [path] |
Output path | (none) | download audio --all |
video [path] |
Output path | (none) | download video --latest |
cinematic-video [path] |
Output path | Alias for download video; cinematic and standard videos share the same artifact type |
download cinematic-video ./documentary.mp4 |
slide-deck [path] |
Output path | --format [pdf|pptx] (default: pdf) |
download slide-deck ./slides.pdf |
infographic [path] |
Output path | (none) | download infographic ./info.png |
report [path] |
Output path | (none) | download report ./report.md |
mind-map [path] |
Output path | (none) | download mind-map ./map.json |
data-table [path] |
Output path | (none) | download data-table ./data.csv |
quiz [path] |
Output path | --format [json|markdown|html] (default: json) |
download quiz --format markdown quiz.md |
flashcards [path] |
Output path | --format [json|markdown|html] (default: json) |
download flashcards cards.json |
Note Commands (`notebooklm note `)
| Command | Arguments | Options | Example |
|---|---|---|---|
list |
- | --json |
note list --json |
create [content] |
Note content (or - for stdin) |
--content TEXT (or - for stdin; mutex with positional), -t/--title TEXT, --json |
cat notes.md | note create - |
get |
Note ID | --json |
note get note123 |
save |
Note ID | --title, --content, --json |
note save note123 --title "Updated title" |
rename <title> |
Note ID, title | --json |
note rename note123 "Title" |
delete |
Note ID | -y/--yes, --json |
note delete note123 -y |
All note subcommands also accept -n/--notebook ID.
source get/artifact get/note getexit1on not-found (BREAKING). All threegetcommands now exit1when the requested ID does not resolve to an existing item, matching the rest of the CLI's user-error convention. Under--jsonthe failure body is the standard typed error envelope ({"error": true, "code": "NOT_FOUND", "message": "...", "id": "...", "notebook_id": "..."}); without--jsonthe message is written to stderr. The previous behavior was exit0with a "not found" line on stdout. The pre-existing "no partial-ID match" branch (raised by_resolve_partial_idas aClickException) was already exit1and is unchanged. See CLI Exit-Code Convention for migration guidance.
Metadata Command
Export notebook metadata and a simplified source list.
notebooklm metadata [OPTIONS]Options:
-n, --notebook ID- Specify notebook (uses current if not set)--json- Output as JSON for scripts
Examples:
notebooklm metadata
notebooklm metadata -n abc123 --jsonSkill Commands (`notebooklm skill `)
Manage NotebookLM agent skill integration.
| Command | Description | Example |
|---|---|---|
install |
Install/update the skill for claude, .agents, or both |
skill install --target all |
status |
Check installed targets and version info | skill status --scope project |
uninstall |
Remove one or more installed targets | skill uninstall --target agents |
show |
Display the packaged skill or an installed target | skill show --target source |
package |
Build a Claude-uploadable skill archive (chat/Cowork) | skill package -o dist/ |
Defaults:
skill installuses--scope user --target allclaudemaps to.claude/skills/notebooklm/SKILL.mdagentsmaps to.agents/skills/notebooklm/SKILL.mdshow --target sourceprints the canonical packaged skill file- Project-scope installs support
--dry-run,--no-clobber, and--force; these flags are rejected for user-scope installs.
The packaged wheel includes the repo-root SKILL.md, so the same skill content powers notebooklm skill install, GitHub discovery, and npx skills add teng-lin/notebooklm-py.
Codex does not use the skill subcommand. In this repository it reads the root AGENTS.md file and invokes the notebooklm CLI or Python API directly.
MCP Commands (`notebooklm mcp `)
Install the NotebookLM MCP server block into supported MCP client configs.
| Command | Description | Example |
|---|---|---|
install <client> |
Configure claude-desktop, claude-code, cursor, or windsurf |
mcp install claude-desktop |
install <client> --config-path PATH |
Write a non-standard config path | mcp install cursor --config-path ./mcp.json |
The installer writes a notebooklm server entry that launches notebooklm-mcp
through uvx. Re-running is idempotent and preserves unrelated servers in the
same config file. See MCP server guide.
Agent Commands (`notebooklm agent `)
Show bundled instructions for supported agent environments.
| Command | Description | Example |
|---|---|---|
show codex |
Print the Codex repository guidance | agent show codex |
show claude |
Print the bundled Claude Code skill template | agent show claude |
agent show codex prefers the root AGENTS.md file when running from a source checkout, so the CLI mirrors the same instructions Codex sees in the repository.
Features Beyond the Web UI
These CLI capabilities are not available in NotebookLM's web interface:
| Feature | Command | Description |
|---|---|---|
| Batch downloads | download <type> --all |
Download all artifacts of a type at once |
| Quiz/Flashcard export | download quiz --format json |
Export as JSON, Markdown, or HTML |
| Mind map extraction | download mind-map |
Export hierarchical JSON for visualization tools |
| Data table export | download data-table |
Download structured tables as CSV |
| Slide deck as PPTX | download slide-deck --format pptx |
Download as editable .pptx (web UI only offers PDF) |
| Slide revision | generate revise-slide "prompt" --artifact --slide N |
Modify individual slides with a natural-language prompt |
| Report template append | generate report --format study-guide --append "..." |
Append instructions to built-in templates |
| Source fulltext | source fulltext |
Retrieve the indexed text content of any source |
| Save chat to note | ask "..." --save-as-note / history --save |
Save Q&A answers or full conversation as notebook notes |
| Programmatic sharing | share commands |
Manage permissions without the UI |
Detailed Command Reference
Authentication: `login`
Authenticate with Google NotebookLM via browser.
Python equivalent: load saved credentials with
NotebookLMClient.from_storage(...)and use it as an async context manager. The CLI's interactive browser-login flow has no Python counterpart — runnotebooklm loginonce to seedstorage_state.json, then drive the API from Python.
notebooklm login [OPTIONS]By default, opens a Chromium browser with a persistent profile. Complete the Google login in the browser window — the CLI detects the redirect back to either personal host (notebook.google.com or notebooklm.google.com) and saves the session automatically (no terminal keystroke required). The wait window is 5 minutes by default; if login is not detected before then, the command exits with a retry hint. Use --browser-timeout to extend that human-interaction window, --browser msedge for Microsoft Edge, or --browser-cookies to import cookies from an already-logged-in browser without launching Playwright.
Options:
--storage PATH- Where to save storage_state.json (default:$NOTEBOOKLM_HOME/profiles//storage_state.json)--browser [chromium|msedge|chrome]- Browser to use for login (default:chromium). Usechromefor system Google Chrome (workaround when bundled Chromium crashes, e.g. macOS 15+); usemsedgefor Microsoft Edge. Note: onlychromiumis auto-installed by the CLI on first login (~170 MB Chromium download);--browser msedgeand--browser chromerequire the corresponding browser to be already installed on your system.--browser-timeout SECONDS- Human-interaction window for ordinary headed login and browser-assisted master-token bootstrap (default:300).--browser-cookies <auto|chrome|edge|firefox|safari|brave|arc|...>- Read cookies from an installed browser instead of launching Playwright. Pass an explicit browser name, orautoto let rookiepy auto-detect. For Chromium-family user profiles, usechrome::(for examplechrome::Profile 1orbrave::Work) to extract from one profile explicitly. For Firefox Multi-Account Containers, usefirefox::<container-name>to extract from a single container, orfirefox::nonefor the no-container default — unscopedfirefoxmerges every container's cookies (and emits a warning when that's happening). Requirespip install "notebooklm-py[cookies]"(full extras matrix: docs/installation.md#optional-extras-matrix).--account EMAIL- Pick a signed-in Google account by email when several are present in the browser. Saves to the active profile by default; use--profile-namefor a separate named profile or--storagefor an exact path. Only valid with--browser-cookies.--all-accounts- Extract every Google account signed in to the browser into separate profiles named from each account email. Only valid with--browser-cookies.--update- With--all-accounts: when an account's natural profile name (e.g.alicefor[email protected]) already exists but has no account metadata, update that profile in place instead of creating a suffixedalice-2. Profiles that already bind a different email still get a suffix to avoid clobbering. Only valid with--all-accounts.--profile-name NAME- Write a targeted--accountimport to this named profile instead of the active profile. Only valid with--browser-cookies.--fresh- Start with a clean browser session (deletes the cached browser profile). Use to switch Google accounts. With explicit--storage, a pre-existing browser sidecar is deleted only when it carries NotebookLM's ownership marker or is the canonical legacy/named-profile layout; arbitrary unowned directories are refused. Has no effect with--browser-cookies.--include-domains LABEL[,LABEL...]- Opt in to extracting sibling-product cookies (default: required Google auth/Drive cookies only). Supported labels:youtube,docs,myaccount,mail,all. Pass labels comma-separated or repeat the flag.
Master-token (headless) options — mint/refresh web cookies from a durable Google master token, no per-session browser. Requires pip install "notebooklm-py[headless]". Full guide: installation.md#d-headless-server-or-ci.
--master-token- Bootstrap headless auth. Requires--account EMAIL. A visible browser opens Google's EmbeddedSetup to capture the single-useoauth_token(needs[browser]), or pass it with--oauth-token. Exchanges it for a durable master token (saved0600atmaster_token.json), mints cookies intostorage_state.json, and verifies by listing notebooks.--master-token-refresh- Legacy forced re-mint; prefernotebooklm auth refresh. It still unconditionally replacesstorage_state.jsonfrom the stored master token and preserves CLI context.--oauth-token VALUE- Provide the single-use EmbeddedSetupoauth_tokenmanually (headless boxes without[browser]).--cdp-url URL- Captureoauth_tokenby attaching to a running Chrome over CDP (e.g.http://localhost:9222) instead of launching a browser.--android-id HEX- Override the per-install Android id (default: generated and persisted with the token). ⚠️ The master token is a full-account, durable credential — use a dedicated/throwaway account only.--force- With--master-token, overwrite even if the target profile already holds a session for a different account. Without it, a mismatched--accountis refused (use a dedicated-pinstead) so account B's mint can't silently clobber account A's profile.
Examples:
# Default (Chromium)
notebooklm login
# Use Microsoft Edge (for orgs that require Edge for SSO)
notebooklm login --browser msedge
# Reuse cookies from your already-logged-in Chrome session
notebooklm login --browser-cookies chrome
notebooklm login --browser-cookies 'chrome::Profile 1' # one Chromium profile
# Auto-detect any supported browser via rookiepy
notebooklm login --browser-cookies auto
# Firefox Multi-Account Containers: target one container
notebooklm login --browser-cookies 'firefox::Work'
notebooklm login --browser-cookies 'firefox::none' # no-container default
# Populate a named profile via cookie import
notebooklm --profile work login --browser-cookies chrome
# Pick a specific browser account by email
notebooklm login --browser-cookies chrome --account [email protected]
# Extract every signed-in browser account into separate profiles
notebooklm login --browser-cookies chrome --all-accounts
# Force a clean browser session before logging in
notebooklm login --fresh
# Headless master-token auth (one browser sign-in, then no per-session browser)
notebooklm login --master-token --account [email protected]
notebooklm login --master-token --account [email protected] --oauth-token "$OAUTH_TOKEN" # headless box
notebooklm login --master-token-refresh # legacy: force a re-mint unconditionallyNotes on --browser-cookies:
- Honors
--profile/NOTEBOOKLM_PROFILEand writes to that profile'sstorage_state.json. - Without
--accountor--all-accounts, imports the selected browser/profile's default Google account into the target profile. - For Chromium-family browsers, unscoped
chrome,brave,edge, etc. fan out across populated user profiles when account selection is needed. Usechrome::to read exactly one profile; directory names such asDefaultandProfile 1are stable across UI renames. - Use
notebooklm auth inspect --browserto see available account emails before a targeted import; pass-vto show the Chromium profile directory each account came from.
Authentication: `auth import-cookies`
Import authentication cookies from a JSON file (or stdin) and persist them to the active profile's storage_state.json — a file-backed, persistent alternative to the env-var-based NOTEBOOKLM_AUTH_JSON for users who can obtain cookies as JSON but find the browser/Playwright login flow difficult.
⚠️ These are full-account credentials. A JSON cookie export grants the same access as being logged in. Only import a file you exported yourself, keep it private, and delete it after import. Be especially cautious with third-party browser cookie-export extensions.
notebooklm auth import-cookies JSON_PATH [OPTIONS]Accepts either a Playwright storage_state object ({"cookies": [...]}) or a bare JSON list of cookie objects (the shape most browser cookie-export tools produce). Use - to read JSON from stdin. Common export fields are normalized (e.g. expirationDate → expires), __Secure-/__Host- cookies are forced Secure, and any storage_state origins (localStorage/sessionStorage) are dropped.
Imported cookies are filtered through the same domain allowlist used by browser login and validated locally for the Tier 1 NotebookLM-required cookies. The required __Secure-1PSIDTS entry must also be live and RFC 6265-routable to the accounts.google.com/RotateCookies endpoint. If any secondary-binding cookie is present, the supplied set must form a usable binding — OSID, or APISID+SAPISID together with bare LSID; a completely absent Tier 2 set is preserved after a warning for compatibility with unablated account flows. The result is written atomically with private (0o600) permissions. Invalid input never overwrites an existing session, and an existing storage_state.json is first copied to storage_state.json.bak so a stale import can be rolled back (one step — each run overwrites the previous .bak).
Incompatible with NOTEBOOKLM_AUTH_JSON: unset that env var first, or the command exits with an error (it does not silently fall back to the env auth).
Options:
--include-domains LABEL(repeatable) - Opt in to persisting sibling-product cookies (default: required Google auth/Drive/NotebookLM domains only). Pass labels comma-separated or repeat the flag (e.g.--include-domains youtube,docsor--include-domains youtube --include-domains docs). Same labels aslogin:youtube,docs,myaccount,mail,all.--include-optional- Persist all optional sibling-product cookie domains.--json- Emit a JSON result (storage_path,cookie_count,backup_path).--quiet- Suppress success output.
Examples:
# Import a bare cookie list exported by a browser tool
notebooklm auth import-cookies cookies.json
# Import a Playwright storage_state into a named profile
notebooklm -p work auth import-cookies playwright-storage-state.json
# Pipe JSON from stdin
cat cookies.json | notebooklm auth import-cookies -Session: `use`
Set the active notebook for subsequent commands.
notebooklm use [OPTIONS] <notebook_id>By default, the notebook must exist on the server; a typo or unreachable backend results in a non-zero exit and the saved context is left untouched. Pass --force to bypass verification (offline / debugging).
Supports partial ID matching:
notebooklm use abc # Matches abc123def456...Options:
--force- Skip the existence check and persist the notebook ID even if verification fails.--json- Emit{"active_notebook_id": "", "success": true, "verified": true|false, "notebook": {...}}.verified: falsecovers the--forcepath.
When credentials are expired, use routes through the canonical "Not logged in" handler instead of a generic "Could not verify" message; under --json you get the standard {"error": true, "code": "AUTH_REQUIRED", ...} envelope.
Session: `status`
Show current context (active notebook and conversation).
notebooklm status [OPTIONS]Options:
--paths- Show resolved configuration file paths--json- Output as JSON (useful for scripts)
Examples:
# Basic status
notebooklm status
# Show where config files are located
notebooklm status --paths
# Output shows home_dir, storage_path, context_path, browser_profile_dir
# JSON output for scripts
notebooklm status --jsonWith --paths:
Configuration Paths
┏━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┓
┃ File ┃ Path ┃ Source ┃
┡━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━┩
│ Home Directory │ /home/user/.notebooklm │ default │
│ Storage State │ .../storage_state.json │ │
│ Context │ .../context.json │ │
│ Browser Profile │ .../browser_profile │ │
└─────────────────┴──────────────────────────────┴─────────────────┘Language: `list`, `get`, `set`
Manage the output language for artifact generation (audio, video, etc.).
Important: Language is a GLOBAL setting that affects all notebooks in your account.
# List all supported languages with native names
notebooklm language list
# Show current language setting (syncs from server)
notebooklm language get
# Set language to Simplified Chinese
notebooklm language set zh_Hans
# Set language to Japanese
notebooklm language set jaOptions for get:
--local- Skip server sync, show local config only--json- Output as JSON
Options for set:
--local- Save to local config only, skip server sync--json- Output as JSON
Common language codes:
| Code | Language |
|---|---|
en |
English |
zh_Hans |
中文(简体) - Simplified Chinese |
zh_Hant |
中文(繁體) - Traditional Chinese |
ja |
日本語 - Japanese |
ko |
한국어 - Korean |
es |
Español - Spanish |
fr |
Français - French |
de |
Deutsch - German |
pt_BR |
Português (Brasil) - Brazilian Portuguese |
Run notebooklm language list for all 80+ supported languages.
Share: `status`, `public`, `view-level`, `add`, `update`, `remove`
Manage notebook sharing settings and user permissions.
# Show current sharing status and shared users
notebooklm share status
# Enable public link sharing (anyone with link can view)
notebooklm share public --enable
# Disable public sharing
notebooklm share public --disable
# Set what viewers can access
notebooklm share view-level full # Full notebook (chat, sources, notes)
notebooklm share view-level chat # Chat interface only
# Share with specific users
notebooklm share add [email protected] # Add as viewer (default)
notebooklm share add [email protected] --permission editor # Add as editor
notebooklm share add [email protected] -m "Check this out!" # With message
notebooklm share add [email protected] --no-notify # Skip email notification
# Update user permission
notebooklm share update [email protected] --permission editor
# Remove user access
notebooklm share remove [email protected]
notebooklm share remove [email protected] -y # Skip confirmationOptions (all commands):
-n, --notebook ID- Specify notebook (uses current if not set, supports partial IDs)--json- Output as JSON
Permission levels:
| Level | Access |
|---|---|
viewer |
Read-only access (default) |
editor |
Can edit notebook content |
View levels:
| Level | Viewers can see |
|---|---|
full |
Chat, sources, and notes |
chat |
Chat interface only |
Label: `list`, `sources`, `generate`, `create`, `rename`, `emoji`, `add`, `remove`, `delete`
Manage source labels — AI-generated (or manually named) topic groupings of a notebook's sources.
# List labels (with member ids + resolved source titles)
notebooklm label list
# Expand a label to its source objects
notebooklm label sources Papers
# AI-group sources into topic labels (the UI's "Reorganize")
notebooklm label generate # --scope unlabeled (default, safe)
notebooklm label generate --scope all -y # destructive: wipe + regenerate every label
# Create an empty, manually-named label
notebooklm label create "Papers" --emoji 📄
# Rename a label (preserves its emoji) / set its emoji
notebooklm label rename Papers "Research Papers"
notebooklm label emoji Papers 📚
# Add source(s) to a label (append; existing members preserved)
notebooklm label add Papers src123 src456
# Remove source(s) from a label (un-assign only; source stays in the notebook)
notebooklm label remove Papers src123
# Delete one or more labels (the label only, not its sources)
notebooklm label delete Papers -yOptions (all commands):
-n, --notebook ID- Specify notebook (uses current if not set, supports partial IDs)--json- Output as JSON
Confirmation gates:
label generate --scope allwipes and regenerates every label with new ids — requires-y/--yes(or an interactive prompt).label deleterequires-y/--yes(or an interactive prompt).
Name-or-ID resolution: `` arguments accept a label id (or partial prefix) or an exact label name. An ambiguous name lists the matching ids (with emoji + source count) so you can re-run with the id.
Selecting sources by label: notebooklm source list --label lists only the sources in a label — a read-only saved-selection filter.
Authentication: `auth check`
Diagnose authentication issues by validating storage file, cookies, and optionally testing token fetch.
notebooklm auth check [OPTIONS]Options:
--test- Also test token fetch from NotebookLM (makes network request)--passive- With--test, validate read-only: never runNOTEBOOKLM_REFRESH_CMD, rotate cookies, or write to disk. Use for unattended readiness/health checks that must not mutate state or race real work. No effect without--test.--json- Output as JSON (useful for scripts)
Examples:
# Quick local validation
notebooklm auth check
# Full validation with network test
notebooklm auth check --test
# Read-only readiness probe (no refresh cmd, no cookie write) for a health check
notebooklm auth check --test --passive
# JSON output for automation
notebooklm auth check --jsonChecks performed:
- Storage file exists and is readable
- JSON structure is valid
- Required cookies (
SID+__Secure-1PSIDTS) are present (the Tier 1MINIMUM_REQUIRED_COOKIESset). The loader also evaluates the warning-only secondary-binding check —OSID, orAPISID+SAPISIDtogether with bareLSID— butauth checkdoes not mark a Tier 2 warning as failure; see auth-cookie-lifecycle.md - Cookie domains are correct (.google.com vs regional)
- (With
--test) Token fetch succeeds
Output shows:
- Authentication source (file path or environment variable)
- Which cookies were found and from which domains
- Detailed cookie breakdown by domain (highlighting key auth cookies)
- Token lengths when using
--test
Use cases:
- Debug "Not logged in" errors
- Verify auth setup in CI/CD environments
- Check if cookies are from correct domain (regional vs .google.com)
- Diagnose NOTEBOOKLM_AUTH_JSON environment variable issues
Exit codes: auth check exits 0 only when every executed check passes
and non-zero (1) when any executed check fails — in both text and --json
modes. A skipped check does not count as a failure: without --test, the token
fetch is skipped, so the exit status reflects only the local cookie checks.
Unattended monitors should therefore rely on the exit code (not on parsing the
table). To gate readiness on a real token fetch, run notebooklm auth check --test and check the exit status. See CLI Exit-Code Convention.
Authentication: `auth refresh`
One-shot keepalive and recovery: open a file-backed session, trigger the layer-1 SIDTS rotation poke against accounts.google.com, persist rotated cookies, and exit. If storage is absent but the exact sibling master_token.json exists, it mints the initial file and passively validates it once; --verify reuses that result. Existing cookies that redirect to Google sign-in automatically try a sibling master-token re-mint; --allow-headless permits layer-3 browser recovery first. Healthy storage stays on the cheap path. When a Playwright storage state lacks in-band notebooklm.account metadata, auth refresh also repairs it if account discovery is unambiguous. It does not replace existing metadata; use login --browser-cookies --account EMAIL to re-bind a profile that already points at the wrong account. Designed for direct use or OS scheduling (launchd / systemd / cron / Task Scheduler / k8s CronJob).
notebooklm auth refresh [OPTIONS]Options:
--browser-cookies,--browser-cookie- Re-extract cookies from an installed browser and match the current profile's account (the unified in-bandstorage_state.jsonrecord — a pre-v0.5.0 profile's account, if it's still only in the legacy siblingcontext.json, is promoted in-band automatically on read). This repairs account routing when browser account order changes after another account logs out. Accepts the same scoped syntax aslogin:chrome::for one Chromium profile, andfirefox::<container-name>orfirefox::nonefor one Firefox container.--include-domains LABEL[,LABEL...]- Forward to the browser-cookie reader (only meaningful with--browser-cookies). Same syntax asnotebooklm login --include-domains.--quiet,-q- Suppress success output; print only on error (cron-friendly)--verify- After refreshing, run a read-only passive token fetch to confirm the resulting cookies actually authenticate; exit non-zero if they still fail. A missing-storage master-token bootstrap always performs this validation and--verifyreuses its result. Especially valuable with--browser-cookies, which rewrites the cookie jar but does not otherwise verify it.--allow-headless- Permit layer-3 browser recovery for this invocation when stored cookies are fully expired. Uses the storage-bound persisted browser profile or loopbackNOTEBOOKLM_HEADLESS_REAUTH_CDP_URL, and does not launch or attach unless the ordinary refresh fails. Incompatible with--browser-cookies.--json- Emit one structured success or error document. On success the keys arestatus,storage_path, andverified.
Cadence: 15-20 minutes is the recommended interval. Tighter is wasteful (the 60 s mtime guard would skip it anyway); significantly looser may cross the __Secure-1PSIDTS server-side validity window for your account/region.
Requires file-backed authentication. auth refresh refuses NOTEBOOKLM_AUTH_JSON because inline JSON has no writable backing store. A root --storage PATH is an explicit file-backed override and wins over that environment variable for the invocation.
Exit codes:
0- the auth path completed without raising. The rotation POST is best-effort: exit 0 also covers (a) the 60 s mtime guard skipping the POST, (b)NOTEBOOKLM_DISABLE_KEEPALIVE_POKE=1being set, (c) another process holding the cross-process rotate lock, and (d) a transienthttpxerror during the POST being caught and logged at DEBUG. Treat exit 0 as "no error" rather than "rotation occurred." For verification, enableNOTEBOOKLM_LOG_LEVEL=DEBUGand check for theRotateCookieslog line.1- a typed command failure occurred (for example, an incompatible option, a failed master-token mint, or a failed post-refresh passive token fetch). The OS scheduler's next firing is the retry mechanism; this command does not retry in-process.2- an unexpected error occurred. Missing storage without a sibling token and malformed existing storage retain this existing contract; neither bootstraps.
Examples:
# One-shot refresh against the default profile
notebooklm auth refresh
# Refresh a named profile (works with --profile / NOTEBOOKLM_PROFILE)
notebooklm --profile work auth refresh
# Permit browser-backed L3 only if ordinary recovery reaches a login redirect
notebooklm --profile work auth refresh --allow-headless
# Re-extract from Chrome and repair account routing if browser account order changed
notebooklm --profile work auth refresh --browser-cookies chrome
notebooklm --profile work auth refresh --browser-cookies 'chrome::Profile 1'
# Quiet variant for cron / systemd
notebooklm --profile work auth refresh --quietPairs with:
NOTEBOOKLM_REFRESH_CMDfor in-process auth-expiry recovery (covers the case whereauth refreshitself fails because cookies have already expired beyond rotation)keepalive=<seconds>onNotebookLMClientfor in-process long-lived workers (no OS scheduler needed)
See Troubleshooting for full per-OS scheduler recipes (launchd plist, systemd user timer, cron, Task Scheduler, k8s CronJob).
Authentication: `auth inspect`
List Google accounts visible to a browser's cookie store. Read-only — never writes to disk. Use this before notebooklm login --browser-cookies --account to see which account emails are available. Chromium-family browsers fan out across populated user profiles by default; use chrome:: to inspect only one profile.
notebooklm auth inspect [OPTIONS]Options:
--browser TEXT- Browser to read cookies from (chrome,firefox,brave,edge,safari,arc, ...).autopicks the first one rookiepy can read. Usechrome::for one Chromium profile, orfirefox::<container-name>/firefox::nonefor one Firefox container. Requirespip install "notebooklm-py[cookies]".--include-domains LABEL[,LABEL...]- Opt in to enumerating accounts via sibling-product cookies (same syntax asnotebooklm login --include-domains). By default this command consults only required Google auth cookies, which is sufficient for account discovery on every tested path.--json- Output as JSON
Examples:
notebooklm auth inspect --browser chrome
notebooklm auth inspect --browser 'chrome::Profile 1' --json
notebooklm auth inspect --browser firefox --jsonAuthentication: `auth logout`
Log out by clearing saved authentication. Removes both the saved cookie file (storage_state.json) and the cached browser profile. With explicit --storage, an arbitrary unowned browser sidecar is left intact; canonical managed profile layouts are still removed. After logout, run notebooklm login to authenticate with a different Google account.
notebooklm auth logoutExamples:
notebooklm auth logout # Clear auth for active profile
notebooklm -p work auth logout # Clear auth for 'work' profile
notebooklm --storage A.json auth logout # Clear the override auth fileSession: `completion`
Print the shell completion script for bash, zsh, or fish. Pipe the output into a file your shell sources at startup; once the script is loaded, Click handles the _NOTEBOOKLM_COMPLETE env-var protocol automatically.
notebooklm completionInstall (one-time):
# zsh — drop the script anywhere on $fpath; the file MUST be named _notebooklm
notebooklm completion zsh > ~/.zfunc/_notebooklm
# (ensure ~/.zfunc is on $fpath in ~/.zshrc, then `autoload -Uz compinit && compinit`)
# bash — source from ~/.bashrc
notebooklm completion bash > ~/.notebooklm-complete.bash
echo 'source ~/.notebooklm-complete.bash' >> ~/.bashrc
# fish — drop into the system completions directory
notebooklm completion fish > ~/.config/fish/completions/notebooklm.fishID-aware tab completion: Once the script is installed, -n/--notebook, -s/--source, and -a/--artifact complete from the active profile's live IDs:
notebooklm ask -n <TAB> # lists notebook IDs (filtered by what you've typed)
notebooklm ask -s <TAB> # lists sources in the active notebook
notebooklm download audio -a <TAB> # lists artifacts in the active notebookNote: commands that take a positional source / artifact / notebook ID (e.g. source delete , artifact get , use ) currently complete only after the option / argument is fully typed — Click's shell_complete= is attached to the flag-style -n/--notebook, -s/--source, -a/--artifact declarations enumerated below.
For -s and -a the active notebook is resolved with the same precedence the command body uses: -n/--notebook flag value already on the line > NOTEBOOKLM_NOTEBOOK env var > the persisted notebooklm use context. With no resolvable notebook (and on any auth / network failure), the completer returns no suggestions silently — it never prints a traceback into your terminal.
Print-only by design: the command never writes to your shell config; you decide where the script lands. This keeps the install path discoverable and avoids surprising shutdowns of existing completion setups.
Source: `add` — how an argument is classified
With no --type, source add decides in this order:
- URL-shaped (contains
://) → aurloryoutubesource. - The path exists on disk → a
filesource, uploaded. This is an existence
check, not an extension check, so a real file is uploaded whatever it is
named —deck.pptx,./deck.pptxandnotesall work. - Otherwise → a
textsource, ingesting the argument itself as content.
Step 3 is where a typo bites: source add dekc.pptx adds a source whose entire
content is the string dekc.pptx. To make that visible, an argument that looks
like a file but does not exist is added with a warning:
$ notebooklm source add dekc.pptx
warning: 'dekc.pptx' looks like a path but does not exist; ingesting as inline
text. Pass --type text to suppress this warning, or check the path for typos.An argument looks like a file when it contains a slash, or when its extension is
one the upload accepts — .pdf .txt .md .markdown .doc .docx .pptx.rtf .odt .csv .tsv .epub — or is HTML-family (.html .htm .xhtml.xht, which the upload endpoint rejects with convert-first guidance), or is.ppt (file-shaped, but legacy PowerPoint has never been proven uploadable, so
it earns the warning without being routed to the uploader). That list is derived
from the single upload-support declaration the Drive download-and-upload router
also reads, so a newly supported file type earns its warning at the same time it
becomes uploadable, rather than drifting behind it (#2202).
Pass --type text to opt out of detection entirely, or --type file to require
the upload path (which then fails loudly on a missing file instead of falling
back to text).
Source: `add` — `--follow-symlinks` security gate
File-source uploads reject symlinks by default. If the path you pass (or any ancestor directory) is a symbolic link, source add refuses the upload rather than silently following it — a workspace symlink could otherwise exfiltrate the file it points at (e.g. ~/Downloads/foo.pdf -> /etc/passwd). Pass --follow-symlinks to opt in explicitly.
URL sources reject internal hosts (localhost, loopback, private IP ranges,
and link-local addresses) by default so the CLI cannot be used as an SSRF
trampoline. Pass --allow-internal only for a deliberate local NotebookLM test;
non-HTTP(S) schemes remain rejected even with the flag.
Python equivalent:
client.sources.add_file(nb_id, path, title=...). The symlink gate is a CLI-only safeguard; callers using the Python API are responsible for resolving symbolic links before passing the path.
# Default — symlink rejected with: "Path is a symlink; pass --follow-symlinks to follow it explicitly."
notebooklm source add ./link-to-doc.pdf --type file
# Opt-in: resolve the symlink, then upload the resolved target
notebooklm source add ./link-to-doc.pdf --type file --follow-symlinksThe same gate applies on the explicit --type file path (no auto-detect), so typing the source type as file does not bypass the check.
Source: `add` `--mime-type` (file sources)
The --mime-type flag on notebooklm source add (file-source path) is a
supported parameter. It overrides the filename-extension inference and
sets the content-type header used for the resumable upload handshake. Omit it
to let the MIME type be inferred from the file extension.
# Inferred from the extension (typical case — no flag needed)
notebooklm source add ./report.pdf --type file
# Override inference (e.g. an extensionless export, or a misnamed file)
notebooklm source add ./report.bin --type file --mime-type application/pdfNote: The same
--mime-typeflag onnotebooklm source add-drive
(Google Drive sources) selects betweengoogle-doc/google-slides/google-sheets/The
<title>you pass tosource add-driveis ignored by NotebookLM for
native Drive imports — the backend re-derives the display title from live Drive
metadata, so the source keeps its Drive name regardless. Runnotebooklm source rename "<title>"after the add if you need a specific
title.Historical: an earlier release treated the file-source
--mime-typeas a
deprecated no-op. It was re-wired to set the upload content-type and is no
longer deprecated; theNOTEBOOKLM_QUIET_DEPRECATIONSnotice it used to
emit is gone.
Source: `add-research`
Perform AI-powered research and add discovered sources to the notebook.
Python equivalent:
client.research.start(nb_id, query, source="web", mode="deep")followed byclient.research.wait_for_completion(...)/client.research.import_sources(...).
notebooklm source add-research [query] [OPTIONS]Options:
-n, --notebook ID- Notebook ID (uses current if not set)--mode [fast|deep]- Research depth (default: fast)--from [web|drive]- Search source (default: web)--import-all- Automatically import all found sources (works with blocking mode)--cited-only- With--import-all, import only cited sources--no-wait- Start research and return immediately (non-blocking)--timeout SECONDS- Per-phase seconds budget for (a) the research-completion poll loop and (b) the--import-allretry loop (default: 1800). Each phase gets the full budget independently, so worst-case total wall time is up to 2× this value. Matchesresearch wait --timeoutsemantics. Before 0.4.2 the in-line poll was hardcoded to 5 minutes, so deep research that ran longer was silently abandoned and left an "Add sources?" modal hanging in the NotebookLM web UI — bump--timeoutfor long deep-research runs.--prompt-file PATH- Read query from a file (or-for stdin) instead of the positional argument
Note:
--mode deepis only supported with--from web(the default). Combining--mode deep --from driveis rejected by the backend withValidationError("Deep Research only supports Web sources.")— for Drive, stick with--mode fast.
Examples:
# Fast web research (blocking)
notebooklm source add-research "Quantum computing basics"
# Drive research (fast mode only — Drive does not support --mode deep)
notebooklm source add-research "Project Alpha" --from drive
# Non-blocking deep research for agent workflows (web only — see note above)
notebooklm source add-research "AI safety papers" --mode deep --no-wait
# Import only cited sources for tighter relevance
notebooklm source add-research "topic" --import-all --cited-only
# Bounded import-retry budget for large result sets
notebooklm source add-research "AI papers" --mode deep --import-all --timeout 3600
# Read long query from file (or stdin via '-')
notebooklm source add-research --prompt-file research_query.txt --mode deep
echo "very long query..." | notebooklm source add-research --prompt-file -Research: `status`
Check research status for the current notebook (non-blocking).
Python equivalent:
client.research.poll(nb_id).
notebooklm research status [OPTIONS]Options:
-n, --notebook ID- Notebook ID (uses current if not set)--json- Output as JSON
Output states:
- No research running - No active research session
- Research in progress - Deep research is still running
- Research completed - Shows query, found sources, and summary
Examples:
# Check status
notebooklm research status
# JSON output for scripts/agents
notebooklm research status --jsonResearch: `wait`
Wait for research to complete (blocking).
Python equivalent:
client.research.wait_for_completion(nb_id), then optionally callclient.research.import_sources(...)(matches the CLI's--import-all/--cited-onlybehavior).
notebooklm research wait [OPTIONS]Options:
-n, --notebook ID- Notebook ID (uses current if not set)--timeout SECONDS- Per-phase budget (default: 1800, matchingsource add-research). Deep runs regularly exceed the former 300s default — 374s live, 358s in theresearch_deep_poll_longcassette; fast runs settle in seconds, so the cap only ever binds on deep. With--import-allthe poll loop and the import-retry loop each get the full budget independently, so worst-case wall time is up to 2× this value.--interval SECONDS- Seconds between status checks (default: 5)--import-all- Import all found sources when done--cited-only- With--import-all, import only cited sources--json- Output as JSON
Examples:
# Basic wait
notebooklm research wait
# Wait longer for deep research
notebooklm research wait --timeout 600
# Wait and auto-import sources
notebooklm research wait --import-all
# Import only cited sources
notebooklm research wait --import-all --cited-only
# JSON output for agent workflows
notebooklm research wait --json --import-allUse case: Primarily for LLM agents that need to wait for non-blocking deep research started with source add-research --no-wait.
Research: `import`
Import a completed research run's sources — without blocking.
Python equivalent:
client.research.import_sources_with_verification(nb_id, run_id, sources), after polling the run tocompletedyourself.
notebooklm research import [OPTIONS]Options:
-n, --notebook ID- Notebook ID (uses current if not set)--run-id ID- Run to import. Omit it and the notebook's single research run is used; when a
notebook has more than one run this errors rather than guessing which you meant, so pass
the id (research statusshows it)--cited-only- Import only report-cited sources (all of them, if no citation resolves —cited_only_fallbacksays which happened)--timeout SECONDS- Seconds budget for the import retry loop (default: 1800)--max-sources N- Import at most N sources (applied after--cited-onlynarrows)--allow-duplicate- Re-add sources whose URL is already in the notebook--json- Output as JSON
Never waits for the run. This is the counterpart to research wait --import-all: if the run is still in progress (or failed, or found nothing), the command exits 1 with an explanation instead of polling. The import RPC itself is not instant — IMPORT_RESEARCH commonly outlives a single client timeout on deep payloads and is retried with reconciliation, bounded by --timeout (default 1800s, same vocabulary as research wait --timeout). That makes the fully composable flow expressible for the first time — you own the cadence:
notebooklm source add-research "AI safety" --mode deep --no-wait # returns immediately
notebooklm research status # your loop, your interval
notebooklm research import # imports, returnsIdempotent — with two documented limits. A source whose URL is already in the notebook is reported as already-present rather than duplicated, so a repeat import reads as "0 new, N already present" instead of looking like a no-op. Under --json that split is imported / imported_sources versus already_present / already_present_sources, and status is already_imported when nothing new landed.
The dedupe is by URL against a snapshot taken just before the import, so: a deep run's report row has no URL and is re-imported on every run, and if the snapshot call itself fails the filter is skipped entirely (the import still proceeds). Both are properties of import_sources_with_verification, shared with research wait --import-all and the MCP tool. If you interrupt an import, check source list before re-running it rather than assuming the re-run is a no-op.
Examples:
# Import the notebook's current completed run
notebooklm research import
# Pin a specific run and take only the cited sources
notebooklm research import --run-id <run_id> --cited-only
# Cap the import, JSON output for agent workflows
notebooklm research import --max-sources 10 --jsonResearch: `cancel`
Cancel an in-flight research run.
Python equivalent:
client.research.cancel(nb_id, run_id).
notebooklm research cancel RUN_ID [OPTIONS]Arguments:
RUN_ID- The run's poll-level id — thetask_idshown byresearch status. For deep research this is thereport_idreturned bysource add-research, not the deep starttask_id(which is a sessionId and will not cancel anything).
Options:
-n, --notebook ID- Notebook ID (uses current if not set)--json- Output as JSON
Examples:
# Cancel a run (find the run id with `research status`)
notebooklm research cancel <run_id>
# JSON output for agent workflows
notebooklm research cancel <run_id> --jsonFire-and-forget: the server reports neither success nor failure for a cancel and does not validate the run id, so this command cannot confirm the cancel took effect. Run
research statusafterward — a cancelled in-progress run shows asfailed.
Generate: `audio`
Generate an audio overview (podcast).
Python equivalent:
client.artifacts.generate_audio(nb_id, ...).
notebooklm generate audio [description] [OPTIONS]Options:
-n, --notebook ID- Notebook ID (uses current if not set)--format [deep-dive|brief|critique|debate]- Podcast format (default: deep-dive)--length [short|default|long]- Duration (default: default)--language LANG- Output language (precedence:--language>NOTEBOOKLM_HLenv > config >'en')-s, --source ID- Limit to specific source IDs (repeatable, uses all if not specified)--wait / --no-wait- Wait for completion (default:--no-wait)--timeout SECONDS- Maximum seconds to wait (default: 1200; no-op without--wait)--interval SECONDS- Seconds between status checks (default: 2; no-op without--wait)--retry N- Retry N times with exponential backoff on rate limit--json- Output as JSON (returnstask_idandstatus)--prompt-file PATH- Read description from a file (or-for stdin); mutually exclusive with positional argument
Examples:
# Basic podcast (starts async, returns immediately)
notebooklm generate audio
# Debate format with custom instructions
notebooklm generate audio "Compare the two main viewpoints" --format debate
# Generate and wait for completion
notebooklm generate audio "Focus on key points" --wait
# Generate using only specific sources
notebooklm generate audio -s src_abc -s src_def
# JSON output for scripting/automation
notebooklm generate audio --json
# Output: {"task_id": "abc123...", "status": "pending"}
# Read long instructions from a file
notebooklm generate audio --prompt-file instructions.txt --format debateGenerate: `video`
Generate a video overview. Use --format cinematic for AI-generated documentary footage (Veo 3); cinematic videos ignore --style and take ~30-40 min (requires AI Ultra). For non-cinematic formats, see generate cinematic-video for the alias subcommand.
Python equivalent:
client.artifacts.generate_video(nb_id, ...).
notebooklm generate video [description] [OPTIONS]Options:
-n, --notebook ID- Notebook ID (uses current if not set)--format [explainer|brief|cinematic|short]- Video format (shortis a vertical short-form video with a fixed style)--style [auto|custom|classic|whiteboard|kawaii|anime|watercolor|retro-print|heritage|paper-craft]- Visual style (not supported with--format cinematicorshort)--style-prompt TEXT- Custom visual style prompt (required when--style custom; rejected with--format cinematicorshort)--language LANG- Output language (precedence:--language>NOTEBOOKLM_HLenv > config >'en')-s, --source ID- Limit to specific source IDs (repeatable, uses all if not specified)--wait / --no-wait- Wait for completion (default:--no-wait)--timeout SECONDS- Maximum seconds to wait (default: 1800;--format cinematicdefaults to 3600; no-op without--wait)--interval SECONDS- Seconds between status checks (default: 2; no-op without--wait)--retry N- Retry N times with exponential backoff on rate limit--json- Output as JSON (returnstask_idandstatus)--prompt-file PATH- Read description from a file (or-for stdin); mutually exclusive with positional argument
Examples:
# Kid-friendly explainer
notebooklm generate video "Explain for 5 year olds" --style kawaii
# Custom visual style
notebooklm generate video --style custom --style-prompt "hand-drawn diagrams"
# Cinematic format (Veo 3)
notebooklm generate video --format cinematic "documentary overview"
# Professional style
notebooklm generate video --style classic --wait
# Generate from specific sources only
notebooklm generate video -s src_123 -s src_456
# JSON output for scripting/automation
notebooklm generate video --jsonNote: Passing a non-cinematic --format to the cinematic-video alias exits 2 with a UsageError. Use generate video --format <other> for non-cinematic formats.
Generate: `revise-slide`
Revise an individual slide in an existing slide deck using a natural-language prompt.
Python equivalent:
client.artifacts.revise_slide(nb_id, artifact_id, slide_index, instructions).
notebooklm generate revise-slide [description] --artifact --slide N [OPTIONS]Required Options:
-a, --artifact ID- The slide deck artifact ID to revise--slide N- Zero-based index of the slide to revise (0 = first slide)
Optional:
-n, --notebook ID- Notebook ID (uses current if not set)--wait / --no-wait- Wait for completion (default:--no-wait)--timeout SECONDS- Maximum seconds to wait (default: 300; no-op without--wait)--interval SECONDS- Seconds between status checks (default: 2; no-op without--wait)--retry N- Retry N times with exponential backoff on rate limit--json- Machine-readable output--prompt-file PATH- Read description from a file (or-for stdin); mutually exclusive with positional argument
Examples:
# Revise the first slide
notebooklm generate revise-slide "Move the title up" --artifact art123 --slide 0
# Revise the fourth slide and wait for completion
notebooklm generate revise-slide "Remove taxonomy table" --artifact art123 --slide 3 --waitNote: The slide deck must already be fully generated before using revise-slide. Use artifact list to find the artifact ID.
Generate: `report`
Generate a text report (briefing doc, study guide, blog post, or custom).
Python equivalent:
client.artifacts.generate_report(nb_id, report_format=..., ...).
notebooklm generate report [description] [OPTIONS]Options:
-n, --notebook ID- Notebook ID (uses current if not set)--format [briefing-doc|study-guide|blog-post|custom]- Report format (default: briefing-doc)--append TEXT- Append extra instructions to the built-in prompt (no effect with--format custom)--language LANG- Output language (precedence:--language>NOTEBOOKLM_HLenv > config >'en')-s, --source ID- Limit to specific source IDs (repeatable, uses all if not specified)--wait / --no-wait- Wait for completion (default:--no-wait)--timeout SECONDS- Maximum seconds to wait (default: 300; no-op without--wait)--interval SECONDS- Seconds between status checks (default: 2; no-op without--wait)--retry N- Retry N times with exponential backoff on rate limit--json- Output as JSON--prompt-file PATH- Read description from a file (or-for stdin); mutually exclusive with positional argument
Examples:
notebooklm generate report --format study-guide
notebooklm generate report "Executive summary for stakeholders" --format briefing-doc
# Generate report from specific sources
notebooklm generate report --format study-guide -s src_001 -s src_002
# Custom report with description (auto-selects custom format)
notebooklm generate report "Create a white paper analyzing the key trends"
# Append instructions to a built-in format
notebooklm generate report --format study-guide --append "Target audience: beginners"
notebooklm generate report --format briefing-doc --append "Focus on AI trends, keep it under 2 pages"
# Read a long custom prompt from file (auto-selects custom format)
notebooklm generate report --prompt-file custom_report.txtArtifact: `list`, `get`, `get-prompt`, `rename`, `delete`, `export`, `poll`, `wait`, `retry`, `suggestions`
Manage existing artifacts (audio, video, slide decks, quizzes, reports, etc.). Every subcommand resolves the notebook via the standard precedence (-n/--notebook flag > NOTEBOOKLM_NOTEBOOK env > active context).
Python equivalent:
client.artifacts.list/get/get_prompt/rename/delete/poll_status/wait_for_completion/retry_failed/suggest_reports(...)for management;export_report/export_data_table/export(...)for export.
notebooklm artifact <list|get|get-prompt|rename|delete|export|poll|wait|retry|suggestions> [OPTIONS]Common options (all subcommands):
-n, --notebook ID- Notebook ID (uses current if not set; supports partial IDs)
Per-subcommand options:
| Subcommand | Required arguments | Options |
|---|---|---|
list |
(none) | --type [all|audio|video|slide-deck|quiz|flashcard|infographic|data-table|mind-map|report|fantasy-map|file], --limit N (default: unlimited), --no-truncate, --json |
get |
ARTIFACT_ID |
--json |
get-prompt |
ARTIFACT_ID |
--json |
rename |
ARTIFACT_ID NEW_TITLE |
--json |
delete |
ARTIFACT_ID |
-y/--yes (skip confirmation), --json |
export |
ARTIFACT_ID |
--title TEXT (required), --type [docs|sheets] (default: docs), --json |
poll |
TASK_ID (from generate <type>) |
--json |
wait |
ARTIFACT_ID |
--timeout SECONDS (default: 300), --interval SECONDS (default: 2), --json |
retry |
ARTIFACT_ID |
--wait (block until terminal), --timeout SECONDS (default: 300), --interval SECONDS (default: 2), --json |
suggestions |
(none) | --json |
Examples:
# Filter artifact list to one notebook and one type, JSON for scripting
notebooklm artifact list --notebook nb_abc --type audio --json
# Inspect a single artifact (partial ID OK)
notebooklm artifact get art123 --json
# Show the prompt an artifact was generated from
notebooklm artifact get-prompt art123 --json
# Rename an artifact
notebooklm artifact rename art123 "Final cut"
# Delete without prompting
notebooklm artifact delete art123 -y --json
# Export a report to Google Docs (title is required)
notebooklm artifact export art123 --title "Climate briefing" --type docs
# Poll a task immediately after a generate call returns
notebooklm generate audio --json # -> { "task_id": "task_abc...", "status": "..." }
notebooklm artifact poll task_abc
# Block until the artifact finishes (or 600s elapses)
notebooklm artifact wait art123 --timeout 600 --json
# Retry a failed artifact in place (UI "Retry"); --wait blocks until terminal
notebooklm artifact retry art123 --wait
# Get AI-suggested report topics for the active notebook
notebooklm artifact suggestions --json
pollvswait: The API returns one identifier that doubles as bothtask_id(right aftergenerate) andartifact_id(once it appears inartifact list).pollis a one-shot status check that does not prefix-match againstartifact list(use it immediately aftergenerate);waitblocks until terminal status and accepts partial IDs that resolve againstartifact list. See the quick-reference note above for full details.
Download: `audio`, `video`, `slide-deck`, `infographic`, `report`, `mind-map`, `data-table`
Download generated artifacts to your local machine.
Python equivalent: the per-type
client.artifacts.download_audio/video/slide_deck/infographic/report/mind_map/data_table(nb_id, path, ...)methods.
notebooklm download <type> [OUTPUT_PATH] [OPTIONS]Artifact Types and Output Formats:
| Type | Default Extension | Description |
|---|---|---|
audio |
.m4a |
Audio overview (podcast) — AAC audio in an MP4 container |
video |
.mp4 |
Video overview |
slide-deck |
.pdf or .pptx |
Slide deck as PDF (default) or PowerPoint |
infographic |
.png |
Infographic image |
report |
.md |
Report as Markdown (Briefing Doc, Study Guide, etc.) |
mind-map |
.json |
Mind map as JSON tree structure |
data-table |
.csv |
Data table as CSV (UTF-8 with BOM for Excel) |
Options:
--all- Download all artifacts of this type--latest- Download only the most recent artifact (default if no ID/name provided)--earliest- Download only the oldest artifact--name NAME- Download artifact with matching title (supports partial matches)-a, --artifact ID- Select specific artifact by ID (supports partial IDs)--dry-run- Show what would be downloaded without actually downloading--force- Overwrite existing files--no-clobber- Don't overwrite existing files (a single download fails;--allskips them; default is auto-rename)--format [pdf|pptx]- Slide deck format (slide-deck command only, default: pdf)--json- Output result in JSON format
Examples:
# Download the latest podcast
notebooklm download audio ./podcast.m4a
# Download all infographics
notebooklm download infographic --all
# Download a specific slide deck by name
notebooklm download slide-deck --name "Final Presentation"
# Download slide deck as PPTX (editable PowerPoint)
notebooklm download slide-deck --format pptx
# Preview a batch download
notebooklm download audio --all --dry-run
# Download a report as markdown
notebooklm download report ./study-guide.md
# Download mind map as JSON
notebooklm download mind-map ./concept-map.json
# Download data table as CSV (opens in Excel)
notebooklm download data-table ./research-data.csvDownload: `quiz`, `flashcards`
Download quiz questions or flashcard decks in various formats.
Python equivalent:
client.artifacts.download_quiz(nb_id, path, output_format="json|markdown|html")anddownload_flashcards(...)with the same signature.
notebooklm download quiz [OUTPUT_PATH] [OPTIONS]
notebooklm download flashcards [OUTPUT_PATH] [OPTIONS]Options:
-n, --notebook ID- Notebook ID (uses current context if not set)--format FORMAT- Output format:json(default),markdown, orhtml-a, --artifact ID- Select specific artifact by ID
Output Formats:
- JSON - Structured data preserving full API fields (answerOptions, rationale, isCorrect, hint)
- Markdown - Human-readable format with checkboxes for correct answers
- HTML - Raw HTML as returned from NotebookLM
Examples:
# Download quiz as JSON
notebooklm download quiz quiz.json
# Download quiz as markdown
notebooklm download quiz --format markdown quiz.md
# Download flashcards as JSON (normalizes f/b keys to front/back)
notebooklm download flashcards cards.json
# Download flashcards as markdown
notebooklm download flashcards --format markdown cards.md
# Download flashcards as raw HTML
notebooklm download flashcards --format html cards.htmlNote: `list`, `create`, `get`, `save`, `rename`, `delete`
Read, create, and update notebook notes (the "Notes" panel in the web UI). Mind-map artifacts surface as notes in the underlying API but are filtered out of note list — use artifact list --type mind-map for those.
Python equivalent:
client.notes.list/create/get/update/delete(...). The CLI'snote saveandnote renameboth map toclient.notes.update(...)(title and/or content). Mind-map helpers (list_mind_maps,delete_mind_map) live on the same API.
notebooklm note <list|create|get|save|rename|delete> [OPTIONS]Common options (all subcommands):
-n, --notebook ID- Notebook ID (uses current if not set; supports partial IDs)--json- Machine-readable output
Per-subcommand options:
| Subcommand | Required arguments | Type-specific options |
|---|---|---|
list |
(none) | — |
create |
[CONTENT] (positional or stdin via -) |
--content TEXT (or - for stdin; mutually exclusive with positional), -t/--title TEXT (default: "New Note") |
get |
NOTE_ID |
— |
save |
NOTE_ID |
--title TEXT, --content TEXT |
rename |
NOTE_ID NEW_TITLE |
— |
delete |
NOTE_ID |
-y/--yes (skip confirmation) |
Examples:
# List notes in the active notebook
notebooklm note list --json
# Create a titled note from a positional string
notebooklm note create "Quick thought" -t "Idea: research follow-up"
# Pipe markdown from a file or another command (use `-` to read stdin)
cat draft.md | notebooklm note create -
# Equivalent --content form
cat draft.md | notebooklm note create --content -
# Update an existing note (partial ID OK; only the fields you pass change)
notebooklm note save note_abc --title "Updated title"
notebooklm note save note_abc --content "New body"
# Rename without touching content
notebooklm note rename note_abc "Renamed"
# Delete without prompting
notebooklm note delete note_abc -y --jsonProfile: `list`, `create`, `switch`, `delete`, `rename`
Manage named profiles under $NOTEBOOKLM_HOME/profiles/<name>/. Each profile owns its own storage_state.json, context.json, and browser profile, so multiple Google accounts can coexist. The active profile is selected by -p/--profile, NOTEBOOKLM_PROFILE, or the default_profile field in $NOTEBOOKLM_HOME/config.json (in that precedence). See configuration.md for the full profile model.
notebooklm profile <list|create|switch|delete|rename> [OPTIONS]Per-subcommand options:
| Subcommand | Required arguments | Options |
|---|---|---|
list |
(none) | --json |
create |
NAME |
--json |
switch |
NAME |
--json |
delete |
NAME |
--yes/-y (skip prompt; --confirm is a deprecated alias; the active default profile cannot be deleted), --json |
rename |
OLD_NAME NEW_NAME |
--json |
Examples:
# Enumerate profiles (active flag included in JSON)
notebooklm profile list --json
# Create a profile shell, then authenticate into it
notebooklm profile create work
notebooklm -p work login
# Make 'work' the default for subsequent commands
notebooklm profile switch work
# Rename a profile (does not change `storage_state.json` contents)
notebooklm profile rename work work-old
# Delete a non-active profile without prompting
notebooklm profile delete old-account --yesNote:
profile deleterefuses to remove the currently active default profile. Switch to a different profile first (notebooklm profile switch <other>) and then delete.
Skill: `install`, `status`, `uninstall`, `show`, `package`
Manage the bundled NotebookLM agent-skill template. The skill lives in SKILL.md at the repository root; installing it materializes a copy under one of:
.claude/skills/notebooklm/SKILL.md(Claude Code,--target claude).agents/skills/notebooklm/SKILL.md(universal agent skill directory,--target agents)
--scope selects whether to write into the user's home (~/.claude/skills/..., ~/.agents/skills/...) or the current project (./.claude/skills/..., ./.agents/skills/...).
skill package takes neither --scope nor --target: it builds a ZIP archive (zip root: notebooklm/SKILL.md, version-stamped) for upload via Claude Settings → Capabilities — the supported hand-off for sandboxed agent environments such as Claude Cowork, which cannot run skill install against a local directory.
notebooklm skill [OPTIONS]Options matrix (defaults: --scope user --target all):
| Subcommand | --scope choices |
--target choices |
Default --target |
Other options |
|---|---|---|---|---|
install |
user, project |
all, claude, agents |
all |
--dry-run, --no-clobber, --force (project scope) |
status |
user, project |
all, claude, agents |
all |
--json |
uninstall |
user, project |
all, claude, agents |
all |
— |
show |
user, project |
source, claude, agents |
source |
— |
package |
— | — | — | -o/--output PATH (default ./notebooklm-skill.zip; an existing directory — or a PATH ending in a separator — gets the default filename inside it), --force, --json |
skill show --target source prints the packaged SKILL.md straight out of the wheel (the canonical content); the other show targets read the materialized copy from disk.
Project-scope install hardening:
--dry-runprints the target files and actions without writing.--no-clobbercreates missing targets but skips differing existing files.--forceoverwrites differing project targets.- These flags are project-scope only;
--scope userpreserves the historical always-overwrite behavior.
Examples:
# Install both targets for the current user (default scope+target)
notebooklm skill install
# Install only the Claude Code target into the current project
notebooklm skill install --scope project --target claude --dry-run
notebooklm skill install --scope project --target claude --force
# Inspect what's installed in the user-scope agents directory
notebooklm skill status --scope user --target agents
# Print the packaged skill source (e.g. for piping into another agent's loader)
notebooklm skill show --target source
# Print the installed Claude target verbatim
notebooklm skill show --scope project --target claude
# Remove all installed targets from the current project
notebooklm skill uninstall --scope project --target all
# Build the uploadable archive in the current directory (./notebooklm-skill.zip)
notebooklm skill package
# Write to an explicit file, or drop the default filename into a directory
notebooklm skill package --output dist/notebooklm-v0.8.zip
notebooklm skill package --output dist/
# Overwrite an existing archive, or emit a machine-readable result
notebooklm skill package --output dist/ --force
notebooklm skill package --jsonskill package refuses to overwrite an existing archive unless --force is passed. With --json it prints {"path", "version", "entries", "size_bytes"} on success and the standard error envelope on failure.
Codex does not consume the skill subcommand. In this repository it reads the root AGENTS.md file and invokes the notebooklm CLI or Python API directly.
Source: `add-drive`
Add a Google Drive document, slide deck, sheet, or PDF as a source. The Drive --mime-type selects which Drive document type to import (Google Doc / Slides / Sheets / PDF). This is distinct from the file-source --mime-type documented above, which sets the resumable-upload content-type for a locally-uploaded file.
By-reference vs upload-only: NotebookLM's Drive import only ingests Google-native Docs/Slides/Sheets + PDF by reference — the four
--mime-typechoices above. An upload-only file that merely lives in Drive (e.g.epub/docx/txt/md/rtf/odt/csv/tsv) cannot be imported this way; usesource add-drive-fileinstead, which downloads it server-side (using your session) and uploads it — no local download step needed.Python equivalent:
client.sources.add_drive(nb_id, file_id, title, mime_type=...).
notebooklm source add-drive [OPTIONS] FILE_ID TITLEOptions:
-n, --notebook ID- Notebook ID (uses current if not set; supports partial IDs)--mime-type [google-doc|google-slides|google-sheets|pdf]- Drive document type (default:google-doc)--json- Output as JSON
Examples:
# Default — treat the Drive file as a Google Doc
notebooklm source add-drive 1AbcD...XyZ "Project Brief"
# Import as Google Slides
notebooklm source add-drive 1AbcD...XyZ "Quarterly Deck" --mime-type google-slides
# Import a Drive-hosted PDF
notebooklm source add-drive 1AbcD...XyZ "Whitepaper" --mime-type pdf --jsonSource: `add-drive-file`
Add an upload-only Google Drive file (epub/docx/pptx/txt/md/rtf/odt/csv/tsv/pdf) by id or share URL. NotebookLM's native Drive import (source add-drive) only ingests Google-native Docs/Slides/Sheets + PDF by reference; for every other Drive-hosted file type, this command downloads the file server-side (using your session) and uploads it through the resumable-upload path — a Drive PDF can go either way.
notebooklm source add-drive-file [OPTIONS] DOCUMENT_IDOptions:
-n, --notebook ID- Notebook ID (uses current if not set; supports partial IDs)--title TEXT- Custom title (default: the file's Drive name)--wait- Wait for processing to finish--json- Output as JSON
Examples:
# Download + upload an upload-only Drive file (e.g. a .docx)
notebooklm source add-drive-file 1AbcD...XyZ
# Custom title, wait for processing to complete
notebooklm source add-drive-file 1AbcD...XyZ --title "Meeting Notes" --waitSource: `stale`, `clean`
source stale is a shell-friendly predicate that reports whether a URL/Drive source needs a refresh; source clean removes duplicate, error, or access-blocked sources in bulk.
Python equivalent:
client.sources.check_freshness(nb_id, source_id)returns the same staleness verdict. There is no single bulk "clean" call — combineclient.sources.list(...)with a filter on duplicate/error/blocked status and callclient.sources.delete(...)per match.
notebooklm source stale [OPTIONS] SOURCE_ID
notebooklm source clean [OPTIONS]stale exit codes (standard by default; opt-in inverted predicate via --exit-on-stale):
- Default:
0on success regardless of freshness,1on error. Branch on the JSONstale/freshfields (or stdout text) for the verdict. - With
--exit-on-stale:0if stale (needs refresh),1if fresh — for back-compat with theif … ; then refresh; fishell idiom. - See CLI Exit-Code Convention for details.
stale options: -n/--notebook ID, --exit-on-stale, --json.
clean options: -n/--notebook ID, --dry-run (preview the candidate set), -y/--yes (skip the confirmation prompt), --json.
Examples:
# Refresh a single stale URL source if needed (back-compat shell idiom)
if notebooklm source stale --exit-on-stale src_abc; then
notebooklm source refresh src_abc
fi
# Default semantics: branch on JSON output instead
if [[ "$(notebooklm source stale src_abc --json | jq -r .stale)" == "true" ]]; then
notebooklm source refresh src_abc
fi
# Preview what `clean` would remove
notebooklm source clean --dry-run
# Remove duplicates/errors/blocked sources without prompting
notebooklm source clean -y --jsonCommon Workflows
Research → Podcast
Find information on a topic and create a podcast about it.
# 1. Create a notebook for this research
notebooklm create "Climate Change Research" --use --json
# Output includes: {"active_notebook_id": "...", "notebook": {...}}
# 2. Add a starting source
notebooklm source add "https://en.wikipedia.org/wiki/Climate_change"
# 3. Research more sources automatically (blocking; --import-all retry budget defaults to 1800s)
notebooklm source add-research "climate change policy 2024" --mode deep --import-all
# 4. Generate a podcast
notebooklm generate audio "Focus on policy solutions and future outlook" --format debate --wait
# 5. Download the result
notebooklm download audio ./climate-podcast.m4aResearch → Podcast (Non-blocking with Subagent)
For LLM agents, use non-blocking mode to avoid timeout:
# 1. Create notebook and set it active
notebooklm create "Climate Change Research" --use
# 2. Add initial source
notebooklm source add "https://en.wikipedia.org/wiki/Climate_change"
# 3. Start deep research (non-blocking)
notebooklm source add-research "climate change policy 2024" --mode deep --no-wait
# Returns immediately
# 4. In a subagent, wait for research and import
notebooklm research wait --import-all
# Blocks until complete, then imports sources
# 5. Continue with podcast generation...Research commands:
research status- Check if research is in progress, completed, or not runningresearch wait --import-all- Block until research completes, then import sources
Document Analysis → Study Materials
Upload documents and create study materials.
# 1. Create notebook
notebooklm create "Exam Prep"
notebooklm use
# 2. Add your documents
notebooklm source add "./textbook-chapter.pdf"
notebooklm source add "./lecture-notes.pdf"
# 3. Get a summary
notebooklm summary
# 4. Generate study materials
notebooklm generate quiz --difficulty hard --wait
notebooklm generate flashcards --wait
notebooklm generate report --format study-guide --wait
# 5. Ask specific questions
notebooklm ask "Explain the key concepts in chapter 3"
notebooklm ask "What are the most likely exam topics?"YouTube → Quick Summary
Turn a YouTube video into notes.
# 1. Create notebook and add video
notebooklm create "Video Notes"
notebooklm use
notebooklm source add "https://www.youtube.com/watch?v=VIDEO_ID"
# 2. Get summary
notebooklm summary
# 3. Ask questions
notebooklm ask "What are the main points?"
notebooklm ask "Create bullet point notes"
# 4. Generate a quick briefing doc
notebooklm generate report --format briefing-doc --waitBulk Import
Add multiple sources at once.
# Set active notebook
notebooklm use
# Add multiple URLs
notebooklm source add "https://example.com/article1"
notebooklm source add "https://example.com/article2"
notebooklm source add "https://example.com/article3"
# Add multiple local files (use a loop)
for f in ./papers/*.pdf; do
notebooklm source add "$f"
doneDoctor: `doctor`
Check profile setup, auth status, and migration.
Diagnoses common issues with profiles, authentication, and directory structure. Use --fix to automatically repair detected problems.
notebooklm doctor [OPTIONS]Options:
--fix- Attempt to fix detected issues (e.g. missing directories, broken configurations)--json- Output diagnostic results as a JSON structure for scripting/automation
Examples:
# Check profile and authentication health
notebooklm doctor
# Auto-repair environment issues
notebooklm doctor --fix
# Print diagnostics in machine-readable format
notebooklm doctor --jsonAgent: `agent show`
Show bundled instructions for supported agent environments.
This command displays tailored instructions for different LLM agents (Codex or Claude Code) to help them understand how to use this CLI programmatically.
notebooklm agent show [OPTIONS] {codex|claude}Examples:
# Show instructions for Codex
notebooklm agent show codex
# Show instructions for Claude Code
notebooklm agent show claudeNote:
agent show codexprefers the rootAGENTS.mdfile when running from a source checkout, so the CLI mirrors the same instructions Codex sees in the repository.
Tips for LLM Agents
When using this CLI programmatically:
Three ways to specify notebooks: (a)
notebooklm useto set persistent context, (b)NOTEBOOKLM_NOTEBOOK=env var for the calling shell, or (c)-ndirectly on each command. Resolution precedence:-n/--notebookflag >NOTEBOOKLM_NOTEBOOKenv > active context > error.Generation commands are async by default (except mind-map):
mind-map: Synchronous, completes instantly (no--waitoption)- All others: Return immediately with task ID (default:
--no-wait)
Avoid
--waitfor LLM agents—all async operations can take minutes to 30+ minutes. Useartifact waitin a background task or inform the user to check back later. Long-waits (generate <kind> --wait,artifact wait,source wait) honor SIGINT cleanly: Ctrl-C exits130with a"Resume with: notebooklm artifact poll <task_id>"hint (ornotebooklm source waiton the source path) instead of a Python traceback. Under--jsonthe cancellation surfaces as{"error": true, "code": "CANCELLED", "resume_hint": "..."}.Partial IDs work:
notebooklm use abcmatches any notebook ID starting with "abc". The same prefix-match resolves-n/--notebook,-s/--source, and-a/--artifactarguments across the CLI.Check status: Use
notebooklm statusto see the current active notebook and conversation.Auto-detection:
source addauto-detects content type:- URLs starting with
http→ web source - YouTube URLs → video transcript extraction
- File paths → file upload (PDF, text, Markdown, Word, audio, video, images)
- Path-shaped strings that don't exist on disk (e.g.
./missin.md) still ingest as inline text but emit a stderr warning so a typo doesn't silently masquerade as a successful upload. Pass--type textto suppress the warning.
- URLs starting with
Stdin pipelines: Four surfaces accept the canonical Unix
-placeholder for "read from stdin":notebooklm ask -,notebooklm ask --prompt-file -,notebooklm note create -(or--content -), andnotebooklm source add -(forces text-source path; bypasses path-shaped detection). Same convention applies to the variousgenerate <kind> --prompt-file -flags.Quiet output for CI/cron: Pass
--quiet(root-level) to suppress status output and INFO/WARN log records; errors still surface and structured--jsonpayloads are still emitted.--quietis mutually exclusive with-v/-vv(combining the two raisesUsageErrorwith exit2). For long-running keepalive loops,notebooklm auth refresh --quietis the subcommand-scoped equivalent.Error handling: Commands exit with non-zero status on failure (
1for user/library errors,2for system/unexpected errors per CLI Exit-Code Convention). With--json, failures surface as a typed envelope{"error": true, "code": "<TYPED_CODE>", "message": "..."}on stdout; without--json, error messages go to stderr.Deep research: Use
--no-waitwithsource add-research --mode deepto avoid blocking. Then useresearch wait --import-allin a subagent to wait for completion.Shell completion:
notebooklm completionprints a completion script that enables ID-aware tab completion for-n/--notebook,-s/--source, and-a/--artifactfrom your active profile's live IDs. See thecompletionsection for install snippets.
File: docs/configuration.md
Configuration
Status: Active
Last Updated: 2026-08-05
This guide covers storage locations, environment settings, and configuration options for notebooklm-py.
File Locations
All data is stored under ~/.notebooklm/ by default, organized by profile:
~/.notebooklm/
├── config.json # Global config: default_profile, language
├── profiles/
│ ├── default/ # Default profile (auto-created)
│ │ ├── storage_state.json # Authentication cookies and session
│ │ ├── context.json # CLI context (active notebook, conversation)
│ │ └── browser_profile/ # Persistent Chromium profile
│ ├── work/ # Named profile example
│ │ ├── storage_state.json
│ │ ├── context.json
│ │ └── browser_profile/
│ └── personal/
│ └── ...config.json stores process-wide settings — the persisted default profile
name (under the default_profile key) and the configured interface language
(language). It is global, not per-profile (seesrc/notebooklm/paths.py and src/notebooklm/cli/language_cmd.py).
Legacy layout: If upgrading from a pre-profile version, the first run auto-migrates flat files into profiles/default/. The migration runs under a single-writer filelock rooted at ~/.notebooklm/.migration.lock, so concurrent CLI invocations (e.g., container start-up races) cannot interleave copies — the loser of the lock re-checks the completion marker and no-ops (see src/notebooklm/migration.py). The legacy flat layout continues to work as a fallback.
You can relocate all files by setting NOTEBOOKLM_HOME:
export NOTEBOOKLM_HOME=/custom/path
# All files now go to /custom/path/profiles//Storage State (`storage_state.json`)
Contains the authentication data extracted from your browser session:
{
"cookies": [
{
"name": "SID",
"value": "...",
"domain": ".google.com",
"path": "/",
"expires": 1234567890,
"httpOnly": true,
"secure": true,
"sameSite": "Lax"
},
...
],
"origins": [],
"notebooklm": {
"version": 1,
"account": {
"authuser": 0,
"email": "[email protected]"
}
}
}Cookie requirements (empirically validated via single-, pair-, and three-way ablation; see auth-cookie-lifecycle.md; enforced by _validate_required_cookies() in _auth/cookie_policy.py):
- Tier 1 — strictly required (raises on absence):
SIDAND__Secure-1PSIDTS.SIDis the only individually-required cookie (__Secure-1PSIDTSis removable on its own because Google can re-mint it viaRotateCookies), but the pair-wise check uncovered that as soon as__Secure-1PSIDTSand any one other auth cookie are both missing, Google rejects withAuthentication expired or invalid. The library therefore enforces both up-front. Authoritative value:MINIMUM_REQUIRED_COOKIESin_auth/cookie_policy.py. - Tier 2 — secondary binding (logs a warning if absent): either
OSIDis present, orAPISIDandSAPISIDare present together with bareLSID(theLSIDconjunct is required — the pair alone fails, per the three-way ablation in #1977). Without this, even valid Tier 1 cookies can't authenticate the homepage GET. Logged rather than raised so unverified edge-case flows (e.g. Workspace SSO) aren't broken by a too-strict client check.
In practice: extract the full cookie set via notebooklm login and don't try to subset it. Partial extractions (a known failure mode of browser-cookies tooling under Chrome 127+ App-Bound Encryption) are the leading suspect for "auth expires immediately" reports — see #371.
The optional notebooklm.account block records the Google account route for
multi-account sessions. New notebooklm login runs write it when the active
account can be discovered safely. Older Playwright-created files can be repaired
with notebooklm auth refresh when the storage state maps to one visible
account; if several accounts are visible, usenotebooklm login --browser-cookies --account EMAIL to bind the
intended account explicitly. auth refresh only repairs absent metadata; if
metadata exists but points at the wrong account, re-bind explicitly with the
browser-cookie login path.
Override location:
notebooklm --storage /path/to/storage_state.json listContext File (`context.json`)
Stores the current CLI context, such as the active notebook:
{
"notebook_id": "abc123def456",
"title": "Quarterly review notes",
"is_owner": true,
"role": "owner",
"created_at": "2026-05-01T17:43:21Z"
}Field summary:
notebook_id— currently selected notebook, written bynotebooklm useand read by every command that takes-n/--notebook.title,is_owner,role,created_at— optional notebook metadata captured at selection time sostatus/ display commands don't need an extra round-trip. Omitted when the CLI didn't have the values to write (seeset_current_notebookinsrc/notebooklm/cli/context.py).roleis"owner"/"editor"/"viewer";is_owneris therole == "owner"shorthand and is retained for backward compatibility. Contexts written beforeroleexisted carry onlyis_owner, andstatusfalls back to it.
This file is managed automatically by notebooklm use, notebooklm clear, and the auth commands.
Browser Profile (`browser_profile/`)
A persistent Chromium user data directory used during notebooklm login.
Why persistent? Google blocks automated login attempts. A persistent profile makes the browser appear as a regular user installation, avoiding bot detection.
To reset: Delete the browser_profile/ directory and run notebooklm login again.
With --storage , the browser profile is isolated with that storage file.
The conventional storage_state.json uses a browser_profile/ directory beside
it; any normal-length custom filename uses .browser_profile. For example,A.json and B.json use A.json.browser_profile/ andB.json.browser_profile/ respectively. If that filename would exceed the
255-byte component limit, the canonical storage path maps to a short stablestorage-<hash>.browser_profile/ name; relative and absolute aliases map to the
same directory.
Browser directories newly created for explicit storage contain a.notebooklm-owned marker. login --fresh refuses to recursively delete an
unowned sidecar, and auth logout skips it and reports the preserved path.
Canonical named-profile and legacy browser directories remain managed even when
their storage_state.json is selected explicitly with --storage; an arbitrary
same-named directory outside NOTEBOOKLM_HOME remains unowned. A marker-only
directory does not count as a reusable L3 browser session.
Upgrading: Existing custom-storage browser sessions are not migrated. You
may need one interactive sign-in per storage file. Do not copy or delete the
old shared browser profile unless its account/profile ownership is known.
Master Token (`master_token.json`)
Written only by notebooklm login --master-token (the [headless] extra). Holds
a durable Google master token (mode 0600) that mints/refreshes the profile'sstorage_state.json cookies with no per-session browser. When present beside a
profile's storage_state.json, an expired session re-mints from it
automatically. If storage is absent, notebooklm auth refresh mints it from the
exact sibling token and passively validates once. The legacy login refresh flag
remains an unconditional forced re-mint.
{"version": 1, "email": "...", "android_id": "<hex>", "master_token": "aas_et/..."}⚠️ Full-account, durable credential — larger blast radius than
storage_state.json; dedicated/throwaway account only. See
installation.md#alternative-master-token-auth-no-cookie-file-to-ship-survives-expiry.
Environment Variables
| Variable | Description | Default |
|---|---|---|
NOTEBOOKLM_HOME |
Base directory for all files | ~/.notebooklm |
NOTEBOOKLM_PROFILE |
Active profile name | default |
NOTEBOOKLM_AUTH_JSON |
Inline authentication JSON (for CI/CD) | - |
NOTEBOOKLM_NOTEBOOK |
Default notebook ID for commands without -n/--notebook |
- |
NOTEBOOKLM_HL |
Default interface/output language code (e.g. en, ja, zh_Hans) |
en |
NOTEBOOKLM_BASE_URL |
Gemini Notebook base URL. Constrained to https://notebook.google.com (default) or https://notebooklm.google.com (pre-rebrand personal, still served) or https://notebooklm.cloud.google.com (enterprise) |
https://notebook.google.com |
NOTEBOOKLM_BL |
bl (build label) URL parameter for the chat streaming endpoint; override when chasing a regression tied to a specific frontend build snapshot |
built-in default in _env.DEFAULT_BL (drift-monitored nightly) |
NOTEBOOKLM_TRANSPORT |
HTTP transport backend: httpx (default) or curl_cffi (opt-in browser-TLS impersonation; requires the curl_cffi package). Use curl_cffi where the default transport is TLS-fingerprint-blocked. |
httpx |
NOTEBOOKLM_LOG_LEVEL |
Logging level: DEBUG, INFO, WARNING, ERROR |
WARNING |
NOTEBOOKLM_DEBUG_RPC |
Legacy: Enable RPC debug logging (use LOG_LEVEL=DEBUG instead) |
false |
NOTEBOOKLM_DEBUG |
Show untruncated RPC response bodies in error messages instead of the default 80-char preview (verbose; intended for deep debugging) | 0 |
NOTEBOOKLM_STRICT_DECODE |
Retired (ignored since v0.7.0). Strict decoding is now the only mode: safe_index always raises UnknownRPCMethodError on schema drift. The former 0 warn-and-fallback opt-out was removed. |
(ignored) |
NOTEBOOKLM_RPC_OVERRIDES |
JSON object mapping RPCMethod enum names to RPC ID strings (community self-patch when Google rotates a method ID; e.g. {"LIST_NOTEBOOKS":"AbC123"}) |
- |
NOTEBOOKLM_REFRESH_CMD |
Optional command (argv list, or shell string with _USE_SHELL=1) invoked when auth refresh is required. Must exit 0 after writing a refreshed storage_state.json; the parent reloads from disk |
- |
NOTEBOOKLM_REFRESH_CMD_USE_SHELL |
Opt the NOTEBOOKLM_REFRESH_CMD subprocess back into shell=True execution. Default shell=False (argv list) — set to the literal 1 (only "1" is honored — not true/yes/on) when the refresh command requires shell metacharacters |
0 |
NOTEBOOKLM_REFRESH_CMD_MIDSESSION |
Opt in (literal 1) to running NOTEBOOKLM_REFRESH_CMD mid-session (the L2.5 rung), not only at cold start. Off by default for one release so operators whose commands assume cold-start-only invocation are not surprised inside long-lived servers; flips to default-on a later release (ADR-0030) |
0 |
NOTEBOOKLM_REFRESH_CMD_LOG_OUTPUT |
Opt in (literal 1) to routing the refresh command's captured stdout/stderr to the redacting DEBUG logger. Off by default — the default DEBUG line carries only basename + exit code + byte counts, so promoting the rung into long-lived servers does not widen exposure of whatever the command prints |
0 |
NOTEBOOKLM_REFRESH_PROFILE |
Child-process hint set for NOTEBOOKLM_REFRESH_CMD; names the resolved profile being refreshed |
resolved profile |
NOTEBOOKLM_REFRESH_STORAGE_PATH |
Child-process hint set for NOTEBOOKLM_REFRESH_CMD; path to the storage_state.json file the command must rewrite |
resolved storage path |
NOTEBOOKLM_DISABLE_KEEPALIVE_POKE |
Disable the proactive accounts.google.com/RotateCookies poke that refreshes __Secure-1PSIDTS ahead of expiry |
0 |
NOTEBOOKLM_PROMOTION_EXIT_TIMEOUT |
Seconds the process waits at exit for an in-flight one-time migration of a pre-v0.x context.json account into storage_state.json. It is a ceiling, not a delay: a finished migration exits immediately. Raise it on a machine where the write is slow (antivirus, network storage); set 0 to never wait. An incomplete wait is always reported at WARNING, so note that --quiet (which raises the floor to ERROR) suppresses that signal. Non-numeric, negative, and non-finite values are refused with a warning and the default is used; a finite value above the platform join limit is clamped. |
30 |
NOTEBOOKLM_HEADLESS_REAUTH |
Opt in to layer-3 headless re-auth during cold construction and automatic refresh paths. Explicit from_storage(allow_headless=True), client.refresh_auth(allow_headless=True), or auth refresh --allow-headless does not require this env var. |
0 |
NOTEBOOKLM_HEADLESS_REAUTH_CDP_URL |
Optional loopback Chrome DevTools endpoint for layer-3 headless re-auth, e.g. http://127.0.0.1:9222. Non-loopback endpoints are ignored for credential safety. |
- |
NOTEBOOKLM_MCP_TRANSPORT |
MCP server transport for notebooklm-mcp: stdio or http |
stdio |
NOTEBOOKLM_MCP_HOST |
MCP HTTP transport bind host; non-loopback refused unless NOTEBOOKLM_MCP_ALLOW_EXTERNAL_BIND=1 |
127.0.0.1 |
NOTEBOOKLM_MCP_PORT |
MCP HTTP transport bind port | 9420 |
NOTEBOOKLM_MCP_ALLOW_EXTERNAL_BIND |
Allow MCP HTTP transport to bind a non-loopback host. Use only behind a trusted proxy. | 0 |
NOTEBOOKLM_MCP_OAUTH_PASSWORD |
Password gating the self-hosted OAuth authorization server that lets claude.ai connect to the remote MCP server (≥16 chars). Set together with NOTEBOOKLM_MCP_OAUTH_BASE_URL; both unset → bearer-only. |
- |
NOTEBOOKLM_MCP_OAUTH_BASE_URL |
Bare public HTTPS origin (no path) the self-hosted OAuth endpoints (/authorize, /token, /.well-known/*) mount under. Required with NOTEBOOKLM_MCP_OAUTH_PASSWORD; partial/weak/non-HTTPS config refuses to start. |
- |
NOTEBOOKLM_MCP_PUBLIC_URL |
Public base URL for the remote MCP file upload/download signed-URL side-channel (falls back to NOTEBOOKLM_MCP_OAUTH_BASE_URL). Unset → source_add type=file / artifact_download return a "not configured" error. |
- |
NOTEBOOKLM_MCP_TRUST_PROXY |
Trust the proxy-set CF-Connecting-IP header as the self-hosted-OAuth login-throttle key. Only enable behind a trusted proxy (e.g. the Cloudflare tunnel); default off keys on the socket peer. |
0 |
NOTEBOOKLM_MCP_STRICT_IDS |
Strict IDs-only mode for MCP tools: require a full canonical id for every notebook/source/note/artifact reference and reject names, titles, and short id prefixes (fail-fast, deterministic automation). |
0 |
NOTEBOOKLM_SERVER_TOKEN |
Bearer token required by every REST /v1 request. The REST server refuses to start without it. |
- |
NOTEBOOKLM_SERVER_HOST |
REST server bind host; non-loopback refused unless NOTEBOOKLM_SERVER_ALLOW_EXTERNAL_BIND=1 |
127.0.0.1 |
NOTEBOOKLM_SERVER_PORT |
REST server bind port | 8000 |
NOTEBOOKLM_SERVER_ALLOW_EXTERNAL_BIND |
Allow REST server to bind a non-loopback host. Use only behind a trusted proxy. | 0 |
NOTEBOOKLM_SERVER_SOURCE_MUTATION_CONCURRENCY |
Max concurrent REST source create/rename/delete/batch handlers. | 4 |
NOTEBOOKLM_SERVER_SOURCE_WAIT_CONCURRENCY |
Max concurrent REST source wait handlers. | 4 |
NOTEBOOKLM_SERVER_GENERATION_CONCURRENCY |
Max concurrent REST artifact generation/retry handlers. | 2 |
NOTEBOOKLM_SERVER_DOWNLOAD_CONCURRENCY |
Max concurrent REST artifact download handlers. | 2 |
NOTEBOOKLM_SERVER_RESEARCH_CONCURRENCY |
Max concurrent REST research start/cancel/import handlers. | 2 |
NOTEBOOKLM_SERVER_CHAT_CONCURRENCY |
Max concurrent REST blocking chat ask handlers. | 4 |
NOTEBOOKLM_QUIET_DEPRECATIONS |
Suppress the project's public-API DeprecationWarnings (the one-off warnings routed through warn_deprecated, e.g. awaiting from_storage(...)). Set to a truthy value (1 / true / yes / on, case-insensitive) to silence them; see docs/deprecations.md. |
(warnings emitted) |
NOTEBOOKLM_FUTURE_ERRORS |
Retired (removed in v0.8.0; ignored). It was the v0.7.0 forward-compat preview gate for the v0.8.0 error contract; now that every break it staged is the default, the flag is a no-op — setting it has no effect. See docs/deprecations.md. |
(ignored) |
NOTEBOOKLM_VCR_RECORD_ERRORS |
Synthetic-error injection mode for VCR test cassettes (429, 5xx, expired_csrf) |
- |
Public config API vs internal resolvers
src/notebooklm/_env.py owns internal environment/default resolution for
runtime behavior. It reads process environment variables and contains internal
defaults such as DEFAULT_BL for the chat streaming build label.
notebooklm.config is the stable public import surface. It intentionally
re-exports only the supported endpoint/language helpers:DEFAULT_BASE_URL, ENTERPRISE_BASE_HOST, get_base_host, get_base_url,get_default_language, and PERSONAL_BASE_HOST. Existing imports
from notebooklm.config remain supported; internal-only _env names should
not be imported by downstream code.
Env vars and precedence
Every NOTEBOOKLM_* variable read by the library and CLI, in one place. CLI
flags always win over env vars; env vars win over persisted profile config /
context; built-in defaults are the last fallback. The "Resolved by" column
points at the canonical resolver so the precedence rule for each variable can
be audited from one location.
| Variable | Purpose | Resolution order (highest → lowest) | Resolved by |
|---|---|---|---|
NOTEBOOKLM_PROFILE |
Active profile name. Selects which ~/.notebooklm/profiles/<name>/ directory backs storage and context. |
-p/--profile flag → NOTEBOOKLM_PROFILE → default_profile from ~/.notebooklm/config.json → default |
paths.resolve_profile |
NOTEBOOKLM_AUTH_JSON |
Inline storage_state.json payload for CI/CD; bypasses on-disk profile storage entirely. |
--storage flag → NOTEBOOKLM_AUTH_JSON → profile-aware storage_state.json → legacy fallback |
auth.load_auth_from_storage |
NOTEBOOKLM_HOME |
Base directory for all per-profile files. | NOTEBOOKLM_HOME → ~/.notebooklm |
paths.get_home_dir |
NOTEBOOKLM_HL |
Default interface/output language for generate <kind> and the hl query parameter on every batchexecute RPC. |
--language flag → NOTEBOOKLM_HL → language value from global ~/.notebooklm/config.json (NOT per-profile) → en |
language.resolve_hl |
NOTEBOOKLM_LOG_LEVEL |
DEBUG/INFO/WARNING/ERROR floor for the notebooklm package logger. |
--quiet flag (forces ERROR) → -v/-vv flags (force INFO/DEBUG) → NOTEBOOKLM_DEBUG_RPC=1 (forces DEBUG) → NOTEBOOKLM_LOG_LEVEL → WARNING |
_logging.configure_logging + notebooklm_cli.cli |
NOTEBOOKLM_DEBUG_RPC |
Legacy alias that sets the package logger to DEBUG. Prefer NOTEBOOKLM_LOG_LEVEL=DEBUG for new code. |
(See NOTEBOOKLM_LOG_LEVEL.) |
_logging.configure_logging |
NOTEBOOKLM_NOTEBOOK |
Default notebook ID when no -n/--notebook flag is passed. Composes with notebooklm use so per-shell overrides do not clobber the persisted active-notebook context. |
-n/--notebook flag → NOTEBOOKLM_NOTEBOOK → active context (from notebooklm use) → error |
cli.helpers.require_notebook (Click also reads it natively via cli/options.py:notebook_option's envvar=) |
NOTEBOOKLM_RPC_OVERRIDES |
JSON object mapping RPCMethod enum names to RPC ID strings (e.g. {"LIST_NOTEBOOKS": "AbC123"}). Overrides runtime RPC IDs — community self-patch when Google rotates a method ID. Empty string / unset disables the mechanism; invalid JSON or non-object payloads emit a WARNING and are ignored. |
Process env, evaluated per RPC resolve (cached on the raw env string). | notebooklm.rpc.overrides._parse_rpc_overrides |
NOTEBOOKLM_QUIET_DEPRECATIONS |
Suppress the project's public-API DeprecationWarnings — the one-off warnings routed through src/notebooklm/_deprecation.py::warn_deprecated (e.g. awaiting from_storage(...)). Set to a truthy value (1 / true / yes / on) to silence them. See docs/deprecations.md. |
(warnings emitted) | _deprecation._deprecations_quiet / deprecations_quiet |
NOTEBOOKLM_FUTURE_ERRORS |
Retired (removed in v0.8.0; ignored). It was the v0.7.0 forward-compat preview gate for the v0.8.0 error contract (ADR-0019 / umbrella #1346). Now that every break it staged — get() raising *NotFoundError, the attribute-only typed returns, the removed interval= alias, the bool→None returns, the refusal-raises, and the mutate-existing fail-loud — is the default, the flag is a no-op: setting it has no effect. See docs/deprecations.md. |
(ignored) | — |
NOTEBOOKLM_STRICT_DECODE |
Retired (ignored since v0.7.0). Strict decoding is the only mode — safe_index always raises UnknownRPCMethodError on schema drift. The former 0 warn-and-fallback opt-out was removed; setting the variable has no effect. |
(ignored) | — |
NOTEBOOKLM_BASE_URL |
Gemini Notebook base URL. Constrained to https://notebook.google.com (default) or https://notebooklm.google.com (pre-rebrand personal host, still served — the documented rollback lever; if auth fails after switching, re-run notebooklm login --fresh) or https://notebooklm.cloud.google.com (enterprise); other schemes/hosts/paths raise ValueError. |
Process env on every base-URL lookup. | _env.get_base_url |
NOTEBOOKLM_BL |
bl (build label) URL parameter sent on the chat streaming endpoint (ChatAPI.ask). Pins the frontend build the request is attributed to. The built-in _env.DEFAULT_BL is watched by the nightly canary's build-label lane, which compares it against the label Google actually serves; an override here does not change that verdict. |
Process env on every chat stream call; whitespace-only falls back to _env.DEFAULT_BL. |
_env.get_default_bl |
NOTEBOOKLM_DEBUG |
When 1, RPC error messages include the full untruncated response body instead of the default 80-char preview. Verbose; intended for deep debugging only. |
Process env on each error formatting call. | exceptions._truncate_response_preview |
NOTEBOOKLM_REFRESH_CMD |
Optional command invoked when auth refresh is required. Must exit 0 after writing a refreshed storage_state.json; the parent reloads cookies from disk. Stdout/stderr are not parsed (only surfaced in the non-zero-exit error message). Parsing honors NOTEBOOKLM_REFRESH_CMD_USE_SHELL. |
Process env on each refresh subprocess spawn. | auth refresh-spawn helper (constant NOTEBOOKLM_REFRESH_CMD_ENV in notebooklm.auth) |
NOTEBOOKLM_REFRESH_CMD_USE_SHELL |
Opt the optional NOTEBOOKLM_REFRESH_CMD subprocess back into shell=True. Default shell=False parses the command with shlex.split and invokes it as an argv list (safer; resists shell-injection footguns when the env var is sourced from CI configs or container env files). |
Process env on each refresh subprocess spawn. | auth refresh-spawn helper (constant NOTEBOOKLM_REFRESH_CMD_USE_SHELL_ENV in notebooklm.auth) |
NOTEBOOKLM_REFRESH_CMD_MIDSESSION |
Opt in (literal 1) to firing NOTEBOOKLM_REFRESH_CMD mid-session (the L2.5 rung of the unified ladder), not just at cold start. Default off for one release — the rung was cold-start-only before, and enabling it inside a long-lived server changes when the operator's command runs; flips to default-on a later release. See ADR-0030. |
Read on the mid-session recovery path. | auth._run_refresh_cmd (constant NOTEBOOKLM_REFRESH_CMD_MIDSESSION_ENV in notebooklm.auth) |
NOTEBOOKLM_REFRESH_CMD_LOG_OUTPUT |
Opt in (literal 1) to routing the refresh command's captured stdout/stderr into the redacting DEBUG logger. Default off: because the mid-session promotion widens exposure of whatever the command prints inside long-lived servers, the default DEBUG line carries only basename + exit code + byte counts. |
Process env / logging path on each refresh subprocess spawn. | auth._run_refresh_cmd (constant NOTEBOOKLM_REFRESH_CMD_LOG_OUTPUT_ENV in notebooklm.auth) |
NOTEBOOKLM_REFRESH_PROFILE |
Child env var injected into NOTEBOOKLM_REFRESH_CMD; names the resolved NotebookLM profile that is being refreshed. Refresh scripts may read it, but setting it in the parent shell does not select the profile. |
Set by auth refresh-spawn helper from the resolved profile. |
auth._run_refresh_cmd |
NOTEBOOKLM_REFRESH_STORAGE_PATH |
Child env var injected into NOTEBOOKLM_REFRESH_CMD; points to the storage_state.json file the command must rewrite before exiting 0. Refresh scripts may read it, but setting it in the parent shell does not select storage. |
Set by auth refresh-spawn helper from the explicit storage path or profile-aware storage path. |
auth._run_refresh_cmd |
NOTEBOOKLM_DISABLE_KEEPALIVE_POKE |
When 1, disable the proactive accounts.google.com/RotateCookies poke that refreshes __Secure-1PSIDTS ahead of expiry. Useful when running behind a proxy that rejects the extra request, or in offline test fixtures. |
Process env on every keepalive check. | auth keepalive guards (constant NOTEBOOKLM_DISABLE_KEEPALIVE_POKE_ENV in notebooklm.auth) |
NOTEBOOKLM_PROMOTION_EXIT_TIMEOUT |
Seconds the process waits at exit for an in-flight one-time migration of a pre-v0.x context.json account into storage_state.json. A ceiling shared by all outstanding writers, not a delay — a finished migration exits immediately. 0 never waits. Unset, empty, or whitespace-only falls back to the default; non-numeric, negative, and non-finite (inf/nan) values are refused with a WARNING and the default is used; a finite value above threading.TIMEOUT_MAX is clamped. An incomplete wait is always reported at WARNING (so --quiet, which forces ERROR, suppresses it). |
Process env, read once per process at exit → 30.0 |
_auth.profile_migration._promotion_exit_timeout |
NOTEBOOKLM_HEADLESS_REAUTH |
Opt in to layer-3 headless re-auth for cold construction and automatic refresh paths. Explicit Python/CLI allow_headless flags do not require the env var. |
Literal 1 enables; all other values disabled. |
_auth.headless_reauth.headless_reauth_env_enabled |
NOTEBOOKLM_HEADLESS_REAUTH_CDP_URL |
Optional Chrome DevTools Protocol endpoint for layer-3 headless re-auth. Must be loopback (127.0.0.1, ::1, or localhost); remote endpoints are ignored because CDP is account-equivalent. |
Explicit function argument → env var → no CDP arm. | _auth.headless_reauth.resolve_cdp_url |
NOTEBOOKLM_MCP_TRANSPORT |
Default transport for notebooklm-mcp: stdio or http. CLI --transport wins. |
--transport flag → env var → stdio |
mcp.__main__._build_parser |
NOTEBOOKLM_MCP_HOST |
HTTP bind host for notebooklm-mcp --transport http. Non-loopback refused unless NOTEBOOKLM_MCP_ALLOW_EXTERNAL_BIND=1. |
--host flag → env var → 127.0.0.1 |
mcp.__main__._build_parser / _serving.check_bind_allowed |
NOTEBOOKLM_MCP_PORT |
HTTP bind port for notebooklm-mcp --transport http. |
--port flag → env var → 9420 |
mcp.__main__._build_parser / _resolve_port |
NOTEBOOKLM_MCP_ALLOW_EXTERNAL_BIND |
Allow MCP HTTP transport to bind a non-loopback host. Use only behind a trusted proxy. | Literal 1 enables; all other values disabled. |
mcp.__main__._check_http_bind_allowed → _serving.check_bind_allowed |
NOTEBOOKLM_MCP_TRUST_PROXY |
Trust the proxy-set CF-Connecting-IP header as the self-hosted-OAuth login-throttle key. Enable only behind a trusted proxy (e.g. the Cloudflare tunnel); default off keys the throttle on the socket peer. |
Literal 1 enables; all other values disabled. |
mcp._oauth.get_oauth_config / _client_ip |
NOTEBOOKLM_MCP_STRICT_IDS |
Strict IDs-only mode: MCP notebook/source/note/artifact references must be a full canonical id; names, titles, and short id prefixes are rejected before any list call (deterministic automation). Off by default → default name/prefix/title resolution is unchanged. |
Literal 1 enables; all other values disabled. |
mcp._resolve._strict_ids_enabled |
NOTEBOOKLM_SERVER_TOKEN |
Bearer token required by every REST /v1 request. The server refuses to start when unset/empty. |
--token flag → env var → startup failure |
server.__main__._check_token_configured / server._auth.require_auth |
NOTEBOOKLM_SERVER_HOST |
REST server bind host. Non-loopback refused unless NOTEBOOKLM_SERVER_ALLOW_EXTERNAL_BIND=1. |
--host flag → env var → 127.0.0.1 |
server.__main__._build_parser / _serving.check_bind_allowed |
NOTEBOOKLM_SERVER_PORT |
REST server bind port. | --port flag → env var → 8000 |
server.__main__._build_parser / _resolve_port |
NOTEBOOKLM_SERVER_ALLOW_EXTERNAL_BIND |
Allow REST server to bind a non-loopback host. Use only behind a trusted proxy. | Literal 1 enables; all other values disabled. |
server.__main__._check_bind_allowed → _serving.check_bind_allowed |
NOTEBOOKLM_SERVER_SOURCE_MUTATION_CONCURRENCY |
Max concurrent REST source create/rename/delete/batch handlers. | Env var → 4; blank uses default; must be integer >=1. |
server._limits.ServerLimiters.from_env |
NOTEBOOKLM_SERVER_SOURCE_WAIT_CONCURRENCY |
Max concurrent REST source wait handlers. | Env var → 4; blank uses default; must be integer >=1. |
server._limits.ServerLimiters.from_env |
NOTEBOOKLM_SERVER_GENERATION_CONCURRENCY |
Max concurrent REST artifact generation/retry handlers. | Env var → 2; blank uses default; must be integer >=1. |
server._limits.ServerLimiters.from_env |
NOTEBOOKLM_SERVER_DOWNLOAD_CONCURRENCY |
Max concurrent REST artifact download handlers. | Env var → 2; blank uses default; must be integer >=1. |
server._limits.ServerLimiters.from_env |
NOTEBOOKLM_SERVER_RESEARCH_CONCURRENCY |
Max concurrent REST research start/cancel/import handlers. | Env var → 2; blank uses default; must be integer >=1. |
server._limits.ServerLimiters.from_env |
NOTEBOOKLM_SERVER_CHAT_CONCURRENCY |
Max concurrent REST blocking chat ask handlers. | Env var → 4; blank uses default; must be integer >=1. |
server._limits.ServerLimiters.from_env |
NOTEBOOKLM_VCR_RECORD_ERRORS |
Synthetic-error injection mode for VCR test cassettes. Lowercase-normalized; valid values are 429 (rate limit), 5xx (server error), or expired_csrf (CSRF token expiration). Used to record synthetic error cassettes under VCR. |
Process env on each request, evaluated by ErrorInjectionMiddleware to intercept and synthesize failures. |
_error_injection._get_error_injection_mode |
Boolean handling. NOTEBOOKLM_DEBUG_RPC treats 1 / true / yes
(case-insensitive) as truthy; everything else is falsy.NOTEBOOKLM_STRICT_DECODE is ignored as of v0.7.0 (strict decoding is the
only mode); no value changes decoder behavior.NOTEBOOKLM_QUIET_DEPRECATIONS treats 1 / true / yes / on
(case-insensitive) as truthy; any other value (including unset) leaves
deprecation warnings enabled. It silences the project's public-APIDeprecationWarnings — the get()-returns-None warning and deprecated
keyword aliases (e.g. ResearchAPI.wait_for_completion's legacy interval=) —
without changing their behavior.NOTEBOOKLM_NOTEBOOK is treated as unset when empty or whitespace-only so a
bare export NOTEBOOKLM_NOTEBOOK= does not block notebooklm use /-n/--notebook from resolving.
The --quiet global flag. notebooklm --quiet <subcommand> suppresses
status prose and raises the notebooklm package logger floor to ERROR for
the duration of one invocation, so cron and CI logs stay clean while real
failures still surface. Structured --json payloads are still emitted. It is
mutually exclusive with -v/-vv — combining the two raises a UsageError
(exit 2) since the resolved log levels conflict (ERROR vs INFO/DEBUG).
For shell-wide / always-on log suppression, use NOTEBOOKLM_LOG_LEVEL.
NOTEBOOKLM_HOME
Relocates all configuration files to a custom directory:
export NOTEBOOKLM_HOME=/custom/path
# All files now go here:
# /custom/path/profiles//storage_state.json
# /custom/path/profiles//context.json
# /custom/path/profiles//browser_profile/Use cases:
- Per-project isolation
- Custom storage locations
NOTEBOOKLM_PROFILE
Selects the active profile without changing the persisted default:
export NOTEBOOKLM_PROFILE=work
notebooklm list # Uses ~/.notebooklm/profiles/work/Equivalent to passing -p work on every command. The CLI flag takes precedence over the env var.
The persisted default is read from the default_profile key of~/.notebooklm/config.json (set via notebooklm profile switch <name>). When
neither a -p/--profile flag nor NOTEBOOKLM_PROFILE is set, paths.resolve_profile
falls back to this value (and finally to "default" if config.json doesn't
exist or has no default_profile key).
NOTEBOOKLM_AUTH_JSON
Provides authentication inline without writing files. Ideal for CI/CD:
export NOTEBOOKLM_AUTH_JSON='{"cookies":[...]}'
notebooklm list # Works without any file on diskPrecedence:
--storageCLI flag (highest)NOTEBOOKLM_AUTH_JSONenvironment variable- Profile-aware path:
$NOTEBOOKLM_HOME/profiles//storage_state.json ~/.notebooklm/profiles/default/storage_state.json(default)~/.notebooklm/storage_state.json(legacy fallback)
Note: Cannot run notebooklm login when NOTEBOOKLM_AUTH_JSON is set.
`NOTEBOOKLM_REFRESH_CMD`
Optional. If set, this command is invoked when an auth refresh is required —
replacing the default browser-cookie path. The contract is exit-code based:
- The command must exit
0. - On exit, it must have written a refreshed
storage_state.jsonat the
path inNOTEBOOKLM_REFRESH_STORAGE_PATH. The parent sets this child
env var to the explicit storage path or the resolved profile-aware
storage path before spawning the command.
The parent then reloads cookies from disk and retries the token fetch. Stdout
and stderr are not parsed — they are only captured for inclusion in the
error message when the command exits non-zero. Read by theNOTEBOOKLM_REFRESH_CMD_ENV constant in notebooklm.auth.
The child process also receives NOTEBOOKLM_REFRESH_PROFILE with the resolved
profile name.
See also NOTEBOOKLM_REFRESH_CMD_USE_SHELL to opt back into shell=True
parsing.
By default the command fires only at active cold start — client construction
/ AuthTokens.from_storage, which run the recovery ladder when the stored
cookies are dead. Passive readiness probes do NOT invoke it: auth check --test --passive (and other passive token fetches) use the strict, no-recovery
loader and never enter the refresh path, so a passive check reports expiry
without spawning the command. Set NOTEBOOKLM_REFRESH_CMD_MIDSESSION=1 to also
fire it mid-session (the L2.5 rung of the unified recovery ladder), e.g.
inside a long-lived server that has been running past cookie expiry. This is opt-in for
one release and flips to default-on afterward; enable it only once you have
confirmed your command is safe to invoke while the client is live. When you need
to see what the command printed, set NOTEBOOKLM_REFRESH_CMD_LOG_OUTPUT=1 to
route its captured stdout/stderr into the redacting DEBUG logger — the
default DEBUG line records only the command basename, exit code, and byte
counts. See ADR-0030 for the ladder design.
NOTEBOOKLM_HL
Sets the default interface/output language used by the client. The value is
passed as the hl query parameter on every batchexecute RPC call and is used by
the generate audio|video|slide-deck|infographic|data-table|mind-map|report
commands. In the Python API, omitted language on ArtifactsAPI.generate_*
keeps the historical "en" artifact default; pass language=None to opt in to
this NOTEBOOKLM_HL resolver.
export NOTEBOOKLM_HL=ja
notebooklm generate audio "deep dive" # Japanese audio overviewSurrounding whitespace is stripped; an empty or whitespace-only value falls
back to en. For the generate commands, the resolution order is:
--languageCLI flagNOTEBOOKLM_HLenvironment variablelanguagevalue from the global~/.notebooklm/config.json(set vianotebooklm language set <code>). The language is stored once perNOTEBOOKLM_HOME, not per profile — switchingnotebooklm -p work
does not switch the configured language. Seesrc/notebooklm/cli/language_cmd.pyfor the resolver andsrc/notebooklm/paths.pyfor the storage location.en(built-in default)
NOTEBOOKLM_QUIET_DEPRECATIONS
Suppresses the project's public-API DeprecationWarnings while you migrate. Set
it to a truthy value (1, true, yes, or on, case-insensitive) to silence
them; any other value (including unset) leaves them enabled.
It gates the one-off deprecation warnings routed throughsrc/notebooklm/_deprecation.py::warn_deprecated — e.g. awaitingNotebookLMClient.from_storage(...) instead of using the async with form. (The
v0.7.0 error-contract runways it also gated — the get()-returns-None warning,
the wait_for_completion(interval=...) alias, and the dict-subscript bridge — all
completed their removal in v0.8.0, so those warnings no longer exist; seedeprecations.md.)
The helper that reads this variable isnotebooklm._deprecation._deprecations_quiet (public aliasdeprecations_quiet).
# Silence the project's public-API deprecation warnings while migrating
export NOTEBOOKLM_QUIET_DEPRECATIONS=1Note: this variable does not affect
source add --mime-type/client.sources.add_file(mime_type=...)—mime_typeis a supported
parameter and emits no warning.
NOTEBOOKLM_FUTURE_ERRORS (removed in v0.8.0)
Removed. This was the v0.7.0 forward-compat preview gate for the v0.8.0 error
contract (ADR-0019 / umbrella
#1346): setting it made
the v0.7.0 warn-runways adopt their v0.8.0 raise-target early so you could test
forward-compatibility before the breaking flips shipped. v0.8.0 makes every one
of those flips the default — get() raising *NotFoundError on a miss, the
attribute-only typed returns, the removed interval= alias, the bool→None
returns, the refusal-raises, and the mutate-existing fail-loud — so the flag and
its resolver were deleted. Setting NOTEBOOKLM_FUTURE_ERRORS now has no
effect. Remove it from your environment / CI config. Seedeprecations.md for the full Removed-in-v0.8.0 table.
Timeouts
Most batchexecute RPCs issued by the client (whether through NotebookLMClient
or any of the CLI commands) use a 30-second HTTP request timeout by default,
with a tighter 10-second connection-establishment timeout. The shorter
connect timeout helps surface network-level issues quickly while the read
timeout accommodates slow server responses. The timeout is exposed as a
constructor argument on NotebookLMClient (timeout=) for callers that need to
tune it per-workload — see the DEFAULT_TIMEOUT / DEFAULT_CONNECT_TIMEOUT
constants in src/notebooklm/_runtime/config.py.
The chat streaming endpoint (ChatAPI.ask) also exposes a separate per-read
silence window (chat_timeout=). Left unset it is max(180 s, timeout=) —
180 seconds because shared notebooks can be slow to send the first streamed chat
byte, floored at timeout= so a larger configured budget still reaches chat (see
below); fast metadata RPCs stay
on the normal 30-second timeout. A chat read timeout means the server
sent no stream bytes for that window, either before the first byte or between
chunks; it does not mean total generation time exceeded 30 seconds. Passchat_timeout=None to inherit the normal client timeout for chat. The CLIask --request-timeout N flag overrides both values for that invocation.
IMPORT_RESEARCH (research.import_sources) has a third window: the server
ingests every requested entry before answering one RPC, so the window scales
with batch size — 60 s + 3 s per requested source, capped at 240 s — and is
tunable outright via import_research_timeout=.
How the per-RPC windows compose with `timeout=`
timeout= is the base read budget for every RPC. The two built-in per-RPC
windows above are defaults, not caps: they only ever lengthen that base,
never shorten it. So NotebookLMClient(auth, timeout=600) really does buy 600
seconds everywhere, including chat and IMPORT_RESEARCH (before this rule landed
they silently clamped such a client back to 180 s / 240 s — issue #2205).
| Construction | chat window | IMPORT_RESEARCH window (1 source) |
|---|---|---|
NotebookLMClient(auth) |
180 s | 63 s |
NotebookLMClient(auth, timeout=600) |
600 s | 600 s |
NotebookLMClient(auth, timeout=600, chat_timeout=10) |
10 s | 600 s |
NotebookLMClient(auth, import_research_timeout=900) |
180 s | 900 s |
An explicitly passed chat_timeout= / import_research_timeout= is the
caller's final word and is used as given — including a value below timeout=,
so deliberately fast failure stays expressible. Only the untouched defaults
compose. Both kwargs read identically:
| value | meaning |
|---|---|
| unset | the built-in window, floored at timeout= |
| a number | exactly that window, replacing the built-in and the floor |
None |
inherit timeout= verbatim, with no per-RPC window at all |
A non-positive or non-finite value for either raises ValueError at
construction rather than silently producing a window that times out instantly.
Independently, an IMPORT_RESEARCH attempt made byimport_sources_with_verification is clamped to whatever is left of that call's
own max_elapsed retry budget, so a late retry cannot be granted a window
larger than the budget it has left. Note what that does and does not promise:
every timeout here is an httpx slot, and the read slot is an inactivity limit
between socket reads — so max_elapsed bounds when a new attempt may start,
not the wall-clock duration of one already in flight. Enforcing the latter would
mean cancelling an in-flight non-idempotent POST, which risks duplicated sources
the client can no longer see. When less than 10 seconds of that budget remains — too little for an
attempt to outlast connection establishment — the loop stops instead of sending
one: IMPORT_RESEARCH is non-idempotent, so an attempt whose result the client
cannot observe can still commit sources server-side and duplicate them. The
first attempt is exempt, so max_elapsed=0 still means "try once".
Decoder strictness
NotebookLM's batchexecute responses are obfuscated, undocumented, and reshaped
by Google without notice. The decoder uses a shared safe_index helper to walk
nested response payloads. When it can't descend (an index is out of range, or
the value at a step isn't indexable), it raisesUnknownRPCMethodError (a subclass of DecodingError / RPCError) with
structured method_id, path, source, and data_at_failure attributes.
Strict decoding is the only mode. The legacy NOTEBOOKLM_STRICT_DECODE=0
warn-and-return-None opt-out (which emitted a DeprecationWarning through
v0.5.0/v0.6.0) was retired in v0.7.0; the env var is now ignored. The
strict contract surfaces real schema drift (Google rotating a response shape)
as a typed exception instead of a silent None return, so callers that
previously treated None as a sentinel must handle UnknownRPCMethodError.
The same UnknownRPCMethodError is also raised by decode_response() when the
batchexecute response contains RPC IDs but not the one the call requested
(typically a sign that Google rotated the method ID).
Background and rationale for the flip: see
docs/adr/0011-schema-validation-policy.md.
CLI Options
Global Options
| Option | Description | Default |
|---|---|---|
--storage PATH |
Path to storage_state.json | $NOTEBOOKLM_HOME/profiles//storage_state.json |
-p, --profile NAME |
Use a named profile | Active profile or default |
-v, --verbose |
Enable verbose output (-v for INFO, -vv for DEBUG) |
- |
--quiet |
Suppress status output and INFO/WARN logs (only errors survive). Mutually exclusive with -v. |
- |
--version |
Show version | - |
--help |
Show help | - |
Viewing Configuration
See where your configuration files are located:
notebooklm status --pathsOutput:
Configuration Paths
┏━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┓
┃ File ┃ Path ┃ Source ┃
┡━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━┩
│ Profile │ default │ default │
│ Home Directory │ /home/user/.notebooklm │ default │
│ Profile Directory│ /home/user/.notebooklm/profiles/default │ │
│ Storage State │ .../profiles/default/storage_state.json │ │
│ Context │ .../profiles/default/context.json │ │
│ Browser Profile │ .../profiles/default/browser_profile │ │
└──────────────────┴──────────────────────────────────────────┴───────────┘Session Management
Session Lifetime
Authentication sessions are tied to Google's cookie expiration:
- Sessions typically last several days to weeks
- Google may invalidate sessions for security reasons
- Rate limiting or suspicious activity can trigger earlier expiration
Refreshing Sessions
Automatic Refresh: CSRF tokens and session IDs are automatically refreshed when authentication errors are detected. This handles most "session expired" errors transparently.
Manual Re-authentication: If your session cookies have fully expired (automatic refresh won't help), re-authenticate:
notebooklm loginMultiple Accounts
Profiles (recommended): Use named profiles to manage multiple Google accounts under a single home directory:
# Create and authenticate profiles
notebooklm profile create work
notebooklm -p work login
notebooklm -p work list
notebooklm profile create personal
notebooklm -p personal login
notebooklm -p personal list
# Switch the active profile
notebooklm profile switch work
notebooklm list # Uses work profile
# List all profiles
notebooklm profile list
# Use env var for session-wide override
export NOTEBOOKLM_PROFILE=personal
notebooklm list # Uses personal profileEach profile stores its own storage_state.json, context.json, and browser_profile/ under ~/.notebooklm/profiles/<name>/.
Alternative: NOTEBOOKLM_HOME still works for full directory-level isolation:
export NOTEBOOKLM_HOME=~/.notebooklm-work
notebooklm loginOne-off override with --storage:
notebooklm --storage /path/to/storage_state.json listWhen --storage is set, two different context files are used —
they are NOT the same file:
- Notebook / conversation context lives at a suffixed file
.context.json(storage_path.with_suffix(storage_path.suffix + ".context.json"),
seepaths.get_context_path). Two--storageinvocations against different
files cannot see each other's selected notebook, and neither pollutes the
default profile context. - Account-routing metadata (the
notebooklm.accountobject —authuser
index and optionalemail) lives in-band inside the selectedstorage_state.json. This keeps copied files andNOTEBOOKLM_AUTH_JSON
secrets bound to the same Google account route as the original profile. - Persistent browser state lives beside the selected storage file. A path
namedstorage_state.jsonusesbrowser_profile/; other filenames append.browser_profileto the full filename when it fits, or use a stable
canonical-path hash when it does not. Login, headless re-auth, doctor's L3
readiness row,status --paths, and logout all resolve the same
storage-specific directory. Recursive deletion bylogin --freshor logout
requires either the ownership marker or a canonical managed profile layout.
Run notebooklm --storage status --paths to see exactly which
storage, context, and browser-profile paths are in use.
CI/CD Configuration
GitHub Actions (Recommended)
Use NOTEBOOKLM_AUTH_JSON for secure, file-free authentication:
jobs:
notebook-task:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install notebooklm-py
run: pip install notebooklm-py
# Pre-flight: fail fast and loud on missing/expired auth.
# `auth check --json` returns exit 0 even when status is "error"; --test makes the network
# call needed to detect expired cookies, and the `jq -e` flag converts a non-"ok" status
# into a non-zero exit code so the runner step actually fails.
- name: Verify auth (fail-fast on expired cookies)
env:
NOTEBOOKLM_AUTH_JSON: ${{ secrets.NOTEBOOKLM_AUTH_JSON }}
run: notebooklm auth check --test --json | jq -e '.status == "ok"'
- name: List notebooks
env:
NOTEBOOKLM_AUTH_JSON: ${{ secrets.NOTEBOOKLM_AUTH_JSON }}
run: notebooklm listBenefits:
- No file writes needed
- Secret stays in memory only
- Clean, simple workflow
Obtaining the Secret Value
- Run
notebooklm loginlocally - If the file was created by an older Playwright login and lacks
notebooklm.account, runnotebooklm auth refreshto repair single-account states or re-login withnotebooklm login --browser-cookies --account EMAILfor multi-account states - Copy the contents of
~/.notebooklm/profiles/default/storage_state.json(the canonical write location; the legacy~/.notebooklm/storage_state.jsonis only read as a fallback) - Add as a GitHub repository secret named
NOTEBOOKLM_AUTH_JSON(see installation.md#d-headless-server-or-ci for trailing-newline + ephemeral-runner refresh notes)
Alternative: File-Based Auth
If you prefer file-based authentication:
- name: Setup NotebookLM auth
run: |
mkdir -p ~/.notebooklm/profiles/default
echo "${{ secrets.NOTEBOOKLM_AUTH_JSON }}" > ~/.notebooklm/profiles/default/storage_state.json
chmod 600 ~/.notebooklm/profiles/default/storage_state.json
- name: List notebooks
run: notebooklm listFor profile-specific CI auth:
- name: Setup work profile auth
run: |
mkdir -p ~/.notebooklm/profiles/work
echo "${{ secrets.WORK_AUTH_JSON }}" > ~/.notebooklm/profiles/work/storage_state.json
chmod 600 ~/.notebooklm/profiles/work/storage_state.json
- name: List notebooks (work)
run: notebooklm -p work listSession Expiration
CSRF tokens are automatically refreshed during API calls. However, the underlying session cookies still expire. For long-running CI pipelines:
- Update the
NOTEBOOKLM_AUTH_JSONsecret every 1-2 weeks - Monitor for persistent auth failures (these indicate cookie expiration)
Debugging
Enable Verbose Output
Some commands support verbose output via Rich console:
# Most errors are printed to stderr with details
notebooklm list 2>&1 | catEnable RPC Debug Logging
NOTEBOOKLM_DEBUG_RPC=1 notebooklm listLogger namespace compatibility
Runtime transport and middleware logs still use the historicalnotebooklm._core logger key via CORE_LOGGER_NAME. That name is a
compatibility logging contract for existing filters and tests; it does not
mean the deleted _core.py module or a concrete Session owner still exists.
Check Authentication
Verify your session is working:
# Should list notebooks or show empty list
notebooklm list
# If you see "Unauthorized" or redirect errors, re-login
notebooklm loginCheck Configuration Paths
# See where files are being read from
notebooklm status --pathsNetwork Issues
The CLI uses httpx for HTTP requests. Common issues:
- Timeout: Google's API can be slow; large operations may time out
- SSL errors: Ensure your system certificates are up to date
- Proxy: Set standard environment variables (
HTTP_PROXY,HTTPS_PROXY) if needed
Platform Notes
macOS
Works out of the box. Chromium is downloaded automatically by Playwright.
Linux
For Playwright system dependencies and the Chromium install on Debian/Ubuntu, see docs/installation.md#platform-notes (and troubleshooting.md#linux if you hit TypeError: onExit is not a function).
Windows
Works with PowerShell or CMD. Use backslashes for paths:
notebooklm --storage C:\Users\Name\.notebooklm\storage_state.json listOr set environment variable:
$env:NOTEBOOKLM_HOME = "C:\Users\Name\custom-notebooklm"
notebooklm listWSL
Browser login opens in the Windows host browser. The storage file is saved in the WSL filesystem.
Headless Servers & Containers
Playwright is only required for the notebooklm login command. All other operations use standard HTTP requests via httpx.
For the install + auth-bootstrap recipe (run notebooklm login on a workstation, copy storage_state.json to the server, set NOTEBOOKLM_AUTH_JSON), see the canonical Persona D guide: docs/installation.md#d-headless-server-or-ci.