Status: Active Last Updated: 2026-09-05
Complete command reference for the notebooklm CLI—providing full programmatic access to all NotebookLM features, including capabilities not exposed in the web UI.
The CLI subsystem diagram explains how commands, services, the transport-neutral application layer, and output rendering fit together. The selection workflow shows how profile, credential, and backend options are resolved.
Exit codes: every command follows the convention documented in CLI Exit-Code Convention (
0success,1user/app error,2system/unexpected,130SIGINT). Two commands intentionally deviate for shell control-flow use (source stale --exit-on-staleopts into an inverted predicate;source waitis three-way); see the doc for details.
notebooklm [-p PROFILE] [--storage PATH] [--backend web|android] [--version] [-v|--quiet] <command> [OPTIONS] [ARGS]-p, --profile NAME- Use a named profile (overridesNOTEBOOKLM_PROFILEenv var)--storage PATH- Override the default storage location--backend web|android- Select the API backend (overridesNOTEBOOKLM_BACKEND; defaultweb). Android requirespip install "notebooklm-py[android]"and a profile bootstrapped withnotebooklm login --master-token --account EMAIL; browser cookies andNOTEBOOKLM_AUTH_JSONare Web-only credentials.-v, --verbose- Increase verbosity (-vfor INFO,-vvfor DEBUG)--quiet- Suppress status output and INFO/WARN log records (only errors survive). Structured--jsonpayloads are still emitted. Mutually exclusive with-v/-vv; combining the two raisesUsageError(exit2).--version- Show version and exit--help- Show help message
NOTEBOOKLM_HOME- Base directory for all config files (default:~/.notebooklm)NOTEBOOKLM_PROFILE- Active profile name (default:default)NOTEBOOKLM_NOTEBOOK- Default notebook ID for any command exposing-n/--notebook. Resolution order:-n/--notebookflag >NOTEBOOKLM_NOTEBOOKenv > active context (notebooklm use) > error. Empty/whitespace values are treated as unset.NOTEBOOKLM_AUTH_JSON- Inline authentication JSON (for CI/CD, no file writes needed)NOTEBOOKLM_HL- Default output language for artifact generation (overridden by--language)NOTEBOOKLM_LOG_LEVEL- Override the package logger level (e.g.DEBUG,INFO,WARNING,ERROR)NOTEBOOKLM_DEBUG_RPC- Enable RPC debug logging (1to enable)NOTEBOOKLM_QUIET_DEPRECATIONS- Suppress deprecation notices (1to enable)
See Configuration for full env-var precedence and CI/CD setup.
- Session commands - Authentication and context management
- Notebook commands - CRUD operations on notebooks
- Chat commands - Querying and conversation management
- Grouped commands -
source,label,collection,artifact,agent,generate,download,note,share,research,language,skill,auth,profile,mcp - Utility commands -
metadata,doctor
| Command | Description | Example |
|---|---|---|
login |
Authenticate via browser | notebooklm login / notebooklm login --browser msedge |
login --master-token |
Headless auth: mint cookies from a durable master token ([headless]) |
notebooklm login --master-token --account you@gmail.com |
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 |
usage |
Show live account compute usage and reset times | notebooklm usage |
usage --categories |
Include usage categories, availability, and estimated costs | notebooklm usage --categories |
usage --json |
Output the full usage snapshot as JSON | notebooklm -p work usage --json |
clear |
Clear current context | notebooklm clear |
auth check |
Diagnose authentication issues | notebooklm auth check |
auth check --test |
Validate with network test | notebooklm auth check --test |
auth check --json |
Output as JSON | notebooklm auth check --json |
auth inspect |
List Google accounts visible to a browser cookie store (read-only) | notebooklm auth inspect --browser chrome |
auth import-cookies |
Import auth cookies from a JSON file (or stdin) into the active profile | notebooklm auth import-cookies cookies.json |
auth logout |
Clear saved cookies and cached browser profile | notebooklm auth logout |
auth refresh |
One-shot SIDTS rotation poke (for OS schedulers) | notebooklm auth refresh |
auth refresh --quiet |
Refresh; suppress success output | notebooklm auth refresh --quiet |
doctor |
Check environment health | notebooklm doctor |
doctor --fix |
Auto-fix detected issues | notebooklm doctor --fix |
doctor --json |
Output diagnostics as JSON | notebooklm doctor --json |
completion <shell> |
Print shell completion script (bash/zsh/fish) |
notebooklm completion zsh > ~/.zfunc/_notebooklm |
Shows the current account's five-hour and weekly compute usage percentages and the reset timestamps
supplied by the server. It requires authentication but no active notebook. Global --profile,
--storage, and --backend web|android options apply as usual.
notebooklm usage
notebooklm usage --categories
notebooklm -p work usage --json
notebooklm --backend android usage --json--categories (alias --actions) adds a table of category codes, names, quota sufficiency, relative
cost tiers, estimated cost percentages, and remaining deferred artifact generations. JSON always
includes these details.
Text percentages are rounded to two decimal places; JSON preserves the returned precision.
The category table uses readable feature names: for example, Cinematic video (code 3), Slide deck
(6), Data table (8), Chat Q&A (18), Source guide (21), and Suggested questions (22). JSON retains the
actions array and exact protocol enum names in kind for scripts. NOS (16) and
NOS_IMAGE_GENERATION (19) are internal server names without a verified public feature mapping;
the table marks that uncertainty instead of guessing a feature name. See the
protocol evidence.
Est. cost* is the server's estimated percentage of budget. In the recorded Flashcards and Quiz
generations it matched the initial reservation against the five-hour window; the weekly debit
was a different percentage, and final usage settled lower. The response does not explicitly name
the estimate's budget window, so the CLI describes the five-hour basis as observed behavior. The
Quota column is the current account-level availability flag for that category, not a separate
five-hour or weekly availability reading.
The JSON object contains:
| Field | Meaning |
|---|---|
status |
ready, disabled (meter not enabled for the account), or skipped (temporarily unavailable) |
enabled, available |
Account eligibility and whether a snapshot is ready |
is_exhausted |
Whether the active window is exhausted; null when unavailable |
active_window |
weekly when weekly usage is at least 100%, otherwise five_hour; null when unavailable |
windows |
Rows with kind (five_hour or weekly), used_percent, remaining_percent, and resets_at (ISO 8601 UTC, e.g. 2026-09-05T18:30:00+00:00) |
actions |
Rows with code, kind (lowercase enum name, e.g. audio_overview), has_sufficient_quota, cost_tier (low, medium, high, very_high, or null), remaining_deferred_artifact_generations, and estimated_cost_percent |
Unknown action codes remain in JSON with kind: null. Absent cost estimates, cost tiers, and
deferred-generation counts are null, distinct from zero. Disabled and skipped meters return empty
windows and actions arrays and exit 0; transport, authentication, and decoding failures use the
standard error envelope and exit nonzero. An exhausted snapshot also exits 0: the command reports
usage rather than enforcing it.
This is the live compute meter, separate from static published plan limits. Percentages may change after generation settles. Advertised action costs are estimates, not final charges or a credit balance; reset timestamps are reported directly without calculating a countdown.
| 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 |
| 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.
| Command | Description | Example |
|---|---|---|
list |
List all notebooks | notebooklm list |
list --json |
Output as JSON | notebooklm list --json |
list --limit N |
Show at most N notebooks (default: unlimited) | notebooklm list --limit 10 |
list --no-truncate |
Do not truncate the Title column | notebooklm list --no-truncate |
use <id> |
Set active notebook (verifies existence by default) | notebooklm use abc123 |
use <id> --force |
Skip the existence check (offline / debugging) | notebooklm use abc123 --force |
use <id> --json |
Emit {active_notebook_id, success, verified, notebook} envelope |
notebooklm use abc --json |
create <title> |
Create notebook (does not change active context) | notebooklm create "Research" |
create <title> --use |
Create notebook and make it the active context | notebooklm create "Research" --use |
create <title> --json |
JSON envelope; with --use includes active_notebook_id |
notebooklm create "X" --use --json |
copy <title> |
Copy the current notebook, including sources and Studio artifacts | notebooklm copy "Research — Copy" |
copy <title> -n <id> |
Copy a specific notebook (partial IDs accepted) | notebooklm copy "Research — Copy" -n abc123 |
copy <title> --use --json |
Make the copy active and emit {source_notebook_id, notebook, active_notebook_id} |
notebooklm copy "Research — Copy" --use --json |
delete -n <id> |
Delete notebook (uses current notebook if -n omitted) |
notebooklm delete -n abc123 |
delete -n <id> -y |
Skip confirmation | notebooklm delete -n abc123 -y |
delete -n <id> --json |
Emit {notebook_id, success} envelope (plus context_cleared: true when deleting the active notebook); requires -y (refuses to prompt in JSON mode) |
notebooklm delete -n abc123 -y --json |
rename <title> |
Rename current notebook | notebooklm rename "New Title" |
summary |
Get AI summary | notebooklm summary |
summary --topics |
Include suggested topics | notebooklm summary --topics |
| Command | Description | Example |
|---|---|---|
ask <question> |
Ask a question (auto-resumes the last conversation) | notebooklm ask "What is this about?" |
ask - |
Read question from stdin (Unix - convention) |
echo "what is X?" | notebooklm ask - |
ask --prompt-file PATH |
Read question from file (use - for stdin) |
notebooklm ask --prompt-file q.txt |
ask -c <id> |
Continue a specific conversation | notebooklm ask -c conv_abc "..." |
ask --new |
DESTRUCTIVE. Permanently delete the notebook's current server-side conversation (turns are not recoverable) and start fresh on the next ask. Prompts for confirmation with the conversation's short id; pass --yes/-y to skip the prompt. --json implies --yes so scripted callers don't hang on stdin. Closes the workaround documented in #659. |
notebooklm ask --new -y "start fresh" |
ask --new --yes (alias -y) |
Skip the --new destructive-delete confirmation prompt. |
notebooklm ask --new --yes "..." |
ask -s <id> |
Limit to specific source IDs (repeatable) | notebooklm ask "Summarize" -s src1 -s src2 |
ask --json |
Get answer with source references | notebooklm ask "Explain X" --json |
ask --request-timeout N |
Per-invocation HTTP request/read timeout in seconds for chat. Defaults to the library chat timeout. --timeout is a back-compat alias for the same flag. |
notebooklm ask "long prompt" --request-timeout 120 |
ask --save-as-note |
Save response as a note. When the answer contains [N] citations, the saved note preserves interactive hover-anchored citation links matching the NotebookLM web UI's "Save to note" behavior (issue #660). Answers without citations fall back to a plain-text note. |
notebooklm ask "Explain X" --save-as-note |
ask --save-as-note --note-title |
Save response with custom note title. The NotebookLM server may apply smart-title generation for citation-rich saves and override the requested title; the success message reflects what the server actually stored. | notebooklm ask "Explain X" --save-as-note --note-title "Title" |
suggest-prompts |
Get AI-suggested prompts for the notebook (each a title plus a ready-to-send instruction for ask) |
notebooklm suggest-prompts |
suggest-prompts --mode N |
Select the suggestion surface (1-10, default 4): 1=audio deep-dive, 2=audio brief, 3=video explainer, 4=chat questions, 5=audio critique, 6=audio debate, 8=quiz, 9=flashcards, 10=video short. Out-of-range exits 1. | notebooklm suggest-prompts --mode 8 |
suggest-prompts --query TEXT |
Free-text steer for the kind of prompts to suggest | notebooklm suggest-prompts --query "key risks" |
suggest-prompts -s <id> |
Limit to specific source IDs (repeatable; defaults to all sources) | notebooklm suggest-prompts -s src1 -s src2 |
suggest-prompts --json |
Machine-readable output ({notebook_id, suggestions, count}) |
notebooklm suggest-prompts --json |
suggest-next-steps |
Grounded follow-up questions for the notebook (the chips shown under a chat answer, without needing a conversation). -s <id> scopes to sources; --json → {notebook_id, suggestions: [{question, type_code}], count} |
notebooklm suggest-next-steps --json |
configure --mode |
Set predefined chat mode (default, learning-guide, concise, detailed) |
notebooklm configure --mode learning-guide |
configure --persona |
Set custom persona prompt (up to 10,000 chars) | notebooklm configure --persona "Act as a tutor" |
configure --response-length |
Response verbosity (default, longer, shorter) |
notebooklm configure --response-length longer |
configure --json |
Machine-readable output | notebooklm configure --mode concise --json |
history |
View conversation history | notebooklm history |
history -l N |
Maximum number of Q&A turns to show | notebooklm history -l 5 |
history --json |
Machine-readable output | notebooklm history --json |
history --clear |
Clear local conversation cache | notebooklm history --clear |
history --save |
Save history as a note | notebooklm history --save |
history --save -t "Title" |
Save history with custom title | notebooklm history --save -t "Summary" |
history --show-all |
Show full Q&A content (not preview) | notebooklm history --show-all |
history --no-truncate |
Disable the 50-char preview cap on the Question/Answer columns in the table view (the existing -l/--limit flag is unchanged: it caps the number of Q&A turns fetched server-side) |
notebooklm history --no-truncate |
Setting just one of configure --persona / --response-length merges with the current settings — the field you omit is preserved, not reset. A bare configure with no flags resets all custom chat settings to their defaults. (A --mode preset cannot be combined with --persona/--response-length.)
Supported source types: URLs, YouTube videos, files (PDF, text, Markdown, Word, audio, video, images), Google Drive documents, and pasted text.
| Command | Arguments | Options | Example |
|---|---|---|---|
list |
- | --json, --limit N, --no-truncate, --label, --status |
source list --limit 20 --no-truncate |
search <query> |
Passage-search query | -s/--source ID (repeatable), --limit N, --json |
source search "revenue growth" --limit 5 |
add <content> |
URL/file/text (use - for stdin) |
--title, --type, --timeout, --follow-symlinks, --allow-internal (URL sources only), --fallback-fetch, --cleanup-on-failure, --json (file-source --mime-type overrides extension inference — see detailed section) |
source add "https://..." --timeout 90 |
add-drive <id> <title> |
Drive file ID, title | --mime-type [google-doc|google-slides|google-sheets|pdf], --json |
source add-drive abc123 "Doc" --mime-type google-slides |
add-drive-file <id> |
Drive file ID or share URL | --title, --wait, --json |
source add-drive-file abc123 --title "Notes" --wait |
books |
- | --json |
source books |
add-book <content-id> |
Play Books volume id (from books) |
--wait, --json |
source add-book QhsZEAAAQBAJ -n nb123 --wait |
add-research [query] |
Search query (or --prompt-file - for stdin) |
--mode [fast|deep], --from [web|drive], --import-all, --cited-only, --no-wait, --timeout, --prompt-file PATH |
source add-research "AI" --mode deep --no-wait |
get <id> |
Source ID | --json |
source get src123 |
fulltext <id> |
Source ID | --json, -o FILE, --force, --no-clobber, -f [text|markdown] |
source fulltext src123 -f markdown -o out.md (-f markdown requires the markdown extra: pip install "notebooklm-py[markdown]" — full extras matrix: docs/installation.md#optional-extras-matrix) |
guide <id> |
Source ID | --json |
source guide src123 |
stale <id> |
Source ID | --exit-on-stale, --json |
source stale src123 (exit 0 on success; pass --exit-on-stale for the back-compat inverted predicate — see exit codes) |
wait <id> |
Source ID | --timeout, --interval, --json |
source wait src123 --timeout 300 --interval 5 |
clean |
- | --dry-run, -y/--yes, --json |
source clean --dry-run |
rename <id> <title> |
Source ID, new title | --json |
source rename src123 "New Name" |
refresh <id> |
Source ID | --json |
source refresh src123 (exit 1 with the server's reason when the refresh is rejected — v0.9.0, #2290) |
delete <id> |
Source ID | -y/--yes, --json |
source delete src123 -y |
delete-by-title <title> |
Exact source title | -y/--yes, --json |
source delete-by-title "My Source" |
add-async <url>... |
One or more URLs | --allow-internal, --json |
source add-async https://a.example https://b.example — one non-blocking AddSourcesAsync call; URLs pass the same scheme/SSRF gate as source add; prints the queued ids immediately (--json → {notebook_id, sources, count, requested}; use source wait / source list for readiness) |
append <id> <text> |
Source ID (or prefix), text (- for stdin) |
--header, --json |
source append src123 "Addendum…" — appends the text at the end of the source in place |
copy <id>... --to <notebook> |
Source IDs (or prefixes), target notebook id/prefix | --to (required), --json |
source copy src1 src2 --to 1a2b3c — copies into another notebook; prints original → copy pairs. A partial copy lists the ids left behind (not_copied under --json) and exits 1 |
All source subcommands also accept -n/--notebook ID (resolves via flag > NOTEBOOKLM_NOTEBOOK env > active context).
source add-async intentionally remains the distinct non-blocking AddSourcesAsync
command; it is not changed into the public settled sources.add_urls_batch() workflow.
When any CLI command receives an escaping exception with public batch metadata, both
text and JSON error output preserve the per-item commit states and partial resource IDs.
source search <query> searches the indexed passages of every source in the
notebook and orders matches by global relevance rank (lower is better). Repeat
-s/--source ID to restrict the search; full IDs and unique prefixes are both
accepted. The optional --limit is applied after global ranking. Text mode
shows rank, source ID, source-relative span, and passage text. --json emits
the full result array directly:
[
{
"source_id": "source-uuid",
"text": "The matching passage text...",
"rank": 1,
"start": 14202,
"end": 14733
}
]start and end are null when the backend provides no usable span.
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>".
source clean automatically removes duplicate, error, and access-blocked sources; combine with --dry-run to preview the candidate set first.
source fulltext -o FILE auto-renames an existing output path by default (FILE -> FILE (2), etc.) instead of overwriting it. Pass --force to overwrite intentionally, or --no-clobber to fail if the path exists.
source stale reports whether a URL/Drive source needs a refresh. By default it follows the standard CLI exit convention (0 on success, 1 on error); branch on the JSON stale/fresh fields (or stdout text) for the freshness verdict. Pass --exit-on-stale to opt into the back-compat inverted predicate (0 = stale, 1 = fresh) for shell idioms like if notebooklm source stale --exit-on-stale ID; then refresh; fi.
source list also accepts --label <id|name> to list only the sources in a given label (a saved selection). The selector resolves a label id (or partial prefix) or an exact label name; see Label Commands.
source list --status <state> restricts the listing to one ingestion status — ready, processing, error, preparing, or unknown. The choices are derived from SourceStatus, so they cannot drift from the labels the Status column renders, and the filter is applied inside the fetch, so count in --json always matches the rows shown. It composes with --label.
A file add that fails after its source row is registered leaves that row in place deliberately — it is the evidence of what happened, and it still counts against the notebook's source quota. The row sits at preparing, not error, so the status a caller reaches for first finds nothing (#2138):
notebooklm source list --status preparing # the reconciliation query
notebooklm source delete <id> # once you have confirmed it is stuckRows that are genuinely mid-upload also report preparing, so this filter cannot by itself tell "abandoned" from "in flight" — re-run it a minute apart and act only on rows that persist. Nothing is deleted automatically; that posture is deliberate (see #2110).
When the failing add raised in your own process, you do not need to search at all: the exception carries the retained row's id directly, via getattr(exc, "source_id", None).
Both commands emit the same --json source row:
{
"id": "ef72c03c-…", "title": "Rubisco Research", "type": "google_docs", "url": null,
"status": "ready", "status_id": 2, "created_at": "2026-01-23T18:42:00",
"drive_document_id": "1oAk_INJ…", "drive_status": "deleted", "is_drive_degraded": true
}(source list prefixes each row with a 1-based index.)
drive_document_id— the Google Drive file id. A Drive source carries nourl, so this is the only field tying it back to the file it was created from;source add-drivematches on it to stay idempotent.drive_status— Drive-side health:inaccessible/syncing/active/deleted/gen_ai_access_denied, orunknownfor a code this client cannot map.nullmeans the row made no Drive-health claim at all, which is a different answer from"unknown".is_drive_degraded—trueonly when the backend explicitly reported a non-healthy Drive state.falsemeans "nothing degraded was reported" — equally true for a non-Drive source, foractive, and for an unreadable code — not "the file is confirmed present".
All three keys are present on every row (null/false on non-Drive sources), so jq '.sources[] | select(.drive_status == "deleted")' needs no // empty guard.
Which rows are Drive-backed? drive_document_id and drive_status are decoded from structurally independent parts of the response, so either one being non-null means the row is Drive-backed — neither alone is the authoritative test. In particular, the common case is an id with no health claim ({"drive_document_id": "1oAk…", "drive_status": null}), so filtering on drive_status != null will miss most Drive sources.
Unlike the MCP/REST surfaces, the CLI ships no raw Drive status code beside the label. It would carry no extra information (an unmappable code is replaced by a client-side sentinel before it reaches output), and its code space collides adversarially with status_id's — 2/3 mean ready/error for ingestion but syncing/active for Drive, so a consumer reasoning by analogy would select exactly the wrong rows.
status reports NotebookLM's own ingestion, which completes and stays complete after the Drive file is deleted or unshared. A source therefore reads "status": "ready" while "drive_status": "deleted" — answers grounded on it may be stale.
Human (non---json) output reflects this without adding a column:
source listappends the Drive verdict to the Status cell —ready (drive: deleted)— but only for a row the backend reports as degraded (is_drive_degraded), whatever that row's ingestion status is. An unreadable code (unknown) is not flagged here; it is not evidence of degradation, and the table should not cry wolf on protocol drift. Note that Rich sizes columns table-wide, so a single annotated row does re-flow the whole table; that cost is only paid when something is actually wrong.source getaddsDrive File ID:andDrive Status:lines, each shown only when that field is present.
Cross-surface naming. MCP and REST spell this axis
drive_status(raw code) +drive_status_label(string); the CLI usesdrive_statusfor the string, matching its own long-standingstatus(label) /status_id(code) pairing. Soselect(.drive_status == "deleted")is right for the CLI and wrong for MCP/REST, where that key holds an integer. Both surfaces resolve the label through the same mapping helper, so the vocabulary never diverges — only the key name.
Source labels group a notebook's sources into topic buckets. A <id|name> argument accepts a label id (or partial prefix) or an exact label name; an ambiguous name lists the matching ids so you can disambiguate.
| Command | Arguments | Options | Example |
|---|---|---|---|
list |
- | --json |
label list -n nb123 |
sources <id|name> |
Label id or name | --json |
label sources Papers |
generate |
- | --scope [all|unlabeled], -y/--yes, --json |
label generate --scope all -y |
create <name> |
Label name | --emoji 📄, --json |
label create "Papers" --emoji 📄 |
rename <id|name> <new_name> |
Label ref, new name | --json |
label rename Papers "Research Papers" |
emoji <id|name> <emoji> |
Label ref, emoji | --json |
label emoji Papers 📚 |
add <id|name> <source_id>... |
Label ref, one+ source ids | --json |
label add Papers src123 src456 |
remove <id|name> <source_id>... |
Label ref, one+ source ids | --json |
label remove Papers src123 |
delete <id|name>... |
One+ label refs | -y/--yes, --json |
label delete Papers -y |
All label subcommands accept -n/--notebook ID (resolves via flag > NOTEBOOKLM_NOTEBOOK env > active context).
label generate --scope all (wipes and regenerates every label with new ids) and label delete are destructive and require -y/--yes to confirm (or an interactive prompt). label generate --scope unlabeled (the default) only labels currently-unlabeled sources and needs no confirmation. label add appends sources (existing members survive; labels may overlap) and label remove un-assigns sources from the label only — the sources stay in the notebook (and in any other label). label delete removes the label only — its sources become unlabeled, not deleted. source_id arguments to label add/label remove accept partial-prefix matching like every other source-id command.
Collections group whole notebooks into named, account-level buckets (playlist-style) — the sibling of label, which groups sources within a notebook. A notebook can belong to multiple collections. A <id|name> argument accepts a collection id (or partial prefix) or an exact collection name; an ambiguous name lists the matching ids so you can disambiguate.
| Command | Arguments | Options | Example |
|---|---|---|---|
list |
- | --json |
collection list |
notebooks <id|name> |
Collection id or name | --json |
collection notebooks "Research Q3" |
create <name> |
Collection name | --json |
collection create "Research Q3" |
rename <id|name> <new_name> |
Collection ref, new name | --json |
collection rename "Research Q3" "Research Q4" |
add <id|name> <notebook_id>... |
Collection ref, one+ notebook ids | --json |
collection add "Research Q3" nb123 nb456 |
remove <id|name> <notebook_id>... |
Collection ref, one+ notebook ids | --json |
collection remove "Research Q3" nb123 |
delete <id|name>... |
One+ collection refs | -y/--yes, --json |
collection delete "Research Q3" -y |
Collections are account-level, so — unlike label — the collection commands take no -n/--notebook option; their membership arguments are notebooks. collection add appends notebooks (existing members survive; a notebook may belong to multiple collections) and collection remove un-assigns a notebook from that collection only — the notebook is not deleted and stays in any other collection. collection delete (destructive, requires -y/--yes) removes the collection only, never its member notebooks. notebook_id arguments to collection add/collection remove accept partial-prefix matching like every other notebook-id command.
collection create sends the mutation once, then exits with an unknown-write
error because the current response does not identify which collection row
belongs to this call. Inspect collection list and verify the intended ID before
using it; reported IDs are reconciliation candidates, not ownership claims.
| Command | Arguments | Options | Example |
|---|---|---|---|
discover |
[QUERY] |
-n/--notebook, --mode {default,raw,curious,curious_raw}, --json |
research discover "history of the transistor" |
status |
- | -n/--notebook, --run-id/--task-id, --json |
research status --run-id <run_id> |
wait |
- | -n/--notebook, --run-id/--task-id, --timeout, --interval, --import-all, --cited-only, --json |
research wait --run-id <run_id> --import-all |
import |
- | -n/--notebook, --run-id, --cited-only, --timeout, --max-sources, --allow-duplicate, --json |
research import |
cancel |
RUN_ID |
-n/--notebook, --json |
research cancel <run_id> |
All generate commands (except the synchronous mind-map) accept the same uniform polling-flag surface:
-n, --notebook IDto target a specific notebook (also resolved fromNOTEBOOKLM_NOTEBOOK/ active context)-s, --source IDto select specific sources (repeatable)--jsonfor machine-readable output (returnstask_idandstatus)--wait / --no-waitto block until completion (default:--no-wait— returns immediately with atask_id)--timeout SECONDSto cap the--waitbudget (defaults: 1200s for audio, 1800s for video, 3600s for cinematic-video, and 300s for quiz/flashcards/slide-deck/infographic/data-table/report/revise-slide). No-op without--wait.--interval SECONDSto tune polling cadence (default: 2). No-op without--wait.--retry Nto automatically retry on rate limits with exponential backoff--prompt-file PATHto read the description/query from a file (or-for stdin) instead of the command line. Mutually exclusive with the positional argument; useful for long prompts that exceed shell length limits.
Language-aware generate commands (audio, video, cinematic-video, report, infographic, slide-deck, data-table, mind-map) also support:
--language LANGto override output language (precedence:--language>NOTEBOOKLM_HLenv > config >'en')
quiz, flashcards, and revise-slide do not accept --language.
All generation commands construct exact frozen request variants governed by the same neutral domain validation used by MCP and REST. The CLI maps its Click-owned inputs directly into most variants; video uses the shared builder for its cross-option rules. MCP and REST use that central builder and per-kind option table across generation kinds. The CLI still owns its presentation contract: option spelling, progress and wait messages, text/JSON rendering, and exit codes are not embedded in the request, event, or outcome types.
| Command | Arguments | Type-specific options | Example |
|---|---|---|---|
audio [description] |
Instructions | --format [deep-dive|brief|critique|debate], --length [short|default|long] |
generate audio "Focus on history" |
video [description] |
Instructions | --format [explainer|brief|cinematic|short], --style [auto|custom|classic|whiteboard|kawaii|anime|watercolor|retro-print|heritage|paper-craft] (not for --format cinematic/short), --style-prompt TEXT (required with --style custom; rejected with --format cinematic/short) |
generate video "Explainer for kids" |
cinematic-video [description] |
Instructions | Alias for video --format cinematic |
generate cinematic-video "Documentary about quantum physics" |
slide-deck [description] |
Instructions | --format [detailed|presenter], --length [default|short] |
generate slide-deck |
revise-slide <description> |
Revision instructions | -a/--artifact <id> (required), --slide N (required) |
generate revise-slide "Move title up" --artifact <id> --slide 0 |
quiz [description] |
Instructions | --difficulty [easy|medium|hard], --quantity [fewer|standard|more] |
generate quiz --difficulty hard |
flashcards [description] |
Instructions | --difficulty [easy|medium|hard], --quantity [fewer|standard|more] |
generate flashcards |
infographic [description] |
Instructions | --orientation [landscape|portrait|square], --detail [concise|standard|detailed], --style [auto|sketch-note|professional|bento-grid|editorial|instructional|bricks|clay|anime|kawaii|scientific] |
generate infographic |
data-table <description> |
Instructions | (uniform options only) | generate data-table "compare concepts" |
mind-map |
- | --kind [interactive|note-backed], --instructions TEXT (no --wait / --timeout / --interval / --retry / --prompt-file) |
generate mind-map --kind interactive |
report [description] |
Instructions | --format [briefing-doc|study-guide|blog-post|custom], --append TEXT (no effect with --format custom) |
generate report --format study-guide |
Two kinds of mind map (issue #1256). NotebookLM has two distinct mind-map objects.
generate mind-map --kind interactive(the default) builds the interactive kind — a studio artifact (the one the web app now creates) that is polled to completion.generate mind-map --kind note-backedbuilds the note-backed kind — a JSON node tree stored as a note, generated synchronously. Both produce the same{mind_map, note_id, kind}output, appear inartifact list --type mind-map, and download viadownload mind-map.--instructionsis a free-text prompt that steers generation: the interactive kind applies it reliably (it travels in theCREATE_ARTIFACTprompt slot the server honors and thatartifact get-promptreads back); the note-backed kind passes it through toGENERATE_MIND_MAP, but the server may not always act on it.
| Command | Arguments | Options | Example |
|---|---|---|---|
list |
- | --type [all|audio|video|slide-deck|quiz|flashcard|infographic|data-table|mind-map|report|fantasy-map|file], --limit N, --no-truncate, --json |
artifact list --type audio --limit 5 |
get <id> |
Artifact ID | --json |
artifact get art123 |
get-prompt <id> |
Artifact ID | --json |
artifact get-prompt art123 |
rename <id> <title> |
Artifact ID, title | --json |
artifact rename art123 "Title" |
delete <id> |
Artifact ID | -y/--yes, --json |
artifact delete art123 -y |
export <id> |
Artifact ID | --title TEXT (required), --type [docs|sheets], --json |
artifact export art123 --title "My Doc" --type sheets |
poll <task_id> |
Task ID (from generate <type>) |
--json |
artifact poll task123 |
wait <id> |
Artifact ID (from artifact list) |
--timeout (default: 300), --interval (default: 2), --json |
artifact wait art123 --timeout 600 |
retry <id> |
Artifact ID (from artifact list) |
--wait, --timeout (default: 300), --interval (default: 2), --json |
artifact retry art123 --wait |
suggestions |
- | --json |
artifact suggestions |
copy <id>... --to <notebook> |
Artifact IDs (or prefixes), target notebook id/prefix | --to (required), --json |
artifact copy art1 --to 1a2b3c — copies Studio artifacts into another notebook; prints original → copy pairs. A partial copy lists the ids left behind (not_copied under --json) and exits 1 |
choices |
- | --json |
artifact choices — the Studio "Customize" option tables (audio/video/slide-deck format codes + report presets with their full directives under --json); account-level; -n is optional and only fills the request's project_id slot |
All artifact subcommands also accept -n/--notebook ID.
Note:
artifact deleteon a Mind Map clears its content rather than removing the artifact entry — Mind Maps are stored alongside notes and Google may garbage-collect cleared entries later. The CLI prints aCleared mind map:message instead ofDeleted artifact:when this happens.
Common confusion:
pollvswaitID kind. Both commands accept the same identifier — the API returns one ID that serves as both the generationtask_id(during creation) and theartifact_id(once the row appears inartifact list). The split is operational, not semantic:
artifact poll <task_id>— single non-blocking status check. Pass the rawtask_idreturned bynotebooklm generate <type>straight through.polldoes not prefix-match againstartifact list, so it works immediately after ageneratecall returns, before the artifact has appeared in any list response.artifact wait <artifact_id>— blocks (with exponential backoff) until the artifact iscompleted,failed, or--timeoutelapses. Accepts a full UUID or a unique prefix that resolves againstartifact listvia the standard partial-ID resolver.Rule of thumb: just generated something? use
poll. Found it inartifact list? usewait. You can pass the same string to both — the kind label is about lifecycle stage, not about a different identifier format.
artifact retry <artifact_id>re-runs generation for a failed artifact in place (the web UI "Retry" button) — the artifact is not deleted first and the same ID is preserved. Accepts a full UUID or a unique prefix. By default it kicks off the retry and returns immediately (Retry started: <id>); pass--waitto block until the retried generation reaches a terminal state. A synchronous refusal (rate limit / quota / not-retryable) exits non-zero with a typed error rather than reporting a started task. A retry may itself fail again provider-side, in which case you can runartifact retryagain against the same ID.
Every download subcommand accepts the same selection / safety / output flag set: -n/--notebook ID, -a/--artifact ID, --all, --latest (default), --earliest, --name TEXT (fuzzy title match), --dry-run, --force, --no-clobber (don't overwrite existing files: a single download fails, --all skips them; default is auto-rename), and --json.
| Command | Arguments | Type-specific options | Example |
|---|---|---|---|
audio [path] |
Output path | (none) | download audio --all |
video [path] |
Output path | (none) | download video --latest |
cinematic-video [path] |
Output path | Alias for download video; cinematic and standard videos share the same artifact type |
download cinematic-video ./documentary.mp4 |
slide-deck [path] |
Output path | --format [pdf|pptx] (default: pdf) |
download slide-deck ./slides.pdf |
infographic [path] |
Output path | (none) | download infographic ./info.png |
report [path] |
Output path | (none) | download report ./report.md |
mind-map [path] |
Output path | (none) | download mind-map ./map.json |
data-table [path] |
Output path | (none) | download data-table ./data.csv |
quiz [path] |
Output path | --format [json|markdown|html] (default: json) |
download quiz --format markdown quiz.md |
flashcards [path] |
Output path | --format [json|markdown|html] (default: json) |
download flashcards cards.json |
| Command | Arguments | Options | Example |
|---|---|---|---|
list |
- | --json |
note list --json |
create [content] |
Note content (or - for stdin) |
--content TEXT (or - for stdin; mutex with positional), -t/--title TEXT, --json |
cat notes.md | note create - |
get <id> |
Note ID | --json |
note get note123 |
save <id> |
Note ID | --title, --content, --json |
note save note123 --title "Updated title" |
rename <id> <title> |
Note ID, title | --json |
note rename note123 "Title" |
delete <id> |
Note ID | -y/--yes, --json |
note delete note123 -y |
All note subcommands also accept -n/--notebook ID.
source get/artifact get/note getexit1on not-found (BREAKING). All threegetcommands now exit1when the requested ID does not resolve to an existing item, matching the rest of the CLI's user-error convention. Under--jsonthe failure body is the standard typed error envelope ({"error": true, "code": "NOT_FOUND", "message": "...", "id": "...", "notebook_id": "..."}); without--jsonthe message is written to stderr. The previous behavior was exit0with a "not found" line on stdout. The pre-existing "no partial-ID match" branch (raised by_resolve_partial_idas aClickException) was already exit1and is unchanged. See CLI Exit-Code Convention for migration guidance.
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 --jsonManage NotebookLM agent skill integration.
| Command | Description | Example |
|---|---|---|
install |
Install/update the skill for claude, .agents, or both |
skill install --target all |
status |
Check installed targets and version info | skill status --scope project |
uninstall |
Remove one or more installed targets | skill uninstall --target agents |
show |
Display the packaged skill or an installed target | skill show --target source |
package |
Build a Claude-uploadable skill archive (chat/Cowork) | skill package -o dist/ |
Defaults:
skill installuses--scope user --target allclaudemaps to.claude/skills/notebooklm/SKILL.mdagentsmaps to.agents/skills/notebooklm/SKILL.mdshow --target sourceprints the canonical packaged skill file- Project-scope installs support
--dry-run,--no-clobber, and--force; these flags are rejected for user-scope installs.
The packaged wheel includes the repo-root SKILL.md, so the same skill content powers notebooklm skill install, GitHub discovery, and npx skills add teng-lin/notebooklm-py.
Codex does not use the skill subcommand. In this repository it reads the root AGENTS.md file and invokes the notebooklm CLI or Python API directly.
Install the NotebookLM MCP server block into supported MCP client configs.
| Command | Description | Example |
|---|---|---|
install <client> |
Configure claude-desktop, claude-code, cursor, or windsurf |
mcp install claude-desktop |
install <client> --config-path PATH |
Write a non-standard config path | mcp install cursor --config-path ./mcp.json |
The installer writes a notebooklm server entry that launches notebooklm-mcp
through uvx. Re-running is idempotent and preserves unrelated servers in the
same config file. See MCP server guide.
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.
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 |
Authenticate with Google NotebookLM via browser.
Python equivalent: load saved credentials with
NotebookLMClient.from_storage(...)and use it as an async context manager. The CLI's interactive browser-login flow has no Python counterpart — runnotebooklm loginonce to seedstorage_state.json, then drive the API from Python.
notebooklm login [OPTIONS]By default, opens a Chromium browser with a persistent profile. Complete the Google login in the browser window — the CLI detects the redirect back to either personal host (notebook.google.com or notebooklm.google.com) and saves the session automatically (no terminal keystroke required). The wait window is 5 minutes by default; if login is not detected before then, the command exits with a retry hint. Use --browser-timeout to extend that human-interaction window, --browser msedge for Microsoft Edge, or --browser-cookies <browser> to import cookies from an already-logged-in browser without launching Playwright.
Options:
--storage PATH- Where to save storage_state.json (default:$NOTEBOOKLM_HOME/profiles/<profile>/storage_state.json)--browser [chromium|msedge|chrome]- Browser to use for login (default:chromium). Usechromefor system Google Chrome (workaround when bundled Chromium crashes, e.g. macOS 15+); usemsedgefor Microsoft Edge. Note: onlychromiumis auto-installed by the CLI on first login (~170 MB Chromium download);--browser msedgeand--browser chromerequire the corresponding browser to be already installed on your system.--browser-timeout SECONDS- Human-interaction window for ordinary headed login and browser-assisted master-token bootstrap (default:300).--browser-cookies <auto|chrome|edge|firefox|safari|brave|arc|...>- Read cookies from an installed browser instead of launching Playwright. Pass an explicit browser name, orautoto let rookie-cookies auto-detect. For Chromium-family user profiles, usechrome::<profile-name-or-directory>(for examplechrome::Profile 1orbrave::Work) to extract from one profile explicitly. For Firefox Multi-Account Containers, usefirefox::<container-name>to extract from a single container, orfirefox::nonefor the no-container default — unscopedfirefoxmerges every container's cookies (and emits a warning when that's happening). Requirespip install "notebooklm-py[cookies]"(full extras matrix: docs/installation.md#optional-extras-matrix).--account EMAIL- Pick a signed-in Google account by email when several are present in the browser. Saves to the active profile by default; use--profile-namefor a separate named profile or--storagefor an exact path. Only valid with--browser-cookies.--all-accounts- Extract every Google account signed in to the browser into separate profiles named from each account email. Only valid with--browser-cookies.--update- With--all-accounts: when an account's natural profile name (e.g.aliceforalice@gmail.com) already exists but has no account metadata, update that profile in place instead of creating a suffixedalice-2. Profiles that already bind a different email still get a suffix to avoid clobbering. Only valid with--all-accounts.--profile-name NAME- Write a targeted--accountimport to this named profile instead of the active profile. Only valid with--browser-cookies.--fresh- Start with a clean browser session (deletes the cached browser profile). Use to switch Google accounts. With explicit--storage, a pre-existing browser sidecar is deleted only when it carries NotebookLM's ownership marker or is the canonical legacy/named-profile layout; arbitrary unowned directories are refused. Has no effect with--browser-cookies.--include-domains LABEL[,LABEL...]- Opt in to extracting sibling-product cookies (default: required Google auth/Drive cookies only). Supported labels:youtube,docs,myaccount,mail,all. Pass labels comma-separated or repeat the flag.
Master-token (headless) options — mint/refresh web cookies from a durable Google master token, no per-session browser. Requires gpsoauth, included by pip install "notebooklm-py[headless]" or pip install "notebooklm-py[android]". Full guide: installation.md#d-headless-server-or-ci.
--master-token- Bootstrap headless auth. Requires--account EMAIL. A visible browser opens Google's EmbeddedSetup to capture the single-useoauth_token(needs[browser]), or pass it with--oauth-token. Exchanges it for a durable master token (saved0600atmaster_token.json), mints cookies intostorage_state.json, and verifies by listing notebooks.--master-token-refresh- Legacy forced re-mint; prefernotebooklm auth refresh. It still unconditionally replacesstorage_state.jsonfrom the stored master token and preserves CLI context.--oauth-token VALUE- Provide the single-use EmbeddedSetupoauth_tokenmanually (headless boxes without[browser]).--cdp-url URL- Captureoauth_tokenby attaching to a running Chrome over CDP (e.g.http://localhost:9222) instead of launching a browser.--android-id HEX- Override the per-install Android id (default: generated and persisted with the token).⚠️ The master token is a full-account, durable credential — use a dedicated/throwaway account only.--force- With--master-token, overwrite even if the target profile already holds a session for a different account. Without it, a mismatched--accountis refused (use a dedicated-p <profile>instead) so account B's mint can't silently clobber account A's profile.
Examples:
# Default (Chromium)
notebooklm login
# Use Microsoft Edge (for orgs that require Edge for SSO)
notebooklm login --browser msedge
# Reuse cookies from your already-logged-in Chrome session
notebooklm login --browser-cookies chrome
notebooklm login --browser-cookies 'chrome::Profile 1' # one Chromium profile
# Auto-detect any supported browser via rookie-cookies
notebooklm login --browser-cookies auto
# Firefox Multi-Account Containers: target one container
notebooklm login --browser-cookies 'firefox::Work'
notebooklm login --browser-cookies 'firefox::none' # no-container default
# Populate a named profile via cookie import
notebooklm --profile work login --browser-cookies chrome
# Pick a specific browser account by email
notebooklm login --browser-cookies chrome --account alice@example.com
# Extract every signed-in browser account into separate profiles
notebooklm login --browser-cookies chrome --all-accounts
# Force a clean browser session before logging in
notebooklm login --fresh
# Headless master-token auth (one browser sign-in, then no per-session browser)
notebooklm login --master-token --account you@gmail.com
notebooklm login --master-token --account you@gmail.com --oauth-token "$OAUTH_TOKEN" # headless box
notebooklm login --master-token-refresh # legacy: force a re-mint unconditionallyNotes on --browser-cookies:
- Honors
--profile/NOTEBOOKLM_PROFILEand writes to that profile'sstorage_state.json. - Without
--accountor--all-accounts, imports the selected browser/profile's default Google account into the target profile. - For Chromium-family browsers, unscoped
chrome,brave,edge, etc. fan out across populated user profiles when account selection is needed. Usechrome::<profile-name-or-directory>to read exactly one profile; directory names such asDefaultandProfile 1are stable across UI renames. - Use
notebooklm auth inspect --browser <browser>to see available account emails before a targeted import; pass-vto show the Chromium profile directory each account came from.
Import authentication cookies from a JSON file (or stdin) and persist them to the active profile's storage_state.json — a file-backed, persistent alternative to the env-var-based NOTEBOOKLM_AUTH_JSON for users who can obtain cookies as JSON but find the browser/Playwright login flow difficult.
⚠️ These are full-account credentials. A JSON cookie export grants the same access as being logged in. Only import a file you exported yourself, keep it private, and delete it after import. Be especially cautious with third-party browser cookie-export extensions.
notebooklm auth import-cookies JSON_PATH [OPTIONS]Accepts either a Playwright storage_state object ({"cookies": [...]}) or a bare JSON list of cookie objects (the shape most browser cookie-export tools produce). Use - to read JSON from stdin. Common export fields are normalized (e.g. expirationDate → expires), __Secure-/__Host- cookies are forced Secure, and any storage_state origins (localStorage/sessionStorage) are dropped.
Imported cookies are filtered through the same domain allowlist used by browser login and validated locally for the Tier 1 NotebookLM-required cookies. The required __Secure-1PSIDTS entry must also be live and RFC 6265-routable to the accounts.google.com/RotateCookies endpoint. If any secondary-binding cookie is present, the supplied set must form a usable binding — OSID, or APISID+SAPISID together with bare LSID; a completely absent Tier 2 set is preserved after a warning for compatibility with unablated account flows. The result is written atomically with private (0o600) permissions. Invalid input never overwrites an existing session, and an existing storage_state.json is first copied to storage_state.json.bak so a stale import can be rolled back (one step — each run overwrites the previous .bak).
Incompatible with NOTEBOOKLM_AUTH_JSON: unset that env var first, or the command exits with an error (it does not silently fall back to the env auth).
Options:
--include-domains LABEL(repeatable) - Opt in to persisting sibling-product cookies (default: required Google auth/Drive/NotebookLM domains only). Pass labels comma-separated or repeat the flag (e.g.--include-domains youtube,docsor--include-domains youtube --include-domains docs). Same labels aslogin:youtube,docs,myaccount,mail,all.--include-optional- Persist all optional sibling-product cookie domains.--json- Emit a JSON result (storage_path,cookie_count,backup_path).--quiet- Suppress success output.
Examples:
# Import a bare cookie list exported by a browser tool
notebooklm auth import-cookies cookies.json
# Import a Playwright storage_state into a named profile
notebooklm -p work auth import-cookies playwright-storage-state.json
# Pipe JSON from stdin
cat cookies.json | notebooklm auth import-cookies -Set the active notebook for subsequent commands.
notebooklm use [OPTIONS] <notebook_id>By default, the notebook must exist on the server; a typo or unreachable backend results in a non-zero exit and the saved context is left untouched. Pass --force to bypass verification (offline / debugging).
Supports partial ID matching:
notebooklm use abc # Matches abc123def456...Options:
--force- Skip the existence check and persist the notebook ID even if verification fails.--json- Emit{"active_notebook_id": "<id>", "success": true, "verified": true|false, "notebook": {...}}.verified: falsecovers the--forcepath.
When credentials are expired, use routes through the canonical "Not logged in" handler instead of a generic "Could not verify" message; under --json you get the standard {"error": true, "code": "AUTH_REQUIRED", ...} envelope.
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 │ │
└─────────────────┴──────────────────────────────┴─────────────────┘
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.
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 |
Manage source labels — AI-generated (or manually named) topic groupings of a notebook's sources.
# List labels (with member ids + resolved source titles)
notebooklm label list
# Expand a label to its source objects
notebooklm label sources Papers
# AI-group sources into topic labels (the UI's "Reorganize")
notebooklm label generate # --scope unlabeled (default, safe)
notebooklm label generate --scope all -y # destructive: wipe + regenerate every label
# Create an empty, manually-named label
notebooklm label create "Papers" --emoji 📄
# Rename a label (preserves its emoji) / set its emoji
notebooklm label rename Papers "Research Papers"
notebooklm label emoji Papers 📚
# Add source(s) to a label (append; existing members preserved)
notebooklm label add Papers src123 src456
# Remove source(s) from a label (un-assign only; source stays in the notebook)
notebooklm label remove Papers src123
# Delete one or more labels (the label only, not its sources)
notebooklm label delete Papers -yOptions (all commands):
-n, --notebook ID- Specify notebook (uses current if not set, supports partial IDs)--json- Output as JSON
Confirmation gates:
label generate --scope allwipes and regenerates every label with new ids — requires-y/--yes(or an interactive prompt).label deleterequires-y/--yes(or an interactive prompt).
Name-or-ID resolution: <id|name> arguments accept a label id (or partial prefix) or an exact label name. An ambiguous name lists the matching ids (with emoji + source count) so you can re-run with the id.
Selecting sources by label: notebooklm source list --label <id|name> lists only the sources in a label — a read-only saved-selection filter.
Diagnose Web authentication by validating a storage file, its cookies, and
optionally a Web token fetch. This command does not validate Android bearer
authentication; for Android, confirm the android extra and a profile
master_token.json (see Configuration).
notebooklm auth check [OPTIONS]Options:
--test- Also test token fetch from NotebookLM (makes network request)--passive- With--test, validate read-only: never runNOTEBOOKLM_REFRESH_CMD, rotate cookies, or write to disk. Use for unattended readiness/health checks that must not mutate state or race real work. No effect without--test.--json- Output as JSON (useful for scripts)
Examples:
# Quick local validation
notebooklm auth check
# Full validation with network test
notebooklm auth check --test
# Read-only readiness probe (no refresh cmd, no cookie write) for a health check
notebooklm auth check --test --passive
# JSON output for automation
notebooklm auth check --jsonChecks performed:
- Storage file exists and is readable
- JSON structure is valid
- Required cookies (
SID+__Secure-1PSIDTS) are present (the Tier 1MINIMUM_REQUIRED_COOKIESset). The loader also evaluates the warning-only secondary-binding check —OSID, orAPISID+SAPISIDtogether with bareLSID— butauth checkdoes not mark a Tier 2 warning as failure; see auth-cookie-lifecycle.md - Cookie domains are correct (.google.com vs regional)
- (With
--test) Token fetch succeeds
Output shows:
- Authentication source (file path or environment variable)
- Which cookies were found and from which domains
- Detailed cookie breakdown by domain (highlighting key auth cookies)
- Token lengths when using
--test
Use cases:
- Debug "Not logged in" errors
- Verify auth setup in CI/CD environments
- Check if cookies are from correct domain (regional vs .google.com)
- Diagnose NOTEBOOKLM_AUTH_JSON environment variable issues
Exit codes: auth check exits 0 only when every executed check passes
and non-zero (1) when any executed check fails — in both text and --json
modes. A skipped check does not count as a failure: without --test, the token
fetch is skipped, so the exit status reflects only the local cookie checks.
Unattended monitors should therefore rely on the exit code (not on parsing the
table). To gate readiness on a real token fetch, run notebooklm auth check --test and check the exit status. See CLI Exit-Code Convention.
One-shot Web keepalive and recovery: open a file-backed session, trigger the
SIDTS rotation request against accounts.google.com, persist rotated cookies,
and exit. If storage is absent but the exact sibling master_token.json exists,
it mints the initial file and validates it once; --verify reuses that result.
Existing cookies that redirect to Google sign-in can re-mint from a sibling
master token; --allow-headless permits browser recovery first. This command
refreshes Web cookie storage, not Android bearer credentials. Designed for
direct use or OS scheduling (launchd / systemd / cron / Task Scheduler / k8s
CronJob).
notebooklm auth refresh [OPTIONS]Options:
--browser-cookies <browser>,--browser-cookie <browser>- Re-extract cookies from an installed browser and match the current profile's account (the unified in-bandstorage_state.jsonrecord — a pre-v0.5.0 profile's account, if it's still only in the legacy siblingcontext.json, is promoted in-band automatically on read). This repairs account routing when browser account order changes after another account logs out. Accepts the same scoped syntax aslogin:chrome::<profile-name-or-directory>for one Chromium profile, andfirefox::<container-name>orfirefox::nonefor one Firefox container.--include-domains LABEL[,LABEL...]- Forward to the browser-cookie reader (only meaningful with--browser-cookies). Same syntax asnotebooklm login --include-domains.--quiet,-q- Suppress success output; print only on error (cron-friendly)--verify- After refreshing, run a read-only passive token fetch to confirm the resulting cookies actually authenticate; exit non-zero if they still fail. A missing-storage master-token bootstrap always performs this validation and--verifyreuses its result. Especially valuable with--browser-cookies, which rewrites the cookie jar but does not otherwise verify it.--allow-headless- Permit layer-3 browser recovery for this invocation when stored cookies are fully expired. Uses the storage-bound persisted browser profile or loopbackNOTEBOOKLM_HEADLESS_REAUTH_CDP_URL, and does not launch or attach unless the ordinary refresh fails. Incompatible with--browser-cookies.--json- Emit one structured success or error document. On success the keys arestatus,storage_path, andverified.
Cadence: 15-20 minutes is the recommended interval. Tighter is wasteful (the 60 s mtime guard would skip it anyway); significantly looser may cross the __Secure-1PSIDTS server-side validity window for your account/region.
Requires file-backed authentication. auth refresh refuses NOTEBOOKLM_AUTH_JSON because inline JSON has no writable backing store. A root --storage PATH is an explicit file-backed override and wins over that environment variable for the invocation.
Exit codes:
0- the auth path completed without raising. The rotation POST is best-effort: exit 0 also covers (a) the 60 s mtime guard skipping the POST, (b)NOTEBOOKLM_DISABLE_KEEPALIVE_POKE=1being set, (c) another process holding the cross-process rotate lock, and (d) a transienthttpxerror during the POST being caught and logged at DEBUG. Treat exit 0 as "no error" rather than "rotation occurred." For verification, enableNOTEBOOKLM_LOG_LEVEL=DEBUGand check for theRotateCookieslog line.1- a typed command failure occurred (for example, an incompatible option, a failed master-token mint, or a failed post-refresh passive token fetch). The OS scheduler's next firing is the retry mechanism; this command does not retry in-process.2- an unexpected error occurred. Missing storage without a sibling token and malformed existing storage retain this existing contract; neither bootstraps.
Examples:
# One-shot refresh against the default profile
notebooklm auth refresh
# Refresh a named profile (works with --profile / NOTEBOOKLM_PROFILE)
notebooklm --profile work auth refresh
# Permit browser-backed L3 only if ordinary recovery reaches a login redirect
notebooklm --profile work auth refresh --allow-headless
# Re-extract from Chrome and repair account routing if browser account order changed
notebooklm --profile work auth refresh --browser-cookies chrome
notebooklm --profile work auth refresh --browser-cookies 'chrome::Profile 1'
# Quiet variant for cron / systemd
notebooklm --profile work auth refresh --quietPairs with:
NOTEBOOKLM_REFRESH_CMDfor in-process auth-expiry recovery (covers the case whereauth refreshitself fails because cookies have already expired beyond rotation)keepalive=<seconds>onNotebookLMClientfor in-process long-lived workers (no OS scheduler needed)
See Troubleshooting for full per-OS scheduler recipes (launchd plist, systemd user timer, cron, Task Scheduler, k8s CronJob).
List Google accounts visible to a browser's cookie store. Read-only — never writes to disk. Use this before notebooklm login --browser-cookies <browser> --account <email> to see which account emails are available. Chromium-family browsers fan out across populated user profiles by default; use chrome::<profile-name-or-directory> to inspect only one profile.
notebooklm auth inspect [OPTIONS]Options:
--browser TEXT- Browser to read cookies from (chrome,firefox,brave,edge,safari,arc, ...).autopicks the first one rookie-cookies can read. Usechrome::<profile-name-or-directory>for one Chromium profile, orfirefox::<container-name>/firefox::nonefor one Firefox container. Requirespip install "notebooklm-py[cookies]".--include-domains LABEL[,LABEL...]- Opt in to enumerating accounts via sibling-product cookies (same syntax asnotebooklm login --include-domains). By default this command consults only required Google auth cookies, which is sufficient for account discovery on every tested path.--json- Output as JSON
Examples:
notebooklm auth inspect --browser chrome
notebooklm auth inspect --browser 'chrome::Profile 1' --json
notebooklm auth inspect --browser firefox --jsonLog out by clearing saved authentication. Removes both the saved cookie file (storage_state.json) and the cached browser profile. With explicit --storage, an arbitrary unowned browser sidecar is left intact; canonical managed profile layouts are still removed. After logout, run notebooklm login to authenticate with a different Google account.
notebooklm auth logoutExamples:
notebooklm auth logout # Clear auth for active profile
notebooklm -p work auth logout # Clear auth for 'work' profile
notebooklm --storage A.json auth logout # Clear the override auth filePrint the shell completion script for bash, zsh, or fish. Pipe the output into a file your shell sources at startup; once the script is loaded, Click handles the _NOTEBOOKLM_COMPLETE env-var protocol automatically.
notebooklm completion <bash|zsh|fish>Install (one-time):
# zsh — drop the script anywhere on $fpath; the file MUST be named _notebooklm
notebooklm completion zsh > ~/.zfunc/_notebooklm
# (ensure ~/.zfunc is on $fpath in ~/.zshrc, then `autoload -Uz compinit && compinit`)
# bash — source from ~/.bashrc
notebooklm completion bash > ~/.notebooklm-complete.bash
echo 'source ~/.notebooklm-complete.bash' >> ~/.bashrc
# fish — drop into the system completions directory
notebooklm completion fish > ~/.config/fish/completions/notebooklm.fishID-aware tab completion: Once the script is installed, -n/--notebook, -s/--source, and -a/--artifact complete from the active profile's live IDs:
notebooklm ask -n <TAB> # lists notebook IDs (filtered by what you've typed)
notebooklm ask -s <TAB> # lists sources in the active notebook
notebooklm download audio -a <TAB> # lists artifacts in the active notebookNote: commands that take a positional source / artifact / notebook ID (e.g. source delete <id>, artifact get <id>, use <id>) currently complete only after the option / argument is fully typed — Click's shell_complete= is attached to the flag-style -n/--notebook, -s/--source, -a/--artifact declarations enumerated below.
For -s and -a the active notebook is resolved with the same precedence the command body uses: -n/--notebook flag value already on the line > NOTEBOOKLM_NOTEBOOK env var > the persisted notebooklm use context. With no resolvable notebook (and on any auth / network failure), the completer returns no suggestions silently — it never prints a traceback into your terminal.
Print-only by design: the command never writes to your shell config; you decide where the script lands. This keeps the install path discoverable and avoids surprising shutdowns of existing completion setups.
With no --type, source add decides in this order:
- URL-shaped (contains
://) → aurloryoutubesource. - The path exists on disk → a
filesource, uploaded. This is an existence check, not an extension check, so a real file is uploaded whatever it is named —deck.pptx,./deck.pptxandnotesall work. - Otherwise → a
textsource, ingesting the argument itself as content.
Step 3 is where a typo bites: source add dekc.pptx adds a source whose entire
content is the string dekc.pptx. To make that visible, an argument that looks
like a file but does not exist is added with a warning:
$ notebooklm source add dekc.pptx
warning: 'dekc.pptx' looks like a path but does not exist; ingesting as inline
text. Pass --type text to suppress this warning, or check the path for typos.An argument looks like a file when it contains a slash, or when its extension is
one the upload accepts — .pdf .txt .md .markdown .doc .docx .pptx
.rtf .odt .csv .tsv .epub — or is HTML-family (.html .htm .xhtml
.xht, which the upload endpoint rejects with convert-first guidance), or is
.ppt (file-shaped, but legacy PowerPoint has never been proven uploadable, so
it earns the warning without being routed to the uploader). That list is derived
from the single upload-support declaration the Drive download-and-upload router
also reads, so a newly supported file type earns its warning at the same time it
becomes uploadable, rather than drifting behind it (#2202).
Pass --type text to opt out of detection entirely, or --type file to require
the upload path (which then fails loudly on a missing file instead of falling
back to text).
File-source uploads reject symlinks by default. If the path you pass (or any ancestor directory) is a symbolic link, source add refuses the upload rather than silently following it — a workspace symlink could otherwise exfiltrate the file it points at (e.g. ~/Downloads/foo.pdf -> /etc/passwd). Pass --follow-symlinks to opt in explicitly.
URL sources reject internal hosts (localhost, loopback, private IP ranges,
and link-local addresses) by default so the CLI cannot be used as an SSRF
trampoline. Pass --allow-internal only for a deliberate local NotebookLM test;
non-HTTP(S) schemes remain rejected even with the flag.
Python equivalent:
client.sources.add_file(nb_id, path, title=...). The symlink gate is a CLI-only safeguard; callers using the Python API are responsible for resolving symbolic links before passing the path.
# Default — symlink rejected with: "Path is a symlink; pass --follow-symlinks to follow it explicitly."
notebooklm source add ./link-to-doc.pdf --type file
# Opt-in: resolve the symlink, then upload the resolved target
notebooklm source add ./link-to-doc.pdf --type file --follow-symlinksThe same gate applies on the explicit --type file path (no auto-detect), so typing the source type as file does not bypass the check.
The --mime-type flag on notebooklm source add (file-source path) is a
supported parameter. It overrides the filename-extension inference and
sets the content-type header used for the resumable upload handshake. Omit it
to let the MIME type be inferred from the file extension.
# Inferred from the extension (typical case — no flag needed)
notebooklm source add ./report.pdf --type file
# Override inference (e.g. an extensionless export, or a misnamed file)
notebooklm source add ./report.bin --type file --mime-type application/pdfNote: The same
--mime-typeflag onnotebooklm source add-drive(Google Drive sources) selects betweengoogle-doc/google-slides/google-sheets/The
<title>you pass tosource add-driveis ignored by NotebookLM for native Drive imports — the backend re-derives the display title from live Drive metadata, so the source keeps its Drive name regardless. Runnotebooklm source rename <id> "<title>"after the add if you need a specific title.Historical: an earlier release treated the file-source
--mime-typeas a deprecated no-op. It was re-wired to set the upload content-type and is no longer deprecated; theNOTEBOOKLM_QUIET_DEPRECATIONSnotice it used to emit is gone.
Perform AI-powered research and add discovered sources to the notebook.
Python equivalent:
client.research.start(nb_id, query, source="web", mode="deep")followed byclient.research.wait_for_completion(...)/client.research.import_sources(...).
notebooklm source add-research [query] [OPTIONS]Options:
-n, --notebook ID- Notebook ID (uses current if not set)--mode [fast|deep]- Research depth (default: fast)--from [web|drive]- Search source (default: web)--import-all- Automatically import all found sources (works with blocking mode)--cited-only- With--import-all, import only cited sources--no-wait- Start research and return immediately (non-blocking)--timeout SECONDS- Per-phase seconds budget for (a) the research-completion poll loop and (b) bounded read-only candidate inspection after an unknown--import-alloutcome (default: 1800). The import mutation is sent once. Each phase gets the full budget independently, so worst-case total wall time is up to 2× this value. Matchesresearch wait --timeoutsemantics. Before 0.4.2 the in-line poll was hardcoded to 5 minutes, so deep research that ran longer was silently abandoned and left an "Add sources?" modal hanging in the NotebookLM web UI — bump--timeoutfor long deep-research runs.--prompt-file PATH- Read query from a file (or-for stdin) instead of the positional argument
Note:
--mode deepis only supported with--from web(the default). Combining--mode deep --from driveis rejected by the backend withValidationError("Deep Research only supports Web sources.")— for Drive, stick with--mode fast.
Examples:
# Fast web research (blocking)
notebooklm source add-research "Quantum computing basics"
# Drive research (fast mode only — Drive does not support --mode deep)
notebooklm source add-research "Project Alpha" --from drive
# Non-blocking deep research for agent workflows (web only — see note above)
notebooklm source add-research "AI safety papers" --mode deep --no-wait
# Import only cited sources for tighter relevance
notebooklm source add-research "topic" --import-all --cited-only
# Bounded import-retry budget for large result sets
notebooklm source add-research "AI papers" --mode deep --import-all --timeout 3600
# Read long query from file (or stdin via '-')
notebooklm source add-research --prompt-file research_query.txt --mode deep
echo "very long query..." | notebooklm source add-research --prompt-file -Check research status for the current notebook (non-blocking).
Python equivalent:
client.research.poll(nb_id).
notebooklm research status [OPTIONS]Options:
-n, --notebook ID- Notebook ID (uses current if not set)--run-id ID, --task-id ID- Poll one exact run. Omit it to preserve the unfiltered notebook-level behavior; pass it whenever more than one historical run may be visible.--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
# Check one exact run (`--task-id` is an alias)
notebooklm research status --run-id <run_id>
# JSON output for scripts/agents
notebooklm research status --jsonWait for research to complete (blocking).
Python equivalent:
client.research.wait_for_completion(nb_id), then optionally callclient.research.import_sources(...)(matches the CLI's--import-all/--cited-onlybehavior).
notebooklm research wait [OPTIONS]Options:
-n, --notebook ID- Notebook ID (uses current if not set)--run-id ID, --task-id ID- Wait for one exact run. Omit it to preserve the historical notebook-level selection behavior.--timeout SECONDS- Per-phase budget (default: 1800, matchingsource add-research). Deep runs regularly exceed the former 300s default — 374s live, 358s in theresearch_deep_poll_longcassette; fast runs settle in seconds. With--import-allthe poll loop and bounded read-only candidate inspection after an unknown import outcome each get the full budget independently; the import mutation itself is sent once.--interval SECONDS- Seconds between status checks (default: 5)--import-all- Import all found sources when done--cited-only- With--import-all, import only cited sources--json- Output as JSON
Examples:
# Basic wait
notebooklm research wait
# Wait for one exact run (`--task-id` is an alias)
notebooklm research wait --run-id <run_id>
# Wait longer for deep research
notebooklm research wait --timeout 600
# Wait and auto-import sources
notebooklm research wait --import-all
# Import only cited sources
notebooklm research wait --import-all --cited-only
# JSON output for agent workflows
notebooklm research wait --json --import-allUse case: Primarily for LLM agents that need to wait for non-blocking deep research started with source add-research --no-wait.
Import a completed research run's sources — without blocking.
Python equivalent:
client.research.import_sources_with_verification(nb_id, run_id, sources), after polling the run tocompletedyourself.
notebooklm research import [OPTIONS]Options:
-n, --notebook ID- Notebook ID (uses current if not set)--run-id ID- Run to import. Omit it and the notebook's single research run is used; when a notebook has more than one run this errors rather than guessing which you meant, so pass the id (research statusshows it)--cited-only- Import only report-cited sources (all of them, if no citation resolves —cited_only_fallbacksays which happened)--timeout SECONDS- Seconds budget for read-only candidate inspection after an unknown import outcome (default: 1800); the import mutation is never retried--max-sources N- Import at most N sources (applied after--cited-onlynarrows)--allow-duplicate- Re-add sources whose URL is already in the notebook--json- Output as JSON
Never waits for the run. This is the counterpart to research wait --import-all: if the run is still in progress (or failed, or found nothing), the command exits 1 with an explanation instead of polling. The import RPC itself is not instant — IMPORT_RESEARCH commonly outlives a single client timeout on deep payloads. It is sent once; after an unknown outcome the command may perform bounded read-only reconciliation, but candidate rows are diagnostic and are not reported as successful imports. That makes the fully composable flow expressible — you own the cadence:
notebooklm source add-research "AI safety" --mode deep --no-wait # returns immediately
notebooklm research status # your loop, your interval
notebooklm research import # imports, returnsIdempotent — with two documented limits. A source whose URL is already in the notebook is reported as already-present rather than duplicated, so a repeat import reads as "0 new, N already present" instead of looking like a no-op. Under --json that split is imported / imported_sources versus already_present / already_present_sources, and status is already_imported when nothing new landed.
The dedupe is by URL against a snapshot taken just before the import, so: a deep run's report row has no URL and is re-imported on every run, and if the snapshot call itself fails the filter is skipped entirely (the import still proceeds). Both are properties of import_sources_with_verification, shared with research wait --import-all and the MCP tool. If you interrupt an import, check source list before re-running it rather than assuming the re-run is a no-op.
Examples:
# Import the notebook's current completed run
notebooklm research import
# Pin a specific run and take only the cited sources
notebooklm research import --run-id <run_id> --cited-only
# Cap the import, JSON output for agent workflows
notebooklm research import --max-sources 10 --jsonCancel an in-flight research run.
Python equivalent:
client.research.cancel(nb_id, run_id).
notebooklm research cancel RUN_ID [OPTIONS]Arguments:
RUN_ID- The run's poll-level id — thetask_idshown byresearch status. For deep research this is thereport_idreturned bysource add-research, not the deep starttask_id(which is a sessionId and will not cancel anything).
Options:
-n, --notebook ID- Notebook ID (uses current if not set)--json- Output as JSON
Examples:
# Cancel a run (find the run id with `research status`)
notebooklm research cancel <run_id>
# JSON output for agent workflows
notebooklm research cancel <run_id> --jsonFire-and-forget: the server reports neither success nor failure for a cancel and does not validate the run id, so this command cannot confirm the cancel took effect. Run
research statusafterward — a cancelled in-progress run shows asfailed.
Generate an audio overview (podcast).
Python equivalent:
client.artifacts.generate_audio(nb_id, ...).
notebooklm generate audio [description] [OPTIONS]Options:
-n, --notebook ID- Notebook ID (uses current if not set)--format [deep-dive|brief|critique|debate]- Podcast format (default: deep-dive)--length [short|default|long]- Duration (default: default)--language LANG- Output language (precedence:--language>NOTEBOOKLM_HLenv > config >'en')-s, --source ID- Limit to specific source IDs (repeatable, uses all if not specified)--wait / --no-wait- Wait for completion (default:--no-wait)--timeout SECONDS- Maximum seconds to wait (default: 1200; no-op without--wait)--interval SECONDS- Seconds between status checks (default: 2; no-op without--wait)--retry N- Retry N times with exponential backoff on rate limit--json- Output as JSON (returnstask_idandstatus)--prompt-file PATH- Read description from a file (or-for stdin); mutually exclusive with positional argument
Examples:
# Basic podcast (starts async, returns immediately)
notebooklm generate audio
# Debate format with custom instructions
notebooklm generate audio "Compare the two main viewpoints" --format debate
# Generate and wait for completion
notebooklm generate audio "Focus on key points" --wait
# Generate using only specific sources
notebooklm generate audio -s src_abc -s src_def
# JSON output for scripting/automation
notebooklm generate audio --json
# Output: {"task_id": "abc123...", "status": "pending"}
# Read long instructions from a file
notebooklm generate audio --prompt-file instructions.txt --format debateGenerate a video overview. Use --format cinematic for AI-generated documentary footage (Veo 3); cinematic videos ignore --style and take ~30-40 min (requires AI Ultra). For non-cinematic formats, see generate cinematic-video for the alias subcommand.
Python equivalent:
client.artifacts.generate_video(nb_id, ...).
notebooklm generate video [description] [OPTIONS]Options:
-n, --notebook ID- Notebook ID (uses current if not set)--format [explainer|brief|cinematic|short]- Video format (shortis a vertical short-form video with a fixed style)--style [auto|custom|classic|whiteboard|kawaii|anime|watercolor|retro-print|heritage|paper-craft]- Visual style (not supported with--format cinematicorshort)--style-prompt TEXT- Custom visual style prompt (required when--style custom; rejected with--format cinematicorshort)--language LANG- Output language (precedence:--language>NOTEBOOKLM_HLenv > config >'en')-s, --source ID- Limit to specific source IDs (repeatable, uses all if not specified)--wait / --no-wait- Wait for completion (default:--no-wait)--timeout SECONDS- Maximum seconds to wait (default: 1800;--format cinematicdefaults to 3600; no-op without--wait)--interval SECONDS- Seconds between status checks (default: 2; no-op without--wait)--retry N- Retry N times with exponential backoff on rate limit--json- Output as JSON (returnstask_idandstatus)--prompt-file PATH- Read description from a file (or-for stdin); mutually exclusive with positional argument
Examples:
# Kid-friendly explainer
notebooklm generate video "Explain for 5 year olds" --style kawaii
# Custom visual style
notebooklm generate video --style custom --style-prompt "hand-drawn diagrams"
# Cinematic format (Veo 3)
notebooklm generate video --format cinematic "documentary overview"
# Professional style
notebooklm generate video --style classic --wait
# Generate from specific sources only
notebooklm generate video -s src_123 -s src_456
# JSON output for scripting/automation
notebooklm generate video --jsonNote: Passing a non-cinematic --format to the cinematic-video alias exits 2 with a UsageError. Use generate video --format <other> for non-cinematic formats.
Revise an individual slide in an existing slide deck using a natural-language prompt.
Python equivalent:
client.artifacts.revise_slide(nb_id, artifact_id, slide_index, instructions).
notebooklm generate revise-slide [description] --artifact <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:
-n, --notebook ID- Notebook ID (uses current if not set)--wait / --no-wait- Wait for completion (default:--no-wait)--timeout SECONDS- Maximum seconds to wait (default: 300; no-op without--wait)--interval SECONDS- Seconds between status checks (default: 2; no-op without--wait)--retry N- Retry N times with exponential backoff on rate limit--json- Machine-readable output--prompt-file PATH- Read description from a file (or-for stdin); mutually exclusive with positional argument
Examples:
# Revise the first slide
notebooklm generate revise-slide "Move the title up" --artifact art123 --slide 0
# Revise the fourth slide and wait for completion
notebooklm generate revise-slide "Remove taxonomy table" --artifact art123 --slide 3 --waitNote: The slide deck must already be fully generated before using revise-slide. Use artifact list to find the artifact ID.
Generate a text report (briefing doc, study guide, blog post, or custom).
Python equivalent:
client.artifacts.generate_report(nb_id, report_format=..., ...).
notebooklm generate report [description] [OPTIONS]Options:
-n, --notebook ID- Notebook ID (uses current if not set)--format [briefing-doc|study-guide|blog-post|custom]- Report format (default: briefing-doc)--append TEXT- Append extra instructions to the built-in prompt (no effect with--format custom)--language LANG- Output language (precedence:--language>NOTEBOOKLM_HLenv > config >'en')-s, --source ID- Limit to specific source IDs (repeatable, uses all if not specified)--wait / --no-wait- Wait for completion (default:--no-wait)--timeout SECONDS- Maximum seconds to wait (default: 300; no-op without--wait)--interval SECONDS- Seconds between status checks (default: 2; no-op without--wait)--retry N- Retry N times with exponential backoff on rate limit--json- Output as JSON--prompt-file PATH- Read description from a file (or-for stdin); mutually exclusive with positional argument
Examples:
notebooklm generate report --format study-guide
notebooklm generate report "Executive summary for stakeholders" --format briefing-doc
# Generate report from specific sources
notebooklm generate report --format study-guide -s src_001 -s src_002
# Custom report with description (auto-selects custom format)
notebooklm generate report "Create a white paper analyzing the key trends"
# Append instructions to a built-in format
notebooklm generate report --format study-guide --append "Target audience: beginners"
notebooklm generate report --format briefing-doc --append "Focus on AI trends, keep it under 2 pages"
# Read a long custom prompt from file (auto-selects custom format)
notebooklm generate report --prompt-file custom_report.txtManage existing artifacts (audio, video, slide decks, quizzes, reports, etc.). Every subcommand resolves the notebook via the standard precedence (-n/--notebook flag > NOTEBOOKLM_NOTEBOOK env > active context).
Python equivalent:
client.artifacts.list/get/get_prompt/rename/delete/poll_status/wait_for_completion/retry_failed/suggest_reports(...)for management;export_report/export_data_table/export(...)for export.
notebooklm artifact <list|get|get-prompt|rename|delete|export|poll|wait|retry|suggestions> [OPTIONS]Common options (all subcommands):
-n, --notebook ID- Notebook ID (uses current if not set; supports partial IDs)
Per-subcommand options:
| Subcommand | Required arguments | Options |
|---|---|---|
list |
(none) | --type [all|audio|video|slide-deck|quiz|flashcard|infographic|data-table|mind-map|report|fantasy-map|file], --limit N (default: unlimited), --no-truncate, --json |
get |
ARTIFACT_ID |
--json |
get-prompt |
ARTIFACT_ID |
--json |
rename |
ARTIFACT_ID NEW_TITLE |
--json |
delete |
ARTIFACT_ID |
-y/--yes (skip confirmation), --json |
export |
ARTIFACT_ID |
--title TEXT (required), --type [docs|sheets] (default: docs), --json |
poll |
TASK_ID (from generate <type>) |
--json |
wait |
ARTIFACT_ID |
--timeout SECONDS (default: 300), --interval SECONDS (default: 2), --json |
retry |
ARTIFACT_ID |
--wait (block until terminal), --timeout SECONDS (default: 300), --interval SECONDS (default: 2), --json |
suggestions |
(none) | --json |
Examples:
# Filter artifact list to one notebook and one type, JSON for scripting
notebooklm artifact list --notebook nb_abc --type audio --json
# Inspect a single artifact (partial ID OK)
notebooklm artifact get art123 --json
# Show the prompt an artifact was generated from
notebooklm artifact get-prompt art123 --json
# Rename an artifact
notebooklm artifact rename art123 "Final cut"
# Delete without prompting
notebooklm artifact delete art123 -y --json
# Export a report to Google Docs (title is required)
notebooklm artifact export art123 --title "Climate briefing" --type docs
# Poll a task immediately after a generate call returns
notebooklm generate audio --json # -> { "task_id": "task_abc...", "status": "..." }
notebooklm artifact poll task_abc
# Block until the artifact finishes (or 600s elapses)
notebooklm artifact wait art123 --timeout 600 --json
# Retry a failed artifact in place (UI "Retry"); --wait blocks until terminal
notebooklm artifact retry art123 --wait
# Get AI-suggested report topics for the active notebook
notebooklm artifact suggestions --json
pollvswait: The API returns one identifier that doubles as bothtask_id(right aftergenerate) andartifact_id(once it appears inartifact list).pollis a one-shot status check that does not prefix-match againstartifact list(use it immediately aftergenerate);waitblocks until terminal status and accepts partial IDs that resolve againstartifact list. See the quick-reference note above for full details.
Download generated artifacts to your local machine.
Python equivalent: the per-type
client.artifacts.download_audio/video/slide_deck/infographic/report/mind_map/data_table(nb_id, path, ...)methods.
notebooklm download <type> [OUTPUT_PATH] [OPTIONS]Artifact Types and Output Formats:
| Type | Default Extension | Description |
|---|---|---|
audio |
.m4a |
Audio overview (podcast) — AAC audio in an MP4 container |
video |
.mp4 |
Video overview |
slide-deck |
.pdf or .pptx |
Slide deck as PDF (default) or PowerPoint |
infographic |
.png |
Infographic image |
report |
.md |
Report as Markdown (Briefing Doc, Study Guide, etc.) |
mind-map |
.json |
Mind map as JSON tree structure |
data-table |
.csv |
Data table as CSV (UTF-8 with BOM for Excel) |
Options:
--all- Download all artifacts of this type--latest- Download only the most recent artifact (default if no ID/name provided)--earliest- Download only the oldest artifact--name NAME- Download artifact with matching title (supports partial matches)-a, --artifact ID- Select specific artifact by ID (supports partial IDs)--dry-run- Show what would be downloaded without actually downloading--force- Overwrite existing files--no-clobber- Don't overwrite existing files (a single download fails;--allskips them; default is auto-rename)--format [pdf|pptx]- Slide deck format (slide-deck command only, default: pdf)--json- Output result in JSON format
Examples:
# Download the latest podcast
notebooklm download audio ./podcast.m4a
# Download all infographics
notebooklm download infographic --all
# Download a specific slide deck by name
notebooklm download slide-deck --name "Final Presentation"
# Download slide deck as PPTX (editable PowerPoint)
notebooklm download slide-deck --format pptx
# Preview a batch download
notebooklm download audio --all --dry-run
# Download a report as markdown
notebooklm download report ./study-guide.md
# Download mind map as JSON
notebooklm download mind-map ./concept-map.json
# Download data table as CSV (opens in Excel)
notebooklm download data-table ./research-data.csvDownload quiz questions or flashcard decks in various formats.
Python equivalent:
client.artifacts.download_quiz(nb_id, path, output_format="json|markdown|html")anddownload_flashcards(...)with the same signature.
notebooklm download quiz [OUTPUT_PATH] [OPTIONS]
notebooklm download flashcards [OUTPUT_PATH] [OPTIONS]Options:
-n, --notebook ID- Notebook ID (uses current context if not set)--format FORMAT- Output format:json(default),markdown, orhtml-a, --artifact ID- Select specific artifact by ID
Output Formats:
- JSON - Structured data preserving full API fields (answerOptions, rationale, isCorrect, hint)
- Markdown - Human-readable format with checkboxes for correct answers
- HTML - Raw HTML as returned from NotebookLM
Examples:
# Download quiz as JSON
notebooklm download quiz quiz.json
# Download quiz as markdown
notebooklm download quiz --format markdown quiz.md
# Download flashcards as JSON (normalizes f/b keys to front/back)
notebooklm download flashcards cards.json
# Download flashcards as markdown
notebooklm download flashcards --format markdown cards.md
# Download flashcards as raw HTML
notebooklm download flashcards --format html cards.htmlRead, create, and update notebook notes (the "Notes" panel in the web UI). Mind-map artifacts surface as notes in the underlying API but are filtered out of note list — use artifact list --type mind-map for those.
Python equivalent:
client.notes.list/create/get/update/delete(...). The CLI'snote saveandnote renameboth map toclient.notes.update(...)(title and/or content). Mind-map helpers (list_mind_maps,delete_mind_map) live on the same API.
notebooklm note <list|create|get|save|rename|delete> [OPTIONS]Common options (all subcommands):
-n, --notebook ID- Notebook ID (uses current if not set; supports partial IDs)--json- Machine-readable output
Per-subcommand options:
| Subcommand | Required arguments | Type-specific options |
|---|---|---|
list |
(none) | — |
create |
[CONTENT] (positional or stdin via -) |
--content TEXT (or - for stdin; mutually exclusive with positional), -t/--title TEXT (default: "New Note") |
get |
NOTE_ID |
— |
save |
NOTE_ID |
--title TEXT, --content TEXT |
rename |
NOTE_ID NEW_TITLE |
— |
delete |
NOTE_ID |
-y/--yes (skip confirmation) |
Examples:
# List notes in the active notebook
notebooklm note list --json
# Create a titled note from a positional string
notebooklm note create "Quick thought" -t "Idea: research follow-up"
# Pipe markdown from a file or another command (use `-` to read stdin)
cat draft.md | notebooklm note create -
# Equivalent --content form
cat draft.md | notebooklm note create --content -
# Update an existing note (partial ID OK; only the fields you pass change)
notebooklm note save note_abc --title "Updated title"
notebooklm note save note_abc --content "New body"
# Rename without touching content
notebooklm note rename note_abc "Renamed"
# Delete without prompting
notebooklm note delete note_abc -y --jsonManage named profiles under $NOTEBOOKLM_HOME/profiles/<name>/. Each profile owns its own storage_state.json, context.json, and browser profile, so multiple Google accounts can coexist. The active profile is selected by -p/--profile, NOTEBOOKLM_PROFILE, or the default_profile field in $NOTEBOOKLM_HOME/config.json (in that precedence). See configuration.md for the full profile model.
notebooklm profile <list|create|switch|delete|rename> [OPTIONS]Per-subcommand options:
| Subcommand | Required arguments | Options |
|---|---|---|
list |
(none) | --json |
create |
NAME |
--json |
switch |
NAME |
--json |
delete |
NAME |
--yes/-y (skip prompt; --confirm is a deprecated alias; the active default profile cannot be deleted), --json |
rename |
OLD_NAME NEW_NAME |
--json |
Examples:
# Enumerate profiles (active flag included in JSON)
notebooklm profile list --json
# Create a profile shell, then authenticate into it
notebooklm profile create work
notebooklm -p work login
# Make 'work' the default for subsequent commands
notebooklm profile switch work
# Rename a profile (does not change `storage_state.json` contents)
notebooklm profile rename work work-old
# Delete a non-active profile without prompting
notebooklm profile delete old-account --yesNote:
profile deleterefuses to remove the currently active default profile. Switch to a different profile first (notebooklm profile switch <other>) and then delete.
Manage the bundled NotebookLM agent-skill template. The skill lives in SKILL.md at the repository root; installing it materializes a copy under one of:
.claude/skills/notebooklm/SKILL.md(Claude Code,--target claude).agents/skills/notebooklm/SKILL.md(universal agent skill directory,--target agents)
--scope selects whether to write into the user's home (~/.claude/skills/..., ~/.agents/skills/...) or the current project (./.claude/skills/..., ./.agents/skills/...).
skill package takes neither --scope nor --target: it builds a ZIP archive (zip root: notebooklm/SKILL.md, version-stamped) for upload via Claude Settings → Capabilities — the supported hand-off for sandboxed agent environments such as Claude Cowork, which cannot run skill install against a local directory.
notebooklm skill <install|status|uninstall|show|package> [OPTIONS]Options matrix (defaults: --scope user --target all):
| Subcommand | --scope choices |
--target choices |
Default --target |
Other options |
|---|---|---|---|---|
install |
user, project |
all, claude, agents |
all |
--dry-run, --no-clobber, --force (project scope) |
status |
user, project |
all, claude, agents |
all |
--json |
uninstall |
user, project |
all, claude, agents |
all |
— |
show |
user, project |
source, claude, agents |
source |
— |
package |
— | — | — | -o/--output PATH (default ./notebooklm-skill.zip; an existing directory — or a PATH ending in a separator — gets the default filename inside it), --force, --json |
skill show --target source prints the packaged SKILL.md straight out of the wheel (the canonical content); the other show targets read the materialized copy from disk.
Project-scope install hardening:
--dry-runprints the target files and actions without writing.--no-clobbercreates missing targets but skips differing existing files.--forceoverwrites differing project targets.- These flags are project-scope only;
--scope userpreserves the historical always-overwrite behavior.
Examples:
# Install both targets for the current user (default scope+target)
notebooklm skill install
# Install only the Claude Code target into the current project
notebooklm skill install --scope project --target claude --dry-run
notebooklm skill install --scope project --target claude --force
# Inspect what's installed in the user-scope agents directory
notebooklm skill status --scope user --target agents
# Print the packaged skill source (e.g. for piping into another agent's loader)
notebooklm skill show --target source
# Print the installed Claude target verbatim
notebooklm skill show --scope project --target claude
# Remove all installed targets from the current project
notebooklm skill uninstall --scope project --target all
# Build the uploadable archive in the current directory (./notebooklm-skill.zip)
notebooklm skill package
# Write to an explicit file, or drop the default filename into a directory
notebooklm skill package --output dist/notebooklm-v0.8.zip
notebooklm skill package --output dist/
# Overwrite an existing archive, or emit a machine-readable result
notebooklm skill package --output dist/ --force
notebooklm skill package --jsonskill package refuses to overwrite an existing archive unless --force is passed. With --json it prints {"path", "version", "entries", "size_bytes"} on success and the standard error envelope on failure.
Codex does not consume the skill subcommand. In this repository it reads the root AGENTS.md file and invokes the notebooklm CLI or Python API directly.
Add a Google Drive document, slide deck, sheet, or PDF as a source. The Drive --mime-type selects which Drive document type to import (Google Doc / Slides / Sheets / PDF). This is distinct from the file-source --mime-type documented above, which sets the resumable-upload content-type for a locally-uploaded file.
By-reference vs upload-only: NotebookLM's Drive import only ingests Google-native Docs/Slides/Sheets + PDF by reference — the four
--mime-typechoices above. An upload-only file that merely lives in Drive (e.g.epub/docx/txt/md/rtf/odt/csv/tsv) cannot be imported this way; usesource add-drive-file <id>instead, which downloads it server-side (using your session) and uploads it — no local download step needed.Python equivalent:
client.sources.add_drive(nb_id, file_id, title, mime_type=...).
notebooklm source add-drive [OPTIONS] FILE_ID TITLEOptions:
-n, --notebook ID- Notebook ID (uses current if not set; supports partial IDs)--mime-type [google-doc|google-slides|google-sheets|pdf]- Drive document type (default:google-doc)--json- Output as JSON
Examples:
# Default — treat the Drive file as a Google Doc
notebooklm source add-drive 1AbcD...XyZ "Project Brief"
# Import as Google Slides
notebooklm source add-drive 1AbcD...XyZ "Quarterly Deck" --mime-type google-slides
# Import a Drive-hosted PDF
notebooklm source add-drive 1AbcD...XyZ "Whitepaper" --mime-type pdf --jsonAdd an upload-only Google Drive file (epub/docx/pptx/txt/md/rtf/odt/csv/tsv/pdf) by id or share URL. NotebookLM's native Drive import (source add-drive) only ingests Google-native Docs/Slides/Sheets + PDF by reference; for every other Drive-hosted file type, this command downloads the file server-side (using your session) and uploads it through the resumable-upload path — a Drive PDF can go either way.
notebooklm source add-drive-file [OPTIONS] DOCUMENT_IDOptions:
-n, --notebook ID- Notebook ID (uses current if not set; supports partial IDs)--title TEXT- Custom title (default: the file's Drive name)--wait- Wait for processing to finish--json- Output as JSON
Examples:
# Download + upload an upload-only Drive file (e.g. a .docx)
notebooklm source add-drive-file 1AbcD...XyZ
# Custom title, wait for processing to complete
notebooklm source add-drive-file 1AbcD...XyZ --title "Meeting Notes" --waitAdd ebooks from your Google Play Books library as sources ("Expert Intelligence"; US only, 18+). source books lists the library — each row shows the volume content id, title, authors, and whether it can be added (a publisher can opt a title out of content export). source add-book <content-id> adds one; a non-exportable title is refused up front, and the source ingests as an expert_intelligence source you can chat over like any other.
Web and Android supported. On Android, the client obtains the required account-scoped experiment metadata itself; no emulator or Google Play Services installation is needed.
notebooklm source books [--json]
notebooklm source add-book [OPTIONS] CONTENT_IDadd-book options:
-n, --notebook ID- Notebook ID (uses current if not set; supports partial IDs)--wait- Wait for processing to finish--json- Output as JSON
Examples:
# List the Play Books library
notebooklm source books
# Add "The Art of War" and wait for it to finish ingesting
notebooklm source add-book QhsZEAAAQBAJ -n nb_123 --waitsource stale is a shell-friendly predicate that reports whether a URL/Drive source needs a refresh; source clean removes duplicate, error, or access-blocked sources in bulk.
Python equivalent:
client.sources.check_freshness(nb_id, source_id)returns the same staleness verdict. There is no single bulk "clean" call — combineclient.sources.list(...)with a filter on duplicate/error/blocked status and callclient.sources.delete(...)per match.
notebooklm source stale [OPTIONS] SOURCE_ID
notebooklm source clean [OPTIONS]stale exit codes (standard by default; opt-in inverted predicate via --exit-on-stale):
- Default:
0on success regardless of freshness,1on error. Branch on the JSONstale/freshfields (or stdout text) for the verdict. - With
--exit-on-stale:0if stale (needs refresh),1if fresh — for back-compat with theif … ; then refresh; fishell idiom. - See CLI Exit-Code Convention for details.
stale options: -n/--notebook ID, --exit-on-stale, --json.
clean options: -n/--notebook ID, --dry-run (preview the candidate set), -y/--yes (skip the confirmation prompt), --json.
Examples:
# Refresh a single stale URL source if needed (back-compat shell idiom)
if notebooklm source stale --exit-on-stale src_abc; then
notebooklm source refresh src_abc
fi
# Default semantics: branch on JSON output instead
if [[ "$(notebooklm source stale src_abc --json | jq -r .stale)" == "true" ]]; then
notebooklm source refresh src_abc
fi
# Preview what `clean` would remove
notebooklm source clean --dry-run
# Remove duplicates/errors/blocked sources without prompting
notebooklm source clean -y --jsonFind information on a topic and create a podcast about it.
# 1. Create a notebook for this research
notebooklm create "Climate Change Research" --use --json
# Output includes: {"active_notebook_id": "...", "notebook": {...}}
# 2. Add a starting source
notebooklm source add "https://en.wikipedia.org/wiki/Climate_change"
# 3. Research more sources automatically (blocking; --import-all retry budget defaults to 1800s)
notebooklm source add-research "climate change policy 2024" --mode deep --import-all
# 4. Generate a podcast
notebooklm generate audio "Focus on policy solutions and future outlook" --format debate --wait
# 5. Download the result
notebooklm download audio ./climate-podcast.m4aFor LLM agents, use non-blocking mode to avoid timeout:
# 1. Create notebook and set it active
notebooklm create "Climate Change Research" --use
# 2. Add initial source
notebooklm source add "https://en.wikipedia.org/wiki/Climate_change"
# 3. Start deep research (non-blocking)
notebooklm source add-research "climate change policy 2024" --mode deep --no-wait
# Returns immediately
# 4. In a subagent, wait for research and import
notebooklm research wait --import-all
# Blocks until complete, then imports sources
# 5. Continue with podcast generation...Research commands:
research status- Check if research is in progress, completed, or not runningresearch wait --import-all- Block until research completes, then import sources
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?"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 --waitAdd 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"
doneCheck profile setup, auth status, and migration.
Diagnoses common issues with profiles, authentication, and directory structure. Use --fix to automatically repair detected problems.
notebooklm doctor [OPTIONS]Options:
--fix- Attempt to fix detected issues (e.g. missing directories, broken configurations)--json- Output diagnostic results as a JSON structure for scripting/automation
Examples:
# Check profile and authentication health
notebooklm doctor
# Auto-repair environment issues
notebooklm doctor --fix
# Print diagnostics in machine-readable format
notebooklm doctor --jsonDoctor checks local files and authentication material without contacting Google.
It inspects the same auth source as other commands: explicit --storage, then
NOTEBOOKLM_AUTH_JSON, then the selected profile. Its auth check requires a
nonempty, unexpired SID cookie that can be sent to the configured NotebookLM
URL; missing or unusable __Secure-1PSIDTS produces a warning. A local pass
does not establish that Google still accepts the session.
Use notebooklm auth check --test --passive to test token fetching without
refresh hooks, cookie rotation, or storage writes. It requires structurally
usable required cookies first: absent, empty, malformed, or disallowed-domain
__Secure-1PSIDTS fails this local preflight before Google is contacted.
For that incomplete file-backed state, doctor instead suggests
notebooklm auth check --test for best-effort recovery; this check may refresh,
rotate, or update stored cookies. Missing PSIDTS in an inline export requires
a complete export or unsetting NOTEBOOKLM_AUTH_JSON to use stored recovery.
Present expired or differently scoped PSIDTS can pass the name-only preflight,
so doctor retains the passive probe for those warnings.
Preserve the same root --profile or --storage option, and keep
NOTEBOOKLM_AUTH_JSON set when testing inline auth. Doctor prints guidance
for the active source. On Windows, generated commands are explicitly labeled
for PowerShell and quote selectors literally; paste them into PowerShell.
--fix repairs filesystem setup; it does not repair credentials or test them online.
Show bundled instructions for supported agent environments.
This command displays tailored instructions for different LLM agents (Codex or Claude Code) to help them understand how to use this CLI programmatically.
notebooklm agent show [OPTIONS] {codex|claude}Examples:
# Show instructions for Codex
notebooklm agent show codex
# Show instructions for Claude Code
notebooklm agent show claudeNote:
agent show codexprefers the rootAGENTS.mdfile when running from a source checkout, so the CLI mirrors the same instructions Codex sees in the repository.
When using this CLI programmatically:
-
Three ways to specify notebooks: (a)
notebooklm use <id>to set persistent context, (b)NOTEBOOKLM_NOTEBOOK=<id>env var for the calling shell, or (c)-n <id>directly on each command. Resolution precedence:-n/--notebookflag >NOTEBOOKLM_NOTEBOOKenv > active context > error. -
Generation commands are async by default (except mind-map):
mind-map: Synchronous, completes instantly (no--waitoption)- All others: Return immediately with task ID (default:
--no-wait)
Avoid
--waitfor LLM agents—all async operations can take minutes to 30+ minutes. Useartifact wait <id>in a background task or inform the user to check back later. Long-waits (generate <kind> --wait,artifact wait,source wait) honor SIGINT cleanly: Ctrl-C exits130with a"Resume with: notebooklm artifact poll <task_id>"hint (ornotebooklm source wait <id>on the source path) instead of a Python traceback. Under--jsonthe cancellation surfaces as{"error": true, "code": "CANCELLED", "resume_hint": "..."}. -
Partial IDs work:
notebooklm use abcmatches any notebook ID starting with "abc". The same prefix-match resolves-n/--notebook,-s/--source, and-a/--artifactarguments across the CLI. -
Check status: Use
notebooklm statusto see the current active notebook and conversation. -
Auto-detection:
source addauto-detects content type:- URLs starting with
http→ web source - YouTube URLs → video transcript extraction
- File paths → file upload (PDF, text, Markdown, Word, audio, video, images)
- Path-shaped strings that don't exist on disk (e.g.
./missin.md) still ingest as inline text but emit a stderr warning so a typo doesn't silently masquerade as a successful upload. Pass--type textto suppress the warning.
- URLs starting with
-
Stdin pipelines: Four surfaces accept the canonical Unix
-placeholder for "read from stdin":notebooklm ask -,notebooklm ask --prompt-file -,notebooklm note create -(or--content -), andnotebooklm source add -(forces text-source path; bypasses path-shaped detection). Same convention applies to the variousgenerate <kind> --prompt-file -flags. -
Quiet output for CI/cron: Pass
--quiet(root-level) to suppress status output and INFO/WARN log records; errors still surface and structured--jsonpayloads are still emitted.--quietis mutually exclusive with-v/-vv(combining the two raisesUsageErrorwith exit2). For long-running keepalive loops,notebooklm auth refresh --quietis the subcommand-scoped equivalent. -
Error handling: Commands exit with non-zero status on failure (
1for user/library errors,2for system/unexpected errors per CLI Exit-Code Convention). With--json, failures surface as a typed envelope{"error": true, "code": "<TYPED_CODE>", "message": "..."}on stdout; without--json, error messages go to stderr. -
Deep research: Use
--no-waitwithsource add-research --mode deepto avoid blocking. Then useresearch wait --import-allin a subagent to wait for completion. -
Shell completion:
notebooklm completion <bash|zsh|fish>prints a completion script that enables ID-aware tab completion for-n/--notebook,-s/--source, and-a/--artifactfrom your active profile's live IDs. See thecompletionsection for install snippets.
Install notebooklm-py[impersonate,markdown], then opt in for a single web page:
notebooklm source add "https://example.com/article" -n NOTEBOOK_ID --fallback-fetch --jsonA best-effort snapshot precedes the normal NotebookLM import. If that snapshot
fails, the import still runs but recovery is unavailable for that attempt.
Recovery requires a failed-precondition
response plus one newly observed matching ERROR row with experimental failure
code 1. Matching tolerates equivalent URL spellings (host case, default ports,
IPv6 compression, empty paths and percent-escape case), preserving distinct path
and query values. Android correlates by its tentative source ID, since failed rows can
lack a URL. Missing/unknown diagnostics, quota-shaped failures, other error buckets,
ambiguous rows and network timeouts do not trigger recovery. Only public HTTP(S)
HTML/plain-text/Markdown responses are accepted. PDFs, images, YouTube and batches
are outside this fallback. --allow-internal never relaxes fallback networking.
HTTPS redirects cannot downgrade to HTTP. Recovery rechecks the failed source
after fetching, before creating the text replacement.
When recovery is skipped, a warning logs a bounded reason code while the original
exception and mutation evidence are preserved; fetch failures log only their
exception class, never the fetched body.
Recovered sources are static text, with no URL refresh capability. The JSON
fallback object includes the original/final URLs, fetch time and cleanup result;
the text itself retains those URLs and the fetch time. Content checks reject thin
bodies and recognized error/challenge pages, but are heuristic.
Add --cleanup-on-failure only with --fallback-fetch to request cleanup after
recovery. A matching row is not necessarily owned by this operation: cleanup
requires a source ID returned by the creating workflow (available for correlated
Android tentative registrations). Web ghosts normally remain and are reported
as candidates. Cleanup waits for the replacement to become READY and rechecks the
stub; cleanup failure retains the replacement and reports a warning. The backend
has no conditional-delete primitive, so the final check and deletion are not
atomic against other writers.