
Notebooklm
- 21 installs
- 195 repo stars
- Updated April 23, 2026
- win4r/notebooklm-py
Helps with productivity & planning tasks.
About
notebooklm is a Claude Code skill for productivity & planning. It helps solo builders move faster with AI-assisted development.
- notebooklm
- Productivity & Planning
- AI-coding skill
Notebooklm by the numbers
- 21 all-time installs (skills.sh)
- Ranked #2,002 of 3,282 Productivity & Planning skills by installs in the Skillselion catalog
- Data as of Jul 27, 2026 (Skillselion catalog sync)
npx skills add https://github.com/win4r/notebooklm-py --skill notebooklmAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 21 |
|---|---|
| repo stars | ★ 195 |
| Last updated | April 23, 2026 |
| Repository | win4r/notebooklm-py ↗ |
What it does
Helps with productivity & planning tasks.
Files
NotebookLM Automation
Complete programmatic access to Google NotebookLM—including capabilities not exposed in the web UI. Create notebooks, add sources (URLs, YouTube, PDFs, audio, video, images), chat with content, generate all artifact types, and download results in multiple formats.
Installation
From PyPI (Recommended):
pip install notebooklm-pyFrom GitHub (use latest release tag, NOT main branch):
# Get the latest release tag (using curl)
LATEST_TAG=$(curl -s https://api.github.com/repos/teng-lin/notebooklm-py/releases/latest | grep '"tag_name"' | cut -d'"' -f4)
pip install "git+https://github.com/teng-lin/notebooklm-py@${LATEST_TAG}"⚠️ DO NOT install from main branch (pip install git+https://github.com/teng-lin/notebooklm-py). The main branch may contain unreleased/unstable changes. Always use PyPI or a specific release tag, unless you are testing unreleased features.
Skill install methods:
notebooklm skill installinstalls this skill into the supported local agent directories managed by the CLI.npx skills add teng-lin/notebooklm-pyinstalls this skill from the GitHub repository into compatible agent skill directories.- If you are already reading this file inside an agent skill directory, the skill is already installed. You only need the Python package and authentication below.
CLI-managed install:
notebooklm skill installPrerequisites
IMPORTANT: Before using any command, you MUST authenticate:
notebooklm login # Opens browser for Google OAuth
notebooklm list # Verify authentication worksIf commands fail with authentication errors, re-run notebooklm login.
CI/CD, Multiple Accounts, and Parallel Agents
For automated environments, multiple accounts, or parallel agent workflows:
| Variable | Purpose |
|---|---|
NOTEBOOKLM_HOME | Custom config directory (default: ~/.notebooklm) |
NOTEBOOKLM_PROFILE | Active profile name (default: default) |
NOTEBOOKLM_AUTH_JSON | Inline auth JSON - no file writes needed |
CI/CD setup: Set NOTEBOOKLM_AUTH_JSON from a secret containing your storage_state.json contents.
Multiple accounts: Use named profiles (notebooklm profile create work, then notebooklm -p work login). Alternatively, use different NOTEBOOKLM_HOME directories per account.
Parallel agents: The CLI stores notebook context in a shared file (~/.notebooklm/context.json). Multiple concurrent agents using notebooklm use can overwrite each other's context.
Solutions for parallel workflows: 1. Always use explicit notebook ID (recommended): Pass -n <notebook_id> (for wait/download commands) or --notebook <notebook_id> (for others) instead of relying on use 2. Per-agent isolation via profiles: export NOTEBOOKLM_PROFILE=agent-$ID (each profile gets its own context file) 3. Per-agent isolation via home: Set unique NOTEBOOKLM_HOME per agent: export NOTEBOOKLM_HOME=/tmp/agent-$ID 4. Use full UUIDs: Avoid partial IDs in automation (they can become ambiguous)
Agent Setup Verification
Before starting workflows, verify the CLI is ready:
1. notebooklm status → Should show "Authenticated as: email@..." 2. notebooklm list --json → Should return valid JSON (even if empty notebooks list) 3. If either fails → Run notebooklm login
When This Skill Activates
Explicit: User says "/notebooklm", "use notebooklm", or mentions the tool by name
Intent detection: Recognize requests like:
- "Create a podcast about [topic]"
- "Summarize these URLs/documents"
- "Generate a quiz from my research"
- "Turn this into an audio overview"
- "Create flashcards for studying"
- "Generate a video explainer"
- "Make an infographic"
- "Create a mind map of the concepts"
- "Download the quiz as markdown"
- "Add these sources to NotebookLM"
Autonomy Rules
Run automatically (no confirmation):
notebooklm status- check contextnotebooklm auth check- diagnose auth issuesnotebooklm list- list notebooksnotebooklm source list- list sourcesnotebooklm artifact list- list artifactsnotebooklm language list- list supported languagesnotebooklm language get- get current languagenotebooklm language set- set language (global setting)notebooklm artifact wait- wait for artifact completion (in subagent context)notebooklm source wait- wait for source processing (in subagent context)notebooklm research status- check research statusnotebooklm research wait- wait for research (in subagent context)notebooklm use <id>- set context (⚠️ SINGLE-AGENT ONLY - use-nflag in parallel workflows)notebooklm create- create notebooknotebooklm ask "..."- chat queries (without--save-as-note)notebooklm history- display conversation history (read-only)notebooklm source add- add sourcesnotebooklm profile list- list profilesnotebooklm profile create- create profilenotebooklm profile switch- switch active profilenotebooklm doctor- check environment health
Ask before running:
notebooklm delete- destructivenotebooklm generate *- long-running, may failnotebooklm download *- writes to filesystemnotebooklm artifact wait- long-running (when in main conversation)notebooklm source wait- long-running (when in main conversation)notebooklm research wait- long-running (when in main conversation)notebooklm ask "..." --save-as-note- writes a notenotebooklm history --save- writes a note
Quick Reference
| Task | Command |
|---|---|
| Authenticate | notebooklm login |
| Diagnose auth issues | notebooklm auth check |
| Diagnose auth (full) | notebooklm auth check --test |
| List notebooks | notebooklm list |
| Create notebook | notebooklm create "Title" |
| Set context | notebooklm use <notebook_id> |
| Show context | notebooklm status |
| Add URL source | notebooklm source add "https://..." |
| Add file | notebooklm source add ./file.pdf |
| Add YouTube | notebooklm source add "https://youtube.com/..." |
| List sources | notebooklm source list |
| Delete source by ID | notebooklm source delete <source_id> |
| Delete source by exact title | notebooklm source delete-by-title "Exact Title" |
| Wait for source processing | notebooklm source wait <source_id> |
| Web research (fast) | notebooklm source add-research "query" |
| Web research (deep) | notebooklm source add-research "query" --mode deep --no-wait |
| Check research status | notebooklm research status |
| Wait for research | notebooklm research wait --import-all |
| Chat | notebooklm ask "question" |
| Chat (specific sources) | notebooklm ask "question" -s src_id1 -s src_id2 |
| Chat (with references) | notebooklm ask "question" --json |
| Chat (save answer as note) | notebooklm ask "question" --save-as-note |
| Chat (save with title) | notebooklm ask "question" --save-as-note --note-title "Title" |
| Show conversation history | notebooklm history |
| Save all history as note | notebooklm history --save |
| Continue specific conversation | notebooklm ask "question" -c <conversation_id> |
| Save history with title | notebooklm history --save --note-title "My Research" |
| Get source fulltext | notebooklm source fulltext <source_id> |
| Get source guide | notebooklm source guide <source_id> |
| Generate podcast | notebooklm generate audio "instructions" |
| Generate podcast (JSON) | notebooklm generate audio --json |
| Generate podcast (specific sources) | notebooklm generate audio -s src_id1 -s src_id2 |
| Generate video | notebooklm generate video "instructions" |
| Generate report | notebooklm generate report --format briefing-doc |
| Generate report (append instructions) | notebooklm generate report --format study-guide --append "Target audience: beginners" |
| Generate quiz | notebooklm generate quiz |
| Revise a slide | notebooklm generate revise-slide "prompt" --artifact <id> --slide 0 |
| Check artifact status | notebooklm artifact list |
| Wait for completion | notebooklm artifact wait <artifact_id> |
| Download audio | notebooklm download audio ./output.mp3 |
| Download video | notebooklm download video ./output.mp4 |
| Download slide deck (PDF) | notebooklm download slide-deck ./slides.pdf |
| Download slide deck (PPTX) | notebooklm download slide-deck ./slides.pptx --format pptx |
| Download report | notebooklm download report ./report.md |
| Download mind map | notebooklm download mind-map ./map.json |
| Download data table | notebooklm download data-table ./data.csv |
| Download quiz | notebooklm download quiz quiz.json |
| Download quiz (markdown) | notebooklm download quiz --format markdown quiz.md |
| Download flashcards | notebooklm download flashcards cards.json |
| Download flashcards (markdown) | notebooklm download flashcards --format markdown cards.md |
| Delete notebook | notebooklm notebook delete <id> |
| List languages | notebooklm language list |
| Get language | notebooklm language get |
| Set language | notebooklm language set zh_Hans |
| List profiles | notebooklm profile list |
| Create profile | notebooklm profile create work |
| Switch profile | notebooklm profile switch work |
| Delete profile | notebooklm profile delete old |
| Rename profile | notebooklm profile rename old new |
| Use profile (one-off) | notebooklm -p work list |
| Health check | notebooklm doctor |
| Health check (auto-fix) | notebooklm doctor --fix |
Parallel safety: Use explicit notebook IDs in parallel workflows. Commands supporting -n shorthand: artifact wait, source wait, research wait/status, download *. Download commands also support -a/--artifact. Other commands use --notebook. For chat, use -c <conversation_id> to target a specific conversation.
Partial IDs: Use first 6+ characters of UUIDs. Must be unique prefix (fails if ambiguous). Works for ID-based commands such as use, source delete, and wait. For exact source-title deletion, use source delete-by-title "Title". For automation, prefer full UUIDs to avoid ambiguity.
Command Output Formats
Commands with --json return structured data for parsing:
Create notebook:
$ notebooklm create "Research" --json
{"id": "abc123de-...", "title": "Research"}Add source:
$ notebooklm source add "https://example.com" --json
{"source_id": "def456...", "title": "Example", "status": "processing"}Generate artifact:
$ notebooklm generate audio "Focus on key points" --json
{"task_id": "xyz789...", "status": "pending"}Chat with references:
$ notebooklm ask "What is X?" --json
{"answer": "X is... [1] [2]", "conversation_id": "...", "turn_number": 1, "is_follow_up": false, "references": [{"source_id": "abc123...", "citation_number": 1, "cited_text": "Relevant passage from source..."}, {"source_id": "def456...", "citation_number": 2, "cited_text": "Another passage..."}]}Source fulltext (get indexed content):
$ notebooklm source fulltext <source_id> --json
{"source_id": "...", "title": "...", "char_count": 12345, "content": "Full indexed text..."}Understanding citations: The cited_text in references is often a snippet or section header, not the full quoted passage. The start_char/end_char positions reference NotebookLM's internal chunked index, not the raw fulltext. Use SourceFulltext.find_citation_context() to locate citations:
fulltext = await client.sources.get_fulltext(notebook_id, ref.source_id)
matches = fulltext.find_citation_context(ref.cited_text) # Returns list[(context, position)]
if matches:
context, pos = matches[0] # First match; check len(matches) > 1 for duplicatesExtract IDs: Parse the id, source_id, or task_id field from JSON output.
Generation Types
All generate commands support:
-s, --sourceto use specific source(s) instead of all sources--languageto set output language (defaults to configured language or 'en')--jsonfor machine-readable output (returnstask_idandstatus)--retry Nto automatically retry on rate limits with exponential backoff
| Type | Command | Options | Download |
|---|---|---|---|
| Podcast | generate audio | `--format [deep-dive\ | brief\ |
| Video | generate video | `--format [explainer\ | brief], --style [auto\ |
| Slide Deck | generate slide-deck | `--format [detailed\ | presenter], --length [default\ |
| Slide Revision | generate revise-slide "prompt" --artifact <id> --slide N | --wait, --notebook | (re-downloads parent deck) |
| Infographic | generate infographic | `--orientation [landscape\ | portrait\ |
| Report | generate report | `--format [briefing-doc\ | study-guide\ |
| Mind Map | generate mind-map | (sync, instant) | .json |
| Data Table | generate data-table | description required | .csv |
| Quiz | generate quiz | `--difficulty [easy\ | medium\ |
| Flashcards | generate flashcards | `--difficulty [easy\ | medium\ |
Features Beyond the Web UI
These capabilities are available via CLI but not 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 (web UI only shows interactive view) |
| 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 slide deck as editable .pptx (web UI only offers PDF) |
| Slide revision | generate revise-slide "prompt" --artifact <id> --slide N | Modify individual slides with a natural-language prompt |
| Report template append | generate report --format study-guide --append "..." | Append custom instructions to built-in format templates without losing the format type |
| Source fulltext | source fulltext <id> | Retrieve the indexed text content of any source |
| Save chat to note | ask "..." --save-as-note / history --save | Save Q&A answers or conversation history as notebook notes |
| Programmatic sharing | share commands | Manage sharing permissions without the UI |
Common Workflows
Research to Podcast (Interactive)
Time: 5-10 minutes total
1. notebooklm create "Research: [topic]" — if fails: check auth with `notebooklm login` 2. notebooklm source add for each URL/document — if one fails: log warning, continue with others 3. Wait for sources: notebooklm source list --json until all status=READY — required before generation 4. notebooklm generate audio "Focus on [specific angle]" (confirm when asked) — if rate limited: wait 5 min, retry once 5. Note the artifact ID returned 6. Check notebooklm artifact list later for status 7. notebooklm download audio ./podcast.mp3 when complete (confirm when asked)
Research to Podcast (Automated with Subagent)
Time: 5-10 minutes, but continues in background
When user wants full automation (generate and download when ready):
1. Create notebook and add sources as usual 2. Wait for sources to be ready (use source wait or check source list --json) 3. Run notebooklm generate audio "..." --json → parse artifact_id from output 4. Spawn a background agent using Task tool:
Task(
prompt="Wait for artifact {artifact_id} in notebook {notebook_id} to complete, then download.
Use: notebooklm artifact wait {artifact_id} -n {notebook_id} --timeout 600
Then: notebooklm download audio ./podcast.mp3 -a {artifact_id} -n {notebook_id}",
subagent_type="general-purpose"
)5. Main conversation continues while agent waits
Error handling in subagent:
- If
artifact waitreturns exit code 2 (timeout): Report timeout, suggest checkingartifact list - If download fails: Check if artifact status is COMPLETED first
Benefits: Non-blocking, user can do other work, automatic download on completion
Document Analysis
Time: 1-2 minutes
1. notebooklm create "Analysis: [project]" 2. notebooklm source add ./doc.pdf (or URLs) 3. notebooklm ask "Summarize the key points" 4. notebooklm ask "What are the main arguments?" 5. Continue chatting as needed
Bulk Import
Time: Varies by source count
1. notebooklm create "Collection: [name]" 2. Add multiple sources:
notebooklm source add "https://url1.com"
notebooklm source add "https://url2.com"
notebooklm source add ./local-file.pdf3. notebooklm source list to verify
Source limits: Varies by plan—Standard: 50, Plus: 100, Pro: 300, Ultra: 600 sources per notebook. See NotebookLM plans for details. The CLI does not enforce these limits; they are applied by your NotebookLM account. Supported types: PDFs, YouTube URLs, web URLs, Google Docs, text files, Markdown, Word docs, audio files, video files, images
Bulk Import with Source Waiting (Subagent Pattern)
Time: Varies by source count
When adding multiple sources and needing to wait for processing before chat/generation:
1. Add sources with --json to capture IDs:
notebooklm source add "https://url1.com" --json # → {"source_id": "abc..."}
notebooklm source add "https://url2.com" --json # → {"source_id": "def..."}2. Spawn a background agent to wait for all sources:
Task(
prompt="Wait for sources {source_ids} in notebook {notebook_id} to be ready.
For each: notebooklm source wait {id} -n {notebook_id} --timeout 120
Report when all ready or if any fail.",
subagent_type="general-purpose"
)3. Main conversation continues while agent waits 4. Once sources are ready, proceed with chat or generation
Why wait for sources? Sources must be indexed before chat or generation. Takes 10-60 seconds per source.
Deep Web Research (Subagent Pattern)
Time: 2-5 minutes, runs in background
Deep research finds and analyzes web sources on a topic:
1. Create notebook: notebooklm create "Research: [topic]" 2. Start deep research (non-blocking):
notebooklm source add-research "topic query" --mode deep --no-wait3. Spawn a background agent to wait and import:
Task(
prompt="Wait for research in notebook {notebook_id} to complete and import sources.
Use: notebooklm research wait -n {notebook_id} --import-all --timeout 300
Report how many sources were imported.",
subagent_type="general-purpose"
)4. Main conversation continues while agent waits 5. When agent completes, sources are imported automatically
Alternative (blocking): For simple cases, omit --no-wait:
notebooklm source add-research "topic" --mode deep --import-all
# Blocks for up to 5 minutesWhen to use each mode:
--mode fast: Specific topic, quick overview needed (5-10 sources, seconds)--mode deep: Broad topic, comprehensive analysis needed (20+ sources, 2-5 min)
Research sources:
--from web: Search the web (default)--from drive: Search Google Drive
Output Style
Progress updates: Brief status for each step
- "Creating notebook 'Research: AI'..."
- "Adding source: https://example.com..."
- "Starting audio generation... (task ID: abc123)"
Fire-and-forget for long operations:
- Start generation, return artifact ID immediately
- Do NOT poll or wait in main conversation - generation takes 5-45 minutes (see timing table)
- User checks status manually, OR use subagent with
artifact wait
JSON output: Use --json flag for machine-readable output:
notebooklm list --json
notebooklm auth check --json
notebooklm source list --json
notebooklm artifact list --jsonJSON schemas (key fields):
notebooklm list --json:
{"notebooks": [{"id": "...", "title": "...", "created_at": "..."}]}notebooklm auth check --json:
{"checks": {"storage_exists": true, "json_valid": true, "cookies_present": true, "sid_cookie": true, "token_fetch": true}, "details": {"storage_path": "...", "auth_source": "file", "cookies_found": ["SID", "HSID", "..."], "cookie_domains": [".google.com"]}}notebooklm source list --json:
{"sources": [{"id": "...", "title": "...", "status": "ready|processing|error"}]}notebooklm artifact list --json:
{"artifacts": [{"id": "...", "title": "...", "type": "Audio Overview", "status": "in_progress|pending|completed|unknown"}]}Status values:
- Sources:
processing→ready(orerror) - Artifacts:
pendingorin_progress→completed(orunknown)
Error Handling
On failure, offer the user a choice: 1. Retry the operation 2. Skip and continue with something else 3. Investigate the error
Error decision tree:
| Error | Cause | Action |
|---|---|---|
| Auth/cookie error | Session expired | Run notebooklm auth check then notebooklm login |
| "No notebook context" | Context not set | Use -n <id> or --notebook <id> flag (parallel), or notebooklm use <id> (single-agent) |
| "No result found for RPC ID" | Rate limiting | Wait 5-10 min, retry |
GENERATION_FAILED | Google rate limit | Wait and retry later |
| Download fails | Generation incomplete | Check artifact list for status |
| Invalid notebook/source ID | Wrong ID | Run notebooklm list to verify |
| RPC protocol error | Google changed APIs | May need CLI update |
Exit Codes
All commands use consistent exit codes:
| Code | Meaning | Action |
|---|---|---|
| 0 | Success | Continue |
| 1 | Error (not found, processing failed) | Check stderr, see Error Handling |
| 2 | Timeout (wait commands only) | Extend timeout or check status manually |
Examples:
source waitreturns 1 if source not found or processing failedartifact waitreturns 2 if timeout reached before completiongeneratereturns 1 if rate limited (check stderr for details)
Known Limitations
Rate limiting: Audio, video, quiz, flashcards, infographic, and slide deck generation may fail due to Google's rate limits. This is an API limitation, not a bug.
Reliable operations: These always work:
- Notebooks (list, create, delete, rename)
- Sources (add, list, delete)
- Chat/queries
- Mind-map, study-guide, report, data-table generation
Unreliable operations: These may fail with rate limiting:
- Audio (podcast) generation
- Video generation
- Quiz and flashcard generation
- Infographic and slide deck generation
Workaround: If generation fails: 1. Check status: notebooklm artifact list 2. Retry after 5-10 minutes 3. Use the NotebookLM web UI as fallback
Processing times vary significantly. Use the subagent pattern for long operations:
| Operation | Typical time | Suggested timeout |
|---|---|---|
| Source processing | 30s - 10 min | 600s |
| Research (fast) | 30s - 2 min | 180s |
| Research (deep) | 15 - 30+ min | 1800s |
| Notes | instant | n/a |
| Mind-map | instant (sync) | n/a |
| Quiz, flashcards | 5 - 15 min | 900s |
| Report, data-table | 5 - 15 min | 900s |
| Audio generation | 10 - 20 min | 1200s |
| Video generation | 15 - 45 min | 2700s |
Polling intervals: When checking status manually, poll every 15-30 seconds to avoid excessive API calls.
Language Configuration
Language setting controls the output language for generated artifacts (audio, video, etc.).
Important: Language is a GLOBAL setting that affects all notebooks in your account.
# List all 80+ supported languages with native names
notebooklm language list
# Show current language setting
notebooklm language get
# Set language for artifact generation
notebooklm language set zh_Hans # Simplified Chinese
notebooklm language set ja # Japanese
notebooklm language set en # English (default)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) |
Override per command: Use --language flag on generate commands:
notebooklm generate audio --language ja # Japanese podcast
notebooklm generate video --language zh_Hans # Chinese videoOffline mode: Use --local flag to skip server sync:
notebooklm language set zh_Hans --local # Save locally only
notebooklm language get --local # Read local config onlyTroubleshooting
notebooklm --help # Main commands
notebooklm auth check # Diagnose auth issues
notebooklm auth check --test # Full auth validation with network test
notebooklm notebook --help # Notebook management
notebooklm source --help # Source management
notebooklm research --help # Research status/wait
notebooklm generate --help # Content generation
notebooklm artifact --help # Artifact management
notebooklm download --help # Download content
notebooklm language --help # Language settingsDiagnose auth: notebooklm auth check - shows cookie domains, storage path, validation status Re-authenticate: notebooklm login Check version: notebooklm --version Refresh a CLI-managed install: notebooklm skill install
# NotebookLM Configuration
# Copy this file to .env and fill in your values
# =============================================================================
# Configuration Environment Variables
# =============================================================================
# Custom home directory for all config files (optional)
# Default: ~/.notebooklm
# NOTEBOOKLM_HOME=/custom/path
# Inline authentication JSON for CI/CD (optional)
# When set, authentication is read from this variable instead of a file
# Get the value from: cat $NOTEBOOKLM_HOME/storage_state.json (default: ~/.notebooklm)
# NOTEBOOKLM_AUTH_JSON='{"cookies":[...]}'
# Enable RPC debug logging (optional)
# NOTEBOOKLM_DEBUG_RPC=1
# =============================================================================
# E2E Testing Configuration
# =============================================================================
# Required for E2E tests: Your READ-ONLY test notebook ID
# Create a notebook at https://notebooklm.google.com with:
# - Multiple sources (text, URL, PDF, etc.)
# - Some pre-generated artifacts (audio, quiz, etc.)
# Copy the notebook ID from the URL: notebooklm.google.com/notebook/YOUR_ID
# This notebook is used for READ-ONLY tests (list, get, download operations)
NOTEBOOKLM_READ_ONLY_NOTEBOOK_ID=your-notebook-id-here
# Optional: Generation test notebook ID
# If not set, a notebook will be auto-created and its ID stored in
# NOTEBOOKLM_HOME/generation_notebook_id for reuse across test runs.
# NOTEBOOKLM_GENERATION_NOTEBOOK_ID=your-generation-notebook-id
version: 2
updates:
- package-ecosystem: "pip"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 5
groups:
dev-dependencies:
patterns:
- "pytest*"
- "mypy"
- "ruff"
- "vcrpy"
commit-message:
prefix: "deps"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
commit-message:
prefix: "ci"
Description
A clear description of the bug.
Steps to Reproduce
1. ... 2. ... 3. ...
Expected Behavior
What you expected to happen.
Actual Behavior
What actually happened. Include the full error message or traceback if applicable.
Paste error output hereEnvironment
- OS: (e.g., macOS 15, Ubuntu 24.04, Windows 11)
- Python version: (e.g., 3.12)
- notebooklm-py version: (run
notebooklm --version) - Install method: (pip, uv, pipx)
Debug Output
If applicable, run the failing command with -vv for verbose logging and paste the relevant output:
notebooklm -vv <your-command-here>Checklist
- [ ] I verified this bug exists on the latest version of notebooklm-py
- [ ] I searched existing issues and this is not a duplicate
blank_issues_enabled: false
Use Case
Describe the problem or workflow this feature would address.
Proposed Solution
How you'd like this to work (CLI usage, API example, etc.).
Alternatives Considered
Any alternative approaches you've thought of.
Summary
Brief description of the changes.
Related Issue
Closes #<issue_number>
Changes
- ...
Test Plan
- [ ] I tested these changes locally
- [ ] Tests pass (
pytest) - [ ] Linting passes (
ruff check src/ tests/) - [ ] Formatting passes (
ruff format --check src/ tests/) - [ ] Type checking passes (
mypy src/notebooklm --ignore-missing-imports)
Notes
Any additional context, trade-offs, or design decisions.
name: CodeQL
on:
push:
branches: [main]
pull_request:
branches: [main]
schedule:
- cron: '0 12 * * 1' # Monday at noon UTC
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
security-events: write
contents: read
steps:
- uses: actions/checkout@v6
- name: Initialize CodeQL
uses: github/codeql-action/init@v4
with:
languages: python
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v4
name: Nightly E2E Tests
on:
schedule:
# Main branch: 6 AM UTC (10 PM PST / 1 AM EST)
- cron: '0 6 * * *'
# Develop branch: 11 AM UTC (3 AM PST / 6 AM EST) - 5 hours later
- cron: '0 11 * * *'
workflow_dispatch:
inputs:
test_filter:
description: 'Specific test to run (e.g., tests/e2e/test_downloads.py::TestDownloadReport)'
required: false
default: ''
custom_branch:
description: 'Override branch to test (leave empty to test the branch you triggered from)'
required: false
default: ''
jobs:
# Determine which branch to test
resolve-branch:
runs-on: ubuntu-latest
if: github.repository == 'teng-lin/notebooklm-py'
outputs:
branch: ${{ steps.resolve.outputs.branch }}
is_standard: ${{ steps.resolve.outputs.is_standard }}
steps:
- name: Resolve target branch
id: resolve
run: |
if [ "${{ github.event_name }}" = "schedule" ]; then
# Scheduled: determine from cron expression
if [ "${{ github.event.schedule }}" = "0 6 * * *" ]; then
echo "branch=main" >> $GITHUB_OUTPUT
else
echo "branch=develop" >> $GITHUB_OUTPUT
fi
echo "is_standard=true" >> $GITHUB_OUTPUT
else
# Manual: use custom_branch if set, otherwise use triggering branch
CUSTOM="${{ inputs.custom_branch }}"
if [ -n "$CUSTOM" ]; then
TARGET="$CUSTOM"
else
TARGET="${{ github.ref_name }}"
fi
echo "branch=$TARGET" >> $GITHUB_OUTPUT
# Check if it's main or develop
if [ "$TARGET" = "main" ] || [ "$TARGET" = "develop" ]; then
echo "is_standard=true" >> $GITHUB_OUTPUT
else
echo "is_standard=false" >> $GITHUB_OUTPUT
fi
fi
echo "Resolved branch: $(cat $GITHUB_OUTPUT | grep branch= | cut -d= -f2)"
# Run E2E tests on the resolved branch
e2e:
name: E2E Tests (${{ needs.resolve-branch.outputs.branch }}/${{ matrix.os }})
needs: resolve-branch
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest]
concurrency:
group: nightly-${{ needs.resolve-branch.outputs.branch }}-${{ matrix.os }}
cancel-in-progress: true
steps:
- uses: actions/checkout@v6
with:
ref: ${{ needs.resolve-branch.outputs.branch }}
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.12"
cache: 'pip'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -e ".[all]"
- name: Get Playwright version
id: playwright-version
shell: bash
run: |
echo "version=$(pip show playwright | grep '^Version:' | cut -d' ' -f2)" >> $GITHUB_OUTPUT
- name: Cache Playwright browsers
uses: actions/cache@v5
with:
path: |
~/.cache/ms-playwright
~/AppData/Local/ms-playwright
key: playwright-${{ matrix.os }}-${{ steps.playwright-version.outputs.version }}
- name: Install Playwright browsers
run: playwright install chromium
- name: Install Playwright system dependencies (Linux)
if: runner.os == 'Linux'
run: playwright install-deps
- name: Run E2E tests
env:
NOTEBOOKLM_AUTH_JSON: ${{ secrets.NOTEBOOKLM_AUTH_JSON }}
NOTEBOOKLM_READ_ONLY_NOTEBOOK_ID: ${{ secrets.NOTEBOOKLM_READ_ONLY_NOTEBOOK_ID }}
NOTEBOOKLM_GENERATION_NOTEBOOK_ID: ${{ secrets.NOTEBOOKLM_GENERATION_NOTEBOOK_ID }}
shell: bash
run: |
echo "Testing branch: ${{ needs.resolve-branch.outputs.branch }}"
TEST_FILTER="${{ inputs.test_filter }}"
if [ -n "$TEST_FILTER" ]; then
# Run specific test(s) when filter provided
pytest "$TEST_FILTER" -s -v --tb=short --reruns 2 --reruns-delay 30
elif [ "${{ matrix.os }}" = "ubuntu-latest" ]; then
# Linux: run all E2E tests except variants
pytest tests/e2e -m "not variants" -s -v --tb=short --reruns 2 --reruns-delay 30
else
# Windows: run only read-only tests (safer, avoids write operations)
pytest tests/e2e -m "readonly and not variants" -s -v --tb=short --reruns 2 --reruns-delay 30
fi
name: Publish to PyPI
on:
push:
tags:
- "v*"
jobs:
build-and-publish:
name: Build and publish to PyPI
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- uses: actions/checkout@v6
- name: Set up Python 3.12
uses: actions/setup-python@v6
with:
python-version: "3.12"
- name: Validate tag matches version
run: |
TAG_VERSION=${GITHUB_REF#refs/tags/v}
TOML_VERSION=$(python -c "import tomllib; print(tomllib.load(open('pyproject.toml', 'rb'))['project']['version'])")
if [ "$TAG_VERSION" != "$TOML_VERSION" ]; then
echo "Error: Tag version ($TAG_VERSION) doesn't match pyproject.toml ($TOML_VERSION)"
exit 1
fi
echo "Version validated: $TOML_VERSION"
- name: Install build tools
run: |
python -m pip install --upgrade pip
pip install build
- name: Build package
run: |
python -m build
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
name: RPC Health Check
on:
schedule:
# Run at 7 AM UTC daily (1 hour after nightly E2E tests)
- cron: '0 7 * * *'
workflow_dispatch: # Allow manual trigger
concurrency:
group: ${{ github.workflow }}
cancel-in-progress: true
permissions:
contents: read
issues: write
jobs:
health-check:
name: RPC Health Check
runs-on: ubuntu-latest
# Only run on main repo (not forks) due to secrets requirement
if: github.repository == 'teng-lin/notebooklm-py'
steps:
- uses: actions/checkout@v6
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.12"
cache: 'pip'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -e .
- name: Run RPC Health Check
id: health
continue-on-error: true
shell: bash
env:
NOTEBOOKLM_AUTH_JSON: ${{ secrets.NOTEBOOKLM_AUTH_JSON }}
NOTEBOOKLM_READ_ONLY_NOTEBOOK_ID: ${{ secrets.NOTEBOOKLM_READ_ONLY_NOTEBOOK_ID }}
NOTEBOOKLM_GENERATION_NOTEBOOK_ID: ${{ secrets.NOTEBOOKLM_GENERATION_NOTEBOOK_ID }}
run: |
set +e
python scripts/check_rpc_health.py --full 2>&1 | tee health-report.txt
exit_code=${PIPESTATUS[0]}
echo "exit_code=${exit_code}" >> "$GITHUB_OUTPUT"
exit $exit_code
- name: Add Summary
if: always()
shell: bash
run: |
echo "## RPC Health Check Results" >> $GITHUB_STEP_SUMMARY
grep -A 10 "^SUMMARY$" health-report.txt >> $GITHUB_STEP_SUMMARY || echo "No summary found" >> $GITHUB_STEP_SUMMARY
- name: Create Issue on RPC Mismatch
if: steps.health.outputs.exit_code == '1'
uses: peter-evans/create-issue-from-file@v6
with:
title: "RPC ID Mismatch Detected"
content-filepath: health-report.txt
labels: bug, rpc-breakage, automated
- name: Create Issue on Auth Failure
if: steps.health.outputs.exit_code == '2'
uses: peter-evans/create-issue-from-file@v6
with:
title: "RPC Health Check: Authentication Failure"
content-filepath: health-report.txt
labels: bug, automated
- name: Upload Report
if: always()
uses: actions/upload-artifact@v7
with:
name: rpc-health-report
path: health-report.txt
retention-days: 30
- name: Fail if health check failed
if: steps.health.outcome == 'failure'
run: |
echo "RPC Health Check failed (exit code: ${{ steps.health.outputs.exit_code }}). See report above."
exit 1
name: Test
on:
push:
branches: [main]
pull_request:
branches: [main]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
quality:
name: Code Quality
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.12"
cache: 'pip'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -e ".[all]"
- name: Run pre-commit checks
run: pre-commit run --all-files
- name: Run type checking
run: mypy src/notebooklm --ignore-missing-imports
- name: Verify e2e test fixtures
run: pytest tests/e2e --collect-only -q
test:
name: Test (${{ matrix.os }}, Python ${{ matrix.python-version }})
runs-on: ${{ matrix.os }}
needs: quality
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
steps:
- uses: actions/checkout@v6
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -e ".[all]"
- name: Get Playwright version
id: playwright-version
shell: bash
run: |
echo "version=$(pip show playwright | grep '^Version:' | cut -d' ' -f2)" >> $GITHUB_OUTPUT
- name: Cache Playwright browsers
uses: actions/cache@v5
with:
path: |
~/.cache/ms-playwright
~/AppData/Local/ms-playwright
key: playwright-${{ matrix.os }}-${{ steps.playwright-version.outputs.version }}
- name: Install Playwright browsers
run: playwright install chromium
- name: Install Playwright system dependencies (Linux)
if: runner.os == 'Linux'
run: playwright install-deps
- name: Run tests with coverage
run: pytest --cov=src/notebooklm --cov-report=term-missing --cov-fail-under=70
name: Publish to TestPyPI
on:
workflow_dispatch:
jobs:
publish:
name: Build and publish to TestPyPI
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- uses: actions/checkout@v6
- name: Set up Python 3.12
uses: actions/setup-python@v6
with:
python-version: "3.12"
- name: Get version from pyproject.toml
id: version
run: |
VERSION=$(python -c "import tomllib; print(tomllib.load(open('pyproject.toml', 'rb'))['project']['version'])")
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "Publishing version: $VERSION"
- name: Install build tools
run: |
python -m pip install --upgrade pip
pip install build
- name: Build package
run: python -m build
- name: Upload to TestPyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
repository-url: https://test.pypi.org/legacy/
- name: Summary
run: |
echo "## Published to TestPyPI" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Version:** ${{ steps.version.outputs.version }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Package:** https://test.pypi.org/project/notebooklm-py/${{ steps.version.outputs.version }}/" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Next Step:** Run the **Verify Package** workflow with source=testpypi" >> $GITHUB_STEP_SUMMARY
name: Verify Generated Artifacts
on:
schedule:
# Run at 8 AM UTC daily (2 hours after nightly e2e tests)
- cron: '0 8 * * *'
workflow_dispatch: # Allow manual trigger
jobs:
verify:
name: Verify Artifacts
runs-on: ubuntu-latest
if: github.repository == 'teng-lin/notebooklm-py'
steps:
- uses: actions/checkout@v6
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.12"
cache: 'pip'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -e "."
- name: Verify artifacts exist
env:
NOTEBOOKLM_AUTH_JSON: ${{ secrets.NOTEBOOKLM_AUTH_JSON }}
NOTEBOOKLM_GENERATION_NOTEBOOK_ID: ${{ secrets.NOTEBOOKLM_GENERATION_NOTEBOOK_ID }}
run: |
python -c "
import asyncio
import os
import sys
from notebooklm import NotebookLMClient
# Type ID to display name mapping
TYPE_NAMES = {
1: 'Audio',
2: 'Report', # Study Guide, Briefing Doc, Blog Post
3: 'Video',
4: 'Quiz/Flashcards',
5: 'Mind Map',
7: 'Infographic',
8: 'Slide Deck',
9: 'Data Table',
}
# Expected type IDs from generation tests
# Note: Type 2 is Reports (study guide), Type 4 is Quiz+Flashcards
EXPECTED_TYPES = {1, 2, 3, 4, 5, 7, 8, 9}
async def verify():
async with await NotebookLMClient.from_storage() as client:
nb_id = os.environ['NOTEBOOKLM_GENERATION_NOTEBOOK_ID']
print(f'Checking notebook: {nb_id}')
# List artifacts
artifacts = await client.artifacts.list(nb_id)
print(f'\nTotal artifacts: {len(artifacts)}')
# Group by type and status
by_type = {}
for a in artifacts:
key = a._artifact_type
if key not in by_type:
by_type[key] = []
by_type[key].append(a)
print('\nArtifacts by type:')
for t in sorted(by_type.keys()):
items = by_type[t]
type_name = TYPE_NAMES.get(t, f'Unknown({t})')
print(f' {type_name} (type {t}): {len(items)}')
for a in items:
status = a.status_str
variant_info = f', variant={a._variant}' if a._variant else ''
print(f' - {a.title} ({status}{variant_info})')
# Check expected types
found = set(by_type.keys())
missing = EXPECTED_TYPES - found
print(f'\nExpected types: {len(EXPECTED_TYPES)}')
print(f'Found types: {len(found)}')
if missing:
missing_names = [TYPE_NAMES.get(t, str(t)) for t in missing]
print(f'\nWARNING: Missing artifact types: {missing_names}')
# Check for completed artifacts
completed = sum(1 for a in artifacts if a.is_completed)
processing = sum(1 for a in artifacts if a.is_processing)
failed = sum(1 for a in artifacts if a.is_failed)
print(f'\nStatus summary:')
print(f' Completed: {completed}')
print(f' Processing: {processing}')
print(f' Failed: {failed}')
# List notes
notes = await client.notes.list(nb_id)
print(f'\nTotal notes: {len(notes)}')
for n in notes[:10]:
print(f' - {n.title or \"(untitled)\"}')
if len(notes) > 10:
print(f' ... and {len(notes) - 10} more')
# Fail if too many failures or no artifacts at all
if len(artifacts) == 0:
print('\nERROR: No artifacts found!')
sys.exit(1)
if failed > len(artifacts) // 2:
print(f'\nERROR: Too many failed artifacts ({failed}/{len(artifacts)})')
sys.exit(1)
print('\nVerification complete!')
asyncio.run(verify())
"
name: Verify Package
on:
workflow_dispatch:
inputs:
source:
description: 'Package source'
required: true
default: 'testpypi'
type: choice
options:
- testpypi
- pypi
jobs:
verify:
name: Verify from ${{ inputs.source }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Set up Python 3.12
uses: actions/setup-python@v6
with:
python-version: "3.12"
cache: 'pip'
- name: Get version from pyproject.toml
id: version
run: |
VERSION=$(python -c "import tomllib; print(tomllib.load(open('pyproject.toml', 'rb'))['project']['version'])")
echo "version=$VERSION" >> $GITHUB_OUTPUT
- name: Create verification venv
run: python -m venv verify-venv
- name: Install from TestPyPI
if: inputs.source == 'testpypi'
shell: bash
run: |
source verify-venv/bin/activate
pip install --index-url https://test.pypi.org/simple/ \
--extra-index-url https://pypi.org/simple/ \
notebooklm-py==${{ steps.version.outputs.version }}
- name: Install from PyPI
if: inputs.source == 'pypi'
shell: bash
run: |
source verify-venv/bin/activate
pip install notebooklm-py==${{ steps.version.outputs.version }}
- name: Verify version
shell: bash
run: |
source verify-venv/bin/activate
INSTALLED=$(python -c "from notebooklm import __version__; print(__version__)")
EXPECTED="${{ steps.version.outputs.version }}"
if [ "$INSTALLED" != "$EXPECTED" ]; then
echo "Version mismatch: installed=$INSTALLED expected=$EXPECTED"
exit 1
fi
echo "Version verified: $INSTALLED"
- name: Verify CLI
shell: bash
run: |
source verify-venv/bin/activate
notebooklm --version
notebooklm --help
- name: Verify imports
shell: bash
run: |
source verify-venv/bin/activate
python -c "from notebooklm import NotebookLMClient, Notebook, Source, Artifact"
echo "Core imports verified"
- name: Install test dependencies
shell: bash
run: |
source verify-venv/bin/activate
pip install pytest pytest-asyncio pytest-httpx pytest-cov pytest-rerunfailures python-dotenv vcrpy playwright
playwright install chromium
playwright install-deps
- name: Run unit tests
shell: bash
run: |
source verify-venv/bin/activate
pytest tests/unit -v
- name: Run integration tests
shell: bash
run: |
source verify-venv/bin/activate
pytest tests/integration -v
- name: Skip E2E tests (fork)
if: github.repository != 'teng-lin/notebooklm-py'
run: |
echo "::warning::E2E tests skipped - secrets not available in fork repositories"
echo "## E2E Tests Skipped" >> $GITHUB_STEP_SUMMARY
echo "E2E tests were not run because this workflow is executing on a fork." >> $GITHUB_STEP_SUMMARY
- name: Run E2E tests
if: github.repository == 'teng-lin/notebooklm-py'
shell: bash
env:
NOTEBOOKLM_AUTH_JSON: ${{ secrets.NOTEBOOKLM_AUTH_JSON }}
NOTEBOOKLM_READ_ONLY_NOTEBOOK_ID: ${{ secrets.NOTEBOOKLM_READ_ONLY_NOTEBOOK_ID }}
run: |
source verify-venv/bin/activate
if [ -z "$NOTEBOOKLM_AUTH_JSON" ]; then
echo "::error::NOTEBOOKLM_AUTH_JSON secret is not configured"
exit 1
fi
if [ -z "$NOTEBOOKLM_READ_ONLY_NOTEBOOK_ID" ]; then
echo "::error::NOTEBOOKLM_READ_ONLY_NOTEBOOK_ID secret is not configured"
exit 1
fi
pytest tests/e2e -m "not variants" --reruns 2 -v
__pycache__/
*.py[cod]
*.class
.venv/
env/
venv/
.pytest_cache/
.coverage
htmlcov/
dist/
build/
*.egg-info/
.DS_Store
.env
.notebooklm/
# Defence-in-depth: catch accidental in-repo writes of the auth file
storage_state.json
*cookies*.json
captured_rpcs/
.worktrees/
.worktree/
.sisyphus/
.claude/
# Auto-generated by claude-mem plugin
**/CLAUDE.md
!/CLAUDE.md
# Investigation artifacts
investigate*.py
INVESTIGATION*.md
investigation_output/
downloads/
# VCR cassettes - committed after security review
# Cassettes are scrubbed of sensitive data (cookies, tokens, user IDs, emails)
# See tests/vcr_config.py for scrubbing patterns
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.8.6
hooks:
- id: ruff
args: [--fix]
- id: ruff-format
Repository Guidelines
Status: Active Last Updated: 2026-03-13
Project Structure & Module Organization
src/notebooklm/ contains the async client and typed APIs. Internal feature modules use _ prefixes such as _sources.py and _artifacts.py; src/notebooklm/cli/ holds Click commands, and src/notebooklm/rpc/ handles protocol encoding and decoding. Tests are split by scope: tests/unit/, tests/integration/, and tests/e2e/. Recorded HTTP fixtures live in tests/cassettes/. Examples are in docs/examples/, and diagnostics live in scripts/.
Build, Test, and Development Commands
Use uv for local work:
uv sync --extra dev --extra browser
uv run pytest
uv run ruff check src/ tests/
uv run ruff format src/ tests/
uv run mypy src/notebooklm
uv run pre-commit run --all-filesRun uv run pytest tests/e2e -m readonly only after notebooklm login and setting test notebook env vars.
Coding Style & Naming Conventions
Target Python 3.10+, 4-space indentation, and double quotes. Ruff enforces formatting and import order with a 100-character line length. Keep module and test file names in snake_case; prefer descriptive Click command names that match existing groups such as source, artifact, and research. Preserve the internal/public split: _*.py for implementation, exported types in src/notebooklm/__init__.py.
Testing Guidelines
Put pure logic in tests/unit/, VCR-backed flows in tests/integration/, and authenticated NotebookLM coverage in tests/e2e/. Name tests test_<behavior>.py and record cassettes with NOTEBOOKLM_VCR_RECORD=1 uv run pytest tests/integration/test_vcr_*.py -v. Coverage is expected to stay at or above the configured 90% threshold.
Commit, PR, and Agent Notes
Follow the existing commit style: feat(cli): ..., fix(cli): ..., refactor(test): ..., style: .... PRs should include a short summary, linked issue when relevant, and the commands run locally. For Codex or other parallel agents, prefer --json, pass explicit notebook IDs instead of relying on notebooklm use, and isolate runs with NOTEBOOKLM_HOME=/tmp/<agent-id> when multiple agents share one machine.
Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[Unreleased]
Added
- Auto-refresh on auth expiry -
fetch_tokensnow optionally runs a user-provided refresh command when a Google session cookie has expired, reloads cookies from storage, and retries once. Opt in by setting theNOTEBOOKLM_REFRESH_CMDenvironment variable to a script that rewrites~/.notebooklm/storage_state.json(e.g. a sync script reading from a cookie vault). Covers every CLI entry point without changing the public API. A process-scoped flag prevents retry loops.
[0.3.4] - 2026-03-12
Added
- Notebook metadata export - Added notebook metadata APIs and CLI export with a simplified sources list
- New
notebooklm metadatacommand with human-readable and--jsonoutput - New
NotebookMetadataandSourceSummarypublic types - New
client.notebooks.get_metadata()helper - Cinematic Video Overview support - Added cinematic generation and download flows
notebooklm generate video --format cinematic- Infographic styles - Added CLI support for selecting infographic visual styles
- `source delete-by-title` - Added explicit exact-title deletion command for sources
Fixed
- Research imports on timeout - CLI research imports now retry on timeout with backoff
- Metadata command behavior - Aligned metadata output and implementation with current CLI patterns
- Regional login cookies - Improved browser login handling for regional Google domains
- Notebook summary parsing - Fixed notebook summary response parsing
- Source delete UX - Improved source delete resolution, ambiguity handling, and title-vs-ID errors
- Empty downloads - Raise an error instead of producing zero-byte files
- Module execution - Added
python -m notebooklmsupport
Changed
- Documentation refresh - Updated release, development, CLI, README, and Python API docs for current commands, APIs, and
uvworkflows - Public API surface - Exported
NotebookMetadata,SourceSummary, andInfographicStyle
[0.3.3] - 2026-03-03
Added
- `ask --save-as-note` - Save chat answers as notebook notes directly from the CLI (#135)
notebooklm ask "question" --save-as-note- Save response as a notenotebooklm ask "question" --save-as-note --note-title "Title"- Save with custom title- `history --save` - Save full conversation history as a notebook note (#135)
notebooklm history --save- Save history with default titlenotebooklm history --save --note-title "Title"- Save with custom titlenotebooklm history --show-all- Show full Q&A content instead of preview- `generate report --append` - Append custom instructions to built-in report format templates (#134)
- Works with
briefing-doc,study-guide, andblog-postformats (no effect oncustom) - Example:
notebooklm generate report --format study-guide --append "Target audience: beginners" - `generate revise-slide` - Revise individual slides in an existing slide deck (#129)
notebooklm generate revise-slide "prompt" --artifact <id> --slide 0- PPTX download for slide decks - Download slide decks as editable PowerPoint files (#129)
notebooklm download slide-deck --format pptx(web UI only offers PDF)
Fixed
- Partial artifact ID in download commands - Download commands now support partial artifact IDs (#130)
- Chat empty answer - Fixed
askreturning empty answer when API response marker changes (#123) - X.com/Twitter content parsing - Fixed parsing of X.com/Twitter source content (#119)
- Language sync on login - Syncs server language setting to local config after
notebooklm login(#124) - Python version check - Added runtime check with clear error message for Python < 3.10 (#125)
- RPC error diagnostics - Improved error reporting for GET_NOTEBOOK and auth health check failures (#126, #127)
- Conversation persistence - Chat conversations now persist server-side; conversation ID shown in
historyoutput (#138) - History Q&A previews - Fixed populating Q&A previews using conversation turns API (#136)
- `generate report --language` - Fixed missing
--languageoption for report generation (#109)
Changed
- Chat history API - Simplified history retrieval; removed
exchange_id, improved conversation grouping with parallel fetching (#140, #141) - Conversation ID tracking - Server-side conversation lookup via new
hPTbtcRPC (GET_LAST_CONVERSATION_ID) replaces local exchange ID tracking - History Q&A population - Now uses
khqZzRPC (GET_CONVERSATION_TURNS) to fetch full Q&A turns with accurate previews (#136)
Infrastructure
- Bumped
actions/upload-artifactfrom v6 to v7 (#131)
[0.3.2] - 2026-01-26
Fixed
- CLI conversation reset - Fixed conversation ID not resetting when switching notebooks (#97)
- UTF-8 file encoding - Added explicit UTF-8 encoding to all file I/O operations (#93)
- Windows Playwright login - Restored ProactorEventLoop for Playwright login on Windows (#91)
Infrastructure
- Fixed E2E test teardown hook for pytest 8.x compatibility (#101)
- Added 15-second delay between E2E generation tests to avoid rate limits (#95)
[0.3.1] - 2026-01-23
Fixed
- Windows CLI hanging - Fixed asyncio ProactorEventLoop incompatibility causing CLI to hang on Windows (#79)
- Unicode encoding errors - Fixed encoding issues on non-English Windows systems (#80)
- Streaming downloads - Downloads now use streaming with temp files to prevent corrupted partial downloads (#82)
- Partial ID resolution - All CLI commands now support partial ID matching for notebooks, sources, and artifacts (#84)
- Source operations - Fixed empty array handling and
add_drivenesting (#73) - Guide response parsing - Fixed 3-level nesting in
get_guideresponses (#72) - RPC health check - Handle null response in health check scripts (#71)
- Script cleanup - Ensure temp notebook cleanup on failure or interrupt
Infrastructure
- Added develop branch to nightly E2E tests with staggered schedule
- Added custom branch support to nightly E2E workflow for release testing
[0.3.0] - 2026-01-21
Added
- Language settings - Configure output language for artifact generation (audio, video, etc.)
- New
notebooklm language list- List all 80+ supported languages with native names - New
notebooklm language get- Show current language setting - New
notebooklm language set <code>- Set language (e.g.,zh_Hans,ja,es) - Language is a global setting affecting all notebooks in your account
--localflag for offline-only operations (skip server sync)--languageflag on generate commands for per-command override- Sharing API - Programmatic notebook sharing management
- New
client.sharing.get_status(notebook_id)- Get current sharing configuration - New
client.sharing.set_public(notebook_id, True/False)- Enable/disable public link - New
client.sharing.set_view_level(notebook_id, level)- Set viewer access (FULL_NOTEBOOK or CHAT_ONLY) - New
client.sharing.add_user(notebook_id, email, permission)- Share with specific users - New
client.sharing.update_user(notebook_id, email, permission)- Update user permissions - New
client.sharing.remove_user(notebook_id, email)- Remove user access - New
ShareStatus,SharedUserdataclasses for structured sharing data - New
ShareAccess,SharePermission,ShareViewLevelenums - `SourceType` enum - New
str, Enumfor type-safe source identification: GOOGLE_DOCS,GOOGLE_SLIDES,GOOGLE_SPREADSHEET,PDF,PASTED_TEXT,WEB_PAGE,YOUTUBE,MARKDOWN,DOCX,CSV,IMAGE,MEDIA,UNKNOWN- `ArtifactType` enum - New
str, Enumfor type-safe artifact identification: AUDIO,VIDEO,REPORT,QUIZ,FLASHCARDS,MIND_MAP,INFOGRAPHIC,SLIDES,DATA_TABLE,UNKNOWN- `.kind` property - Unified type access across
Source,Artifact, andSourceFulltext:
# Works with both enum and string comparison
source.kind == SourceType.PDF # True
source.kind == "pdf" # Also True
artifact.kind == ArtifactType.AUDIO # True
artifact.kind == "audio" # Also True- `UnknownTypeWarning` - Warning (deduplicated) when API returns unknown type codes
- `SourceStatus.PREPARING` - New status (5) for sources in upload/preparation phase
- E2E test coverage - Added file upload tests for CSV, MP3, MP4, DOCX, JPG, Markdown with type verification
- `--retry` flag for generation commands - Automatic retry with exponential backoff on rate limits
notebooklm generate audio --retry 3- Retry up to 3 times on rate limit errors- Works with all generate commands (audio, video, quiz, etc.)
- `ArtifactStatus.FAILED` - New status (code 4) for artifact generation failures
- Centralized exception hierarchy - All errors now inherit from
NotebookLMErrorbase class - New
SourceAddErrorwith detailed failure messages for source operations - Granular exception types for better error handling in automation
- CLI `share` command group - Notebook sharing management from command line
notebooklm share- Enable public sharingnotebooklm share --revoke- Disable public sharing- Partial UUID matching for note commands -
note get,note delete, etc. now support partial IDs
Fixed
- Silent failures in CLI - Commands now properly report errors instead of failing silently
- Source type emoji display - Improved consistency in
source listoutput
Changed
- Source type detection - Use API-provided type codes as source of truth instead of URL/extension heuristics
- CLI file handling - Simplified to always use
add_file()for proper type detection
Removed
- `detect_source_type()` - Obsolete heuristic function replaced by
Source.kindproperty - `ARTIFACT_TYPE_DISPLAY` - Unused constant replaced by
get_artifact_type_display()
Deprecated
The following emit DeprecationWarning when accessed and will be removed in v0.4.0. See Migration Guide for upgrade instructions.
- `Source.source_type` - Use
.kindproperty instead (returnsSourceTypestr enum) - `Artifact.artifact_type` - Use
.kindproperty instead (returnsArtifactTypestr enum) - `Artifact.variant` - Use
.kind,.is_quiz, or.is_flashcardsinstead - `SourceFulltext.source_type` - Use
.kindproperty instead - `StudioContentType` - Use
ArtifactType(str enum) for user-facing code
[0.2.1] - 2026-01-15
Added
- Authentication diagnostics - New
notebooklm auth checkcommand for troubleshooting auth issues - Shows storage file location and validity
- Lists cookies present and their domains
- Detects
NOTEBOOKLM_AUTH_JSONandNOTEBOOKLM_HOMEusage --testflag performs network validation--jsonflag for machine-readable output (CI/CD friendly)- Structured logging - Comprehensive DEBUG logging across library
NOTEBOOKLM_LOG_LEVELenvironment variable (DEBUG, INFO, WARNING, ERROR)- RPC call timing and method tracking
- Legacy
NOTEBOOKLM_DEBUG_RPC=1still works - RPC health monitoring - Automated nightly check for Google API changes
- Detects RPC method ID mismatches before they cause failures
- Auto-creates GitHub issues with
rpc-breakagelabel on detection
Fixed
- Cookie domain priority - Prioritize
.google.comcookies over regional domains (e.g.,.google.co.uk) for more reliable authentication - YouTube URL parsing - Improved handling of edge cases in YouTube video URLs
Documentation
- Added
auth checkto CLI reference and troubleshooting guide - Consolidated CI/CD troubleshooting in development guide
- Added installation instructions to SKILL.md for Claude Code
- Clarified version numbering policy (PATCH vs MINOR)
[0.2.0] - 2026-01-14
Added
- Source fulltext extraction - Retrieve the complete indexed text content of any source
- New
client.sources.get_fulltext(notebook_id, source_id)Python API - New
source fulltext <source_id>CLI command with--jsonand-ooutput options - Returns
SourceFulltextdataclass with content, title, URL, and character count - Chat citation references - Get detailed source references for chat answers
AskResult.referencesfield contains list ofChatReferenceobjects- Each reference includes
source_id,cited_text,start_char,end_char,chunk_id - Use
notebooklm ask "question" --jsonto see references in CLI output - Source status helper - New
source_status_to_str()function for consistent status display - Quiz and flashcard downloads - Export interactive study materials in multiple formats
- New
download quizanddownload flashcardsCLI commands - Supports JSON, Markdown, and HTML output formats via
--formatflag - Python API:
client.artifacts.download_quiz()andclient.artifacts.download_flashcards() - Extended artifact downloads - Download additional artifact types
- New
download reportcommand (exports as Markdown) - New
download mind-mapcommand (exports as JSON) - New
download data-tablecommand (exports as CSV) - All download commands support
--all,--latest,--name, and--artifactselection options
Fixed
- Regional Google domain authentication - SID cookie extraction now works with regional Google domains (e.g., google.co.uk, google.de, google.cn) in addition to google.com
- Artifact completion detection - Media URL availability is now verified before reporting artifact as complete, preventing premature "ready" status
- URL hostname validation - Use proper URL parsing instead of string operations for security
Changed
- Pre-commit checks - Added mypy type checking to required pre-commit workflow
[0.1.4] - 2026-01-11
Added
- Source selection for chat and artifacts - Select specific sources when using
askorgeneratecommands - New
--sourcesflag accepts comma-separated source IDs or partial matches - Works with all generation commands (audio, video, quiz, etc.) and chat
- Research sources table -
research statusnow displays sources in a formatted table instead of just a count
Fixed
- JSON output broken in TTY terminals -
--jsonflag output was including ANSI color codes, breaking JSON parsing for commands likenotebooklm list --json - Warning stacklevel -
warnings.warncalls now report correct source location
Infrastructure
- Windows CI testing - Windows is now part of the nightly E2E test matrix
- VCR.py integration - Added recorded HTTP cassette support for faster, deterministic integration tests
- Test coverage improvements - Improved coverage for
_artifacts.py(71% → 83%),download.py, andsession.py
[0.1.3] - 2026-01-10
Fixed
- PyPI README links - Documentation links now work correctly on PyPI
- Added
hatch-fancy-pypi-readmeplugin for build-time link transformation - Relative links (e.g.,
docs/troubleshooting.md) are converted to version-tagged GitHub URLs - PyPI users now see links pointing to the exact version they installed (e.g.,
/blob/v0.1.3/docs/...) - Development repository link - Added prominent source link for PyPI users to find the GitHub repo
[0.1.2] - 2026-01-10
Added
- Ruff linter/formatter - Added to development workflow with pre-commit hooks and CI integration
- Multi-version testing - Docker-based test runner script for Python 3.10-3.14 (
/matrixskill) - Artifact verification workflow - New CI workflow runs 2 hours after nightly tests to verify generated artifacts
Changed
- Python version support - Now supports Python 3.10-3.14 (dropped 3.9)
- CI authentication - Use
NOTEBOOKLM_AUTH_JSONenvironment variable (inline JSON, no file writes)
Fixed
- E2E test cleanup - Generation notebook fixture now only cleans artifacts once per session (was deleting artifacts between tests)
- Nightly CI - Fixed pytest marker from
-m e2eto-m "not variants"(e2e marker didn't exist) - macOS CI fix for Playwright version extraction (grep pattern anchoring)
- Python 3.10 test compatibility with mock.patch resolution
Documentation
- Claude Code skill: parallel agent safety guidance
- Claude Code skill: timeout recommendations for all artifact types
- Claude Code skill: clarified
-nvs--notebookflag availability
[0.1.1] - 2026-01-08
Added
NOTEBOOKLM_HOMEenvironment variable for custom storage locationNOTEBOOKLM_AUTH_JSONenvironment variable for inline authentication (CI/CD friendly)- Claude Code skill installation via
notebooklm skill install
Fixed
- Infographic generation parameter structure
- Mind map artifacts now persist as notes after generation
- Artifact export with proper ExportType enum handling
- Skill install path resolution for package data
Documentation
- PyPI release checklist
- Streamlined README
- E2E test fixture documentation
[0.1.0] - 2026-01-06
Added
- Initial release of
notebooklm-py- unofficial Python client for Google NotebookLM - Full notebook CRUD operations (create, list, rename, delete)
- Research polling CLI commands for LLM agent workflows:
notebooklm research status- Check research progress (non-blocking)notebooklm research wait --import-all- Wait for completion and import sourcesnotebooklm source add-research --no-wait- Start deep research without blocking- Multi-artifact downloads with intelligent selection:
download audio,download video,download infographic,download slide-deck- Multiple artifact selection (--all flag)
- Smart defaults and intelligent filtering (--latest, --earliest, --name, --artifact-id)
- File/directory conflict handling (--force, --no-clobber, auto-rename)
- Preview mode (--dry-run) and structured output (--json)
- Source management:
- Add URL sources (with YouTube transcript support)
- Add text sources
- Add file sources (PDF, TXT, MD, DOCX) via native upload
- Delete sources
- Rename sources
- Studio artifact generation:
- Audio overviews (podcasts) with 4 formats and 3 lengths
- Video overviews with 9 visual styles
- Quizzes and flashcards
- Infographics, slide decks, and data tables
- Study guides, briefing docs, and reports
- Query/chat interface with conversation history support
- Research agents (Fast and Deep modes)
- Artifact downloads (audio, video, infographics, slides)
- CLI with 27 commands
- Comprehensive documentation (API, RPC, examples)
- 96 unit tests (100% passing)
- E2E tests for all major features
Fixed
- Audio overview instructions parameter now properly supported at RPC position [6][1][0]
- Quiz and flashcard distinction via title-based filtering
- Package renamed from
notebooklm-automationtonotebooklm - CLI module renamed from
cli.pytonotebooklm_cli.py - Removed orphaned
cli_query.pyfile
⚠️ Beta Release Notice
This is the initial public release of notebooklm-py. While core functionality is tested and working, please note:
- RPC Protocol Fragility: This library uses undocumented Google APIs. Method IDs can change without notice, potentially breaking functionality. See Troubleshooting for debugging guidance.
- Unofficial Status: This is not affiliated with or endorsed by Google.
- API Stability: The Python API may change in future releases as we refine the interface.
Known Issues
- RPC method IDs may change: Google can update their internal APIs at any time, breaking this library. Check the RPC Development Guide for how to identify and update method IDs.
- Rate limiting: Heavy usage may trigger Google's rate limits. Add delays between bulk operations.
- Authentication expiry: CSRF tokens expire after some time. Re-run
notebooklm loginif you encounter auth errors. - Large file uploads: Files over 50MB may fail or timeout. Split large documents if needed.
[Unreleased]: https://github.com/teng-lin/notebooklm-py/compare/v0.3.4...HEAD [0.3.4]: https://github.com/teng-lin/notebooklm-py/compare/v0.3.3...v0.3.4 [0.3.3]: https://github.com/teng-lin/notebooklm-py/compare/v0.3.2...v0.3.3 [0.3.2]: https://github.com/teng-lin/notebooklm-py/compare/v0.3.1...v0.3.2 [0.3.1]: https://github.com/teng-lin/notebooklm-py/compare/v0.3.0...v0.3.1 [0.3.0]: https://github.com/teng-lin/notebooklm-py/compare/v0.2.1...v0.3.0 [0.2.1]: https://github.com/teng-lin/notebooklm-py/compare/v0.2.0...v0.2.1 [0.2.0]: https://github.com/teng-lin/notebooklm-py/compare/v0.1.4...v0.2.0 [0.1.4]: https://github.com/teng-lin/notebooklm-py/compare/v0.1.3...v0.1.4 [0.1.3]: https://github.com/teng-lin/notebooklm-py/compare/v0.1.2...v0.1.3 [0.1.2]: https://github.com/teng-lin/notebooklm-py/compare/v0.1.1...v0.1.2 [0.1.1]: https://github.com/teng-lin/notebooklm-py/compare/v0.1.0...v0.1.1 [0.1.0]: https://github.com/teng-lin/notebooklm-py/releases/tag/v0.1.0
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
IMPORTANT: Follow documentation rules in CONTRIBUTING.md - especially the file creation and naming conventions.
Project Overview
notebooklm-py is an unofficial Python client for Google NotebookLM that uses undocumented RPC APIs. The library enables programmatic automation of NotebookLM features including notebook management, source integration, AI querying, and studio artifact generation (podcasts, videos, quizzes, etc.).
Critical constraint: This uses Google's internal batchexecute RPC protocol with obfuscated method IDs that Google can change at any time. All RPC method IDs in src/notebooklm/rpc/types.py are undocumented and subject to breakage.
Development Commands
# Create/recreate venv with uv (recommended - relocatable venvs)
uv venv .venv
uv pip install -e ".[all]"
playwright install chromium
# Activate virtual environment
source .venv/bin/activate
# Run all tests (excluding e2e by default)
pytest
# Run with coverage
pytest --cov
# Run e2e tests (requires authentication)
pytest tests/e2e -m e2e
# CLI testing
notebooklm --helpPre-Commit Checks (REQUIRED before committing)
IMPORTANT: Always run these checks before committing to avoid CI failures:
# Format code with ruff
ruff format src/ tests/
# Check for linting issues
ruff check src/ tests/
# Type checking with mypy
mypy src/notebooklm --ignore-missing-imports
# Run tests
pytestOr use this one-liner:
ruff format src/ tests/ && ruff check src/ tests/ && mypy src/notebooklm --ignore-missing-imports && pytestArchitecture
Layered Design
CLI Layer (cli/)
↓
Client Layer (client.py, _*.py APIs)
↓
Core Layer (_core.py)
↓
RPC Layer (rpc/)1. RPC Layer (src/notebooklm/rpc/):
types.py: All RPC method IDs and enums (source of truth)encoder.py: Request encodingdecoder.py: Response parsing
2. Core Layer (src/notebooklm/_core.py):
- HTTP client management
- RPC call abstraction
- Request counter handling
3. Client Layer (src/notebooklm/client.py, _*.py):
NotebookLMClient: Main async client with namespaced APIs_notebooks.py,_sources.py,_artifacts.py, etc.: Domain APIs
4. CLI Layer (src/notebooklm/cli/):
- Modular Click commands
session.py,notebook.py,source.py,generate.py, etc.
Key Files
| File | Purpose |
|---|---|
client.py | Main NotebookLMClient class |
_core.py | HTTP and RPC infrastructure |
_notebooks.py | client.notebooks API |
_sources.py | client.sources API |
_artifacts.py | client.artifacts API |
_chat.py | client.chat API |
rpc/types.py | RPC method IDs (source of truth) |
auth.py | Authentication handling |
cli/ | CLI command modules |
Repository Structure
src/notebooklm/
├── __init__.py # Public exports
├── client.py # NotebookLMClient
├── auth.py # Authentication
├── types.py # Dataclasses
├── _core.py # Core infrastructure
├── _notebooks.py # NotebooksAPI
├── _sources.py # SourcesAPI
├── _artifacts.py # ArtifactsAPI
├── _chat.py # ChatAPI
├── _research.py # ResearchAPI
├── _notes.py # NotesAPI
├── rpc/ # RPC protocol layer
│ ├── types.py # Method IDs and enums
│ ├── encoder.py # Request encoding
│ └── decoder.py # Response parsing
└── cli/ # CLI implementation
├── __init__.py
├── helpers.py # Shared utilities
├── session.py # login, use, status, clear
├── notebook.py # list, create, delete, rename
├── source.py # source add, list, delete
├── artifact.py # artifact commands
├── generate.py # generate audio, video, etc.
├── download.py # download commands
├── chat.py # ask, configure, history
└── note.py # note commandsAPI Patterns
Client Usage
# Correct pattern - uses namespaced APIs
async with await NotebookLMClient.from_storage() as client:
notebooks = await client.notebooks.list()
await client.sources.add_url(nb_id, url)
result = await client.chat.ask(nb_id, question)
status = await client.artifacts.generate_audio(nb_id)CLI Structure
Commands are organized as:
- Top-level:
login,use,status,clear,list,create,ask - Grouped:
source add,artifact list,generate audio,download video,note create
Testing Strategy
- Unit tests (
tests/unit/): Test encoding/decoding, no network - Integration tests (
tests/integration/): Mock HTTP responses - E2E tests (
tests/e2e/): Real API, require auth, marked@pytest.mark.e2e
E2E Test Status
- ✅ Notebook operations (list, create, rename, delete)
- ✅ Source operations (add URL/text/YouTube, rename)
- ✅ Download operations (audio, video, infographic, slides)
- ⚠️ Artifact generation may fail due to rate limiting
Common Pitfalls
1. RPC method IDs change: Check network traffic and update rpc/types.py 2. Nested list structures: Params are position-sensitive. Check existing implementations. 3. Source ID nesting: Different methods need [id], [[id]], [[[id]]], or [[[[id]]]] 4. CSRF tokens expire: Use client.refresh_auth() or re-run notebooklm login 5. Rate limiting: Add delays between bulk operations
Documentation
All docs use lowercase-kebab naming in docs/:
docs/cli-reference.md- CLI commandsdocs/python-api.md- Python API referencedocs/configuration.md- Storage and settingsdocs/troubleshooting.md- Known issuesdocs/development.md- Architecture, testing, releasingdocs/rpc-development.md- RPC capture and debuggingdocs/rpc-reference.md- RPC payload structures
When to Suggest CLI vs API
- CLI: Quick tasks, shell scripts, LLM agent automation
- Python API: Application integration, complex workflows, async operations
Pull Request Workflow (REQUIRED)
After creating a PR, you MUST monitor and address feedback:
1. Monitor CI Status
# Check CI status (repeat until all pass)
gh pr checks <PR_NUMBER>Wait for all checks to pass. If any fail, investigate and fix.
2. Check for Review Comments
# Get review comments
gh api repos/teng-lin/notebooklm-py/pulls/<PR_NUMBER>/comments \
--jq '.[] | "File: \(.path):\(.line)\nComment: \(.body)\n---"'3. Address Feedback
For each review comment (especially from gemini-code-assist): 1. Read and understand the feedback 2. Make the suggested fix if it improves the code 3. Commit with a descriptive message referencing the feedback 4. Push and re-check CI 5. Reply to the review thread confirming the fix:
gh api repos/teng-lin/notebooklm-py/pulls/<PR>/comments/<COMMENT_ID>/replies \
-f body="Addressed in commit <SHA>: <brief description>"4. Verify Final State
# Ensure PR is ready to merge
gh pr view <PR_NUMBER> --json state,mergeStateStatus,mergeableImportant: Do NOT consider a PR complete until:
- All CI checks pass
- All review comments are addressed
mergeStateStatusisCLEAN
Contributing to notebooklm-py
For Human Contributors
Getting Started
# Install in development mode
pip install -e ".[all]"
playwright install chromium
# Run tests
pytest
# Run linter
ruff check src/ tests/
# Run formatter
ruff format src/ tests/Code Quality
This project uses ruff for linting and formatting:
# Check for lint issues
ruff check src/ tests/
# Auto-fix lint issues
ruff check --fix src/ tests/
# Check formatting
ruff format --check src/ tests/
# Apply formatting
ruff format src/ tests/Pre-commit hooks (optional but recommended):
pip install pre-commit
pre-commit installPull Request Process
1. Create a feature branch from main 2. Make your changes with clear commit messages 3. Ensure tests pass: pytest 4. Ensure lint passes: ruff check src/ tests/ 5. Ensure formatting: ruff format --check src/ tests/ 6. Submit a PR with a description of changes
Pull Request Quality Expectations
- Reference an issue: PRs should link to an existing issue or clearly describe the problem being solved. If no issue exists, open one first for discussion.
- AI-assisted contributions: Welcome, but the submitter must review, understand, and test the code before submitting. PRs that appear to be unreviewed AI output will be closed.
- No duplicates: Check existing open PRs before submitting. Duplicate PRs for the same issue will be closed in favor of the first or best submission.
- Accurate severity: Claims of "critical" bugs must include evidence (stack trace, reproduction steps, affected users). Routine edge cases are not critical.
- Tested locally: All PRs must include evidence of local testing. The PR template includes a checklist for this.
---
Documentation Rules for AI Agents
IMPORTANT: All AI agents (Claude, Gemini, etc.) must follow these rules when working in this repository.
File Creation Rules
1. No Root Rule - Never create .md files in the repository root unless explicitly instructed by the user.
2. Modify, Don't Fork - Edit existing files; never create FILE_v2.md, FILE_REFERENCE.md, or FILE_updated.md duplicates.
3. Scratchpad Protocol - All analysis, investigation logs, and intermediate work go in docs/scratch/ with date prefix: YYYY-MM-DD-<context>.md
4. Consolidation First - Before creating new docs, search for existing related docs and update them instead.
Protected Sections
Some sections within files are critical and must not be modified without explicit user approval.
Inline markers (source of truth):
<!-- PROTECTED: Do not modify without approval -->
## Critical Section Title
Content that should not be changed by agents...
<!-- END PROTECTED -->For code files:
# PROTECTED: Do not modify without approval
class RPCMethod(Enum):
...
# END PROTECTEDRule: Never modify content between PROTECTED and END PROTECTED markers unless explicitly instructed by the user.
Design Decision Lifecycle
Design decisions should be captured where they're most useful, not in separate documents that become stale.
| When | Where | What to Include |
|---|---|---|
| Feature work | PR description | Design rationale, edge cases, alternatives considered |
| Specific decisions | Commit message | Why this approach was chosen |
| Large discussions | GitHub Issue | Link from PR, spans multiple changes |
| Investigation/debugging | docs/scratch/ | Temporary work, delete when done |
Why not design docs? Separate design documents accumulate and become stale. PR descriptions stay attached to the code changes, are searchable in GitHub, and don't clutter the repository.
Scratch files (docs/scratch/) - Temporary investigation logs and intermediate work. Format: YYYY-MM-DD-<context>.md. Periodically cleaned up.
Naming Conventions
| Type | Format | Example |
|---|---|---|
| Root GitHub files | UPPERCASE.md | README.md, CONTRIBUTING.md |
| Agent files | UPPERCASE.md | CLAUDE.md, AGENTS.md |
| Subfolder README | README.md | docs/examples/README.md |
| All other docs/ files | lowercase-kebab.md | cli-reference.md, contributing.md |
| Scratch files | YYYY-MM-DD-context.md | 2026-01-06-debug-auth.md |
Status Headers
All documentation files should include status metadata:
**Status:** Active | Deprecated
**Last Updated:** YYYY-MM-DDAgents should ignore files marked Deprecated.
Information Management
1. Link, Don't Copy - Reference README.md sections instead of repeating commands. Prevents drift between docs.
2. Scoped Instructions - Subfolders like docs/examples/ may have their own README.md with folder-specific rules.
---
Documentation Structure
docs/
├── cli-reference.md # CLI command reference
├── python-api.md # Python API reference
├── configuration.md # Storage and settings
├── troubleshooting.md # Common issues and solutions
├── stability.md # API versioning policy
├── development.md # Architecture and testing
├── releasing.md # Release checklist
├── rpc-development.md # RPC capture and debugging
├── rpc-reference.md # RPC payload structures
└── examples/ # Runnable example scriptsCLI Reference
Status: Active Last Updated: 2026-03-13
Complete command reference for the notebooklm CLI—providing full programmatic access to all NotebookLM features, including capabilities not exposed in the web UI.
Command Structure
notebooklm [-p PROFILE] [--storage PATH] [--version] [-v] <command> [OPTIONS] [ARGS]Global Options:
-p, --profile NAME- Use a named profile (overridesNOTEBOOKLM_PROFILEenv var)--storage PATH- Override the default storage location-v, --verbose- Enable verbose output--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_AUTH_JSON- Inline authentication JSON (for CI/CD, no file writes needed)NOTEBOOKLM_DEBUG_RPC- Enable RPC debug logging (1to enable)
See Configuration for details on environment variables 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,artifact,agent,generate,download,note,share,research,language,skill,auth,profile - Utility commands -
metadata,doctor
---
Quick Reference
Session Commands
| Command | Description | Example |
|---|---|---|
login | Authenticate via browser | notebooklm login / notebooklm login --browser msedge |
use <id> | 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 |
doctor | Check environment health | notebooklm doctor |
doctor --fix | Auto-fix detected issues | notebooklm doctor --fix |
doctor --json | Output diagnostics as JSON | notebooklm doctor --json |
Profile Commands (notebooklm profile <cmd>)
| 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 <cmd>)
| 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 |
create <title> | Create notebook | notebooklm create "Research" |
delete <id> | Delete notebook | notebooklm delete abc123 |
rename <title> | Rename current notebook | notebooklm rename "New Title" |
summary | Get AI summary | notebooklm summary |
Chat Commands
| Command | Description | Example |
|---|---|---|
ask <question> | Ask a question | notebooklm ask "What is this about?" |
ask -s <id> | Ask using specific sources | notebooklm ask "Summarize" -s src1 -s src2 |
ask --json | Get answer with source references | notebooklm ask "Explain X" --json |
ask --save-as-note | Save response as a note | notebooklm ask "Explain X" --save-as-note |
ask --save-as-note --note-title | Save response with custom note title | notebooklm ask "Explain X" --save-as-note --note-title "Title" |
configure | Set persona/mode | notebooklm configure --mode learning-guide |
history | View conversation history | notebooklm history |
history --clear | Clear local conversation cache | notebooklm history --clear |
history --save | Save history as a note | notebooklm history --save |
history --save --note-title | Save history with custom title | notebooklm history --save --note-title "Summary" |
history --show-all | Show full Q&A content (not preview) | notebooklm history --show-all |
Source Commands (notebooklm source <cmd>)
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 | - | - | source list |
add <content> | URL/file/text | - | source add "https://..." |
add-drive <id> <title> | Drive file ID | - | source add-drive abc123 "Doc" |
add-research <query> | Search query | `--mode [fast | deep], --from [web |
get <id> | Source ID | - | source get src123 |
fulltext <id> | Source ID | --json, -o FILE | source fulltext src123 -o content.txt |
guide <id> | Source ID | --json | source guide src123 |
rename <id> <title> | Source ID, new title | - | source rename src123 "New Name" |
refresh <id> | Source ID | - | source refresh src123 |
delete <id> | Source ID | - | source delete src123 |
delete-by-title <title> | Exact source title | - | source delete-by-title "My Source" |
wait <id> | Source ID | --timeout, --interval | source wait src123 |
source delete <id> accepts only full source IDs or unique partial-ID prefixes. To delete by exact source title, use source delete-by-title "<title>".
Research Commands (notebooklm research <cmd>)
| Command | Arguments | Options | Example |
|---|---|---|---|
status | - | --json | research status |
wait | - | --timeout, --interval, --import-all, --json | research wait --import-all |
Generate Commands (notebooklm generate <type>)
All generate commands support:
--source/-sto select specific sources (repeatable)--jsonfor machine-readable output (returnstask_idandstatus)--languageto override output language (defaults to config or 'en')--retry Nto automatically retry on rate limits with exponential backoff
| Command | Options | Example |
|---|---|---|
audio [description] | `--format [deep-dive\ | brief\ |
video [description] | `--format [explainer\ | brief\ |
cinematic-video [description] | Alias for video --format cinematic; supports the same options | generate cinematic-video "Documentary about quantum physics" |
slide-deck [description] | `--format [detailed\ | presenter], --length [default\ |
revise-slide <description> | -a/--artifact <id> (required), --slide N (required), --wait | generate revise-slide "Move title up" --artifact <id> --slide 0 |
quiz [description] | `--difficulty [easy\ | medium\ |
flashcards [description] | `--difficulty [easy\ | medium\ |
infographic [description] | `--orientation [landscape\ | portrait\ |
data-table <description> | --wait | generate data-table "compare concepts" |
mind-map | (sync, no wait needed) | generate mind-map |
report [description] | `--format [briefing-doc\ | study-guide\ |
Artifact Commands (notebooklm artifact <cmd>)
| Command | Arguments | Options | Example |
|---|---|---|---|
list | - | --type | artifact list --type audio |
get <id> | Artifact ID | - | artifact get art123 |
rename <id> <title> | Artifact ID, title | - | artifact rename art123 "Title" |
delete <id> | Artifact ID | - | artifact delete art123 |
export <id> | Artifact ID | `--type [docs | sheets], --title` |
poll <task_id> | Task ID | - | artifact poll task123 |
wait <id> | Artifact ID | --timeout, --interval | artifact wait art123 |
suggestions | - | -s/--source, --json | artifact suggestions |
Download Commands (notebooklm download <type>)
| Command | Arguments | Options | Example |
|---|---|---|---|
audio [path] | Output path | -a/--artifact, --all, --latest, --name, --force, --dry-run | download audio --all |
video [path] | Output path | -a/--artifact, --all, --latest, --name, --force, --dry-run | download video --latest |
cinematic-video [path] | Output path | Alias for download video; same options as video | download cinematic-video ./documentary.mp4 |
slide-deck [path] | Output path | -a/--artifact, --all, --latest, --name, --force, --dry-run, `--format [pdf\ | pptx]` |
infographic [path] | Output path | -a/--artifact, --all, --latest, --name, --force, --dry-run | download infographic ./info.png |
report [path] | Output path | -a/--artifact, --all, --latest, --name, --force, --dry-run | download report ./report.md |
mind-map [path] | Output path | -a/--artifact, --all, --latest, --name, --force, --dry-run | download mind-map ./map.json |
data-table [path] | Output path | -a/--artifact, --all, --latest, --name, --force, --dry-run | download data-table ./data.csv |
quiz [path] | Output path | -n/--notebook, -a/--artifact, --format (json/markdown/html) | download quiz --format markdown quiz.md |
flashcards [path] | Output path | -n/--notebook, -a/--artifact, --format (json/markdown/html) | download flashcards cards.json |
Note Commands (notebooklm note <cmd>)
| Command | Arguments | Options | Example |
|---|---|---|---|
list | - | - | note list |
create <content> | Note content | - | note create "My notes..." |
get <id> | Note ID | - | note get note123 |
save <id> | Note ID | --title, --content | note save note123 --title "Updated title" |
rename <id> <title> | Note ID, title | - | note rename note123 "Title" |
delete <id> | Note ID | - | note delete note123 |
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 <cmd>)
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 |
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
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.
Agent Commands (notebooklm agent <cmd>)
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 <id> --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 <id> | 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
Session: login
Authenticate with Google NotebookLM via browser.
notebooklm login [OPTIONS]Opens a Chromium browser with a persistent profile. Log in to your Google account, then press Enter in the terminal to save the session.
Options:
--storage PATH- Where to save storage_state.json (default:$NOTEBOOKLM_HOME/storage_state.json)--browser [chromium|msedge]- Browser to use for login (default:chromium). Usemsedgefor Microsoft Edge.
Examples:
# Default (Chromium)
notebooklm login
# Use Microsoft Edge (for orgs that require Edge for SSO)
notebooklm login --browser msedgeSession: use
Set the active notebook for subsequent commands.
notebooklm use <notebook_id>Supports partial ID matching:
notebooklm use abc # Matches abc123def456...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 user@example.com # Add as viewer (default)
notebooklm share add user@example.com --permission editor # Add as editor
notebooklm share add user@example.com -m "Check this out!" # With message
notebooklm share add user@example.com --no-notify # Skip email notification
# Update user permission
notebooklm share update user@example.com --permission editor
# Remove user access
notebooklm share remove user@example.com
notebooklm share remove user@example.com -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 |
Session: 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)--json- Output as JSON (useful for scripts)
Examples:
# Quick local validation
notebooklm auth check
# Full validation with network test
notebooklm auth check --test
# JSON output for automation
notebooklm auth check --jsonChecks performed: 1. Storage file exists and is readable 2. JSON structure is valid 3. Required cookies (SID) are present 4. Cookie domains are correct (.google.com vs regional) 5. (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
Source: add-research
Perform AI-powered research and add discovered sources to the notebook.
notebooklm source add-research <query> [OPTIONS]Options:
--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)--no-wait- Start research and return immediately (non-blocking)
Examples:
# Fast web research (blocking)
notebooklm source add-research "Quantum computing basics"
# Deep research into Google Drive
notebooklm source add-research "Project Alpha" --from drive --mode deep
# Non-blocking deep research for agent workflows
notebooklm source add-research "AI safety papers" --mode deep --no-waitResearch: status
Check research status for the current notebook (non-blocking).
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).
notebooklm research wait [OPTIONS]Options:
-n, --notebook ID- Notebook ID (uses current if not set)--timeout SECONDS- Maximum seconds to wait (default: 300)--interval SECONDS- Seconds between status checks (default: 5)--import-all- Import all found sources when done--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
# 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.
Generate: audio
Generate an audio overview (podcast).
notebooklm generate audio [description] [OPTIONS]Options:
--format [deep-dive|brief|critique|debate]- Podcast format (default: deep-dive)--length [short|default|long]- Duration (default: default)--language LANG- Language code (default: en)-s, --source ID- Use specific source(s) (repeatable, uses all if not specified)--wait- Wait for generation to complete--json- Output as JSON (returnstask_idandstatus)
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"}Generate: video
Generate a video overview.
notebooklm generate video [description] [OPTIONS]Options:
--format [explainer|brief]- Video format--style [auto|classic|whiteboard|kawaii|anime|watercolor|retro|heritage|paper-craft]- Visual style--language LANG- Language code-s, --source ID- Use specific source(s) (repeatable, uses all if not specified)--wait- Wait for generation to complete--json- Output as JSON (returnstask_idandstatus)
Examples:
# Kid-friendly explainer
notebooklm generate video "Explain for 5 year olds" --style kawaii
# 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 --jsonGenerate: revise-slide
Revise an individual slide in an existing slide deck using a natural-language prompt.
notebooklm generate revise-slide <description> --artifact <id> --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:
--wait- Wait for revision to complete--json- Machine-readable output
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).
notebooklm generate report [description] [OPTIONS]Options:
--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)-s, --source ID- Use specific source(s) (repeatable, uses all if not specified)--wait- Wait for generation to complete--json- Output as JSON
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"Download: audio, video, slide-deck, infographic, report, mind-map, data-table
Download generated artifacts to your local machine.
notebooklm download <type> [OUTPUT_PATH] [OPTIONS]Artifact Types and Output Formats:
| Type | Default Extension | Description |
|---|---|---|
audio | .mp4 | Audio overview (podcast) in 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- Skip if file already exists (default)--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.mp3
# 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.
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.html---
Common 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"
# Output: Created notebook: abc123
# 2. Set as active
notebooklm use abc123
# 3. Add a starting source
notebooklm source add "https://en.wikipedia.org/wiki/Climate_change"
# 4. Research more sources automatically (blocking - waits up to 5 min)
notebooklm source add-research "climate change policy 2024" --mode deep --import-all
# 5. Generate a podcast
notebooklm generate audio "Focus on policy solutions and future outlook" --format debate --wait
# 6. Download the result
notebooklm download audio ./climate-podcast.mp3Research → Podcast (Non-blocking with Subagent)
For LLM agents, use non-blocking mode to avoid timeout:
# 1-3. Create notebook and add initial source (same as above)
notebooklm create "Climate Change Research"
notebooklm use abc123
notebooklm source add "https://en.wikipedia.org/wiki/Climate_change"
# 4. Start deep research (non-blocking)
notebooklm source add-research "climate change policy 2024" --mode deep --no-wait
# Returns immediately
# 5. In a subagent, wait for research and import
notebooklm research wait --import-all --timeout 300
# Blocks until complete, then imports sources
# 6. 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 <id>
# 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 <id>
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 <id>
# 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"
done---
Tips for LLM Agents
When using this CLI programmatically:
1. Two ways to specify notebooks: Either use notebooklm use <id> to set context, OR pass -n <id> directly to commands. Most commands support -n/--notebook as an explicit override.
2. 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 --wait for LLM agents—all async operations can take minutes to 30+ minutes. Use artifact wait <id> in a background task or inform the user to check back later.
3. Partial IDs work: notebooklm use abc matches any notebook ID starting with "abc".
4. Check status: Use notebooklm status to see the current active notebook and conversation.
5. Auto-detection: source add auto-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)
6. Error handling: Commands exit with non-zero status on failure. Check stderr for error messages.
7. Deep research: Use --no-wait with source add-research --mode deep to avoid blocking. Then use research wait --import-all in a subagent to wait for completion.
#!/usr/bin/env python3
"""Bulk import sources example.
This script demonstrates:
1. Create a notebook
2. Add multiple sources of different types
3. Handle errors gracefully
4. Report import status
Prerequisites:
pip install "notebooklm-py[browser]"
notebooklm login
Usage:
python bulk-import.py
"""
import asyncio
from notebooklm import NotebookLMClient
# Example sources to import
SOURCES = {
"urls": [
"https://en.wikipedia.org/wiki/Machine_learning",
"https://en.wikipedia.org/wiki/Deep_learning",
],
"youtube": [
"https://www.youtube.com/watch?v=aircAruvnKk", # 3Blue1Brown neural networks
],
"text": [
{
"title": "Project Notes",
"content": """
Key points for our ML research project:
- Focus on transformer architectures
- Compare with traditional RNN approaches
- Benchmark on standard datasets
""",
},
],
}
async def main():
print("=== Bulk Import Example ===\n")
async with await NotebookLMClient.from_storage() as client:
# 1. Create a notebook
print("Creating notebook...")
nb = await client.notebooks.create("Bulk Import Demo")
print(f" Created: {nb.id}\n")
results = {"success": [], "failed": []}
# 2. Import URLs
print("Importing URLs...")
for url in SOURCES["urls"]:
try:
source = await client.sources.add_url(nb.id, url)
results["success"].append(f"URL: {source.title}")
print(f" + {source.title}")
except Exception as e:
results["failed"].append(f"URL: {url} - {e}")
print(f" - Failed: {url}")
# 3. Import YouTube videos (add_url auto-detects YouTube)
print("\nImporting YouTube videos...")
for url in SOURCES["youtube"]:
try:
source = await client.sources.add_url(nb.id, url)
results["success"].append(f"YouTube: {source.title}")
print(f" + {source.title}")
except Exception as e:
results["failed"].append(f"YouTube: {url} - {e}")
print(f" - Failed: {url}")
# 4. Import text content
print("\nImporting text content...")
for item in SOURCES["text"]:
try:
source = await client.sources.add_text(nb.id, item["title"], item["content"])
results["success"].append(f"Text: {source.title}")
print(f" + {source.title}")
except Exception as e:
results["failed"].append(f"Text: {item['title']} - {e}")
print(f" - Failed: {item['title']}")
# 5. Report results
print("\n" + "=" * 40)
print("Import complete!")
print(f" Successful: {len(results['success'])}")
print(f" Failed: {len(results['failed'])}")
if results["failed"]:
print("\nFailed imports:")
for item in results["failed"]:
print(f" - {item}")
print(f"\n Notebook ID: {nb.id}")
print(" (Notebook kept for review - delete manually when done)")
print("\n=== Done! ===")
if __name__ == "__main__":
asyncio.run(main())
#!/usr/bin/env python3
"""Quickstart example for notebooklm-py.
This script demonstrates a complete workflow:
1. Create a notebook
2. Add sources
3. Chat with content
4. Generate a podcast
5. Download the result
Prerequisites:
pip install "notebooklm-py[browser]"
playwright install chromium
notebooklm login # Authenticate first
Usage:
python quickstart.py
"""
import asyncio
from notebooklm import NotebookLMClient
async def main():
print("=== NotebookLM Quickstart ===\n")
async with await NotebookLMClient.from_storage() as client:
# 1. Create a notebook
print("Creating notebook...")
nb = await client.notebooks.create("Quickstart Demo")
print(f" Created: {nb.id} - {nb.title}\n")
# 2. Add a source
print("Adding source...")
url = "https://en.wikipedia.org/wiki/Artificial_intelligence"
source = await client.sources.add_url(nb.id, url)
print(f" Added: {source.title}\n")
# 3. Chat with the content
print("Asking a question...")
result = await client.chat.ask(nb.id, "What are the main topics covered?")
print(f" Answer: {result.answer[:200]}...\n")
# 4. Generate an audio overview
print("Generating podcast (this may take a few minutes)...")
status = await client.artifacts.generate_audio(
nb.id, instructions="Focus on the history and key milestones"
)
print(f" Started generation, task_id: {status.task_id}")
# Wait for completion
final = await client.artifacts.wait_for_completion(
nb.id, status.task_id, timeout=300, poll_interval=10
)
if final.is_complete:
print(f" Complete! URL: {final.url}\n")
# 5. Download (requires browser support)
# output_path = await client.artifacts.download_audio(nb.id, "./podcast.mp3")
# print(f" Downloaded to: {output_path}")
else:
print(f" Generation status: {final.status}\n")
# Cleanup: Delete the demo notebook
print("Cleaning up...")
await client.notebooks.delete(nb.id)
print(" Deleted demo notebook\n")
print("=== Done! ===")
if __name__ == "__main__":
asyncio.run(main())
MIT License
Copyright (c) 2026 Teng Lin
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""Allow running ``python -m notebooklm``."""
from notebooklm.notebooklm_cli import main
if __name__ == "__main__":
main()