38 KiB
Installation
Last Updated: 2026-07-11
This is the canonical installation guide for notebooklm-py. The README has a quickstart; everything else lives here.
Contents
- Prerequisites
- Quick install (TL;DR by persona)
- Choose your install path
- Optional extras matrix
- Post-install steps
- Verifying your install
- Platform notes
- Upgrading and uninstalling
- Common gotchas (appendix)
Prerequisites
- Python 3.10 or later. Tested and classified for 3.10, 3.11, 3.12, 3.13, 3.14. The CLI hard-fails with a clear error on older versions (see
_version_check.py). - Operating systems. macOS (primary development platform), Linux (Debian/Ubuntu, Fedora), Windows 10/11, WSL.
uv(optional but recommended for contributors). Install withcurl -LsSf https://astral.sh/uv/install.sh | shorbrew install uv/winget install astral-sh.uv. End users can use plainpiporpipx.- Disk and bandwidth. Base install is small (~10 MB). The first
notebooklm logindownloads Chromium (~170 MB; 30–90 s; no progress bar — be patient).
Quick install (TL;DR by persona)
Installing the CLI on macOS / Linux — use an isolated installer. Plain
pip installinto the system interpreter fails on modern macOS (Homebrew Python) and Debian/Ubuntu witherror: externally-managed-environment(PEP 668). For CLI/app use, preferuv tool installorpipx install— they putnotebooklm(andnotebooklm-mcp/notebooklm-server) on your PATH in a dedicated environment without touching system Python. Plainpipstill works inside a virtualenv and on Windows (python.org's Python is not externally-managed). Library users install into their own project's venv (uv add/pip install), so PEP 668 never applies.
| Persona | Install command |
|---|---|
| A — AI Agent | pip install "notebooklm-py[browser]" in the user's active env (fall back to uv tool install / pipx install on an externally-managed-environment error) |
| B — End user | uv tool install "notebooklm-py[browser]" or pipx install "notebooklm-py[browser]" (isolated; avoids the PEP 668 error) |
| C — Library user | uv add notebooklm-py (or pip install notebooklm-py inside your project venv) |
| D — Headless server / CI | pip install notebooklm-py inside a venv/container; ship a storage_state.json (no Playwright) |
| E — Contributor | uv sync --frozen --extra browser --extra dev --extra markdown && uv run playwright install chromium && uv run pre-commit install |
| F — Power user | uv tool install --python 3.12 "notebooklm-py[browser,cookies,markdown]" (the cookies extra needs Python ≤ 3.12; --python 3.12 makes uv provision a matching interpreter even if your default is 3.13+) |
Choose your install path
A. AI Agent (primary persona)
For Claude Code, Codex, and similar agent harnesses.
The project ships notebooklm skill install, SKILL.md, and AGENTS.md. Agents run install on the user's behalf in the user's existing environment — no new venv. They typically can't interact with a browser, but most agent harnesses (Claude Code, Codex) can shell out to Playwright when a user is present, and [cookies] is a preferred optimization for reusing the user's already-logged-in browser cookies.
Note on agent harness coverage:
notebooklm skill installempirically writes to~/.claude/skills/notebooklm/SKILL.mdand~/.agents/skills/notebooklm/SKILL.md. Cursor and other harnesses with bespoke skill formats are not auto-targeted; they fall back topip install+ manual skill registration.
Recommended install (Python-version-aware; surfaces real errors instead of swallowing them):
pip install "notebooklm-py[browser]" # mandatory; errors must propagate
# [cookies] (rookiepy) is optional and known to FAIL TO BUILD on Python 3.13+.
# Skip it deliberately on 3.13+ rather than swallowing the error — that lets
# *real* install failures (typos, network, PyPI outages) surface for the agent.
if python -c "import sys; sys.exit(0 if sys.version_info < (3, 13) else 1)"; then
pip install "notebooklm-py[cookies]" # errors propagate
else
echo "Skipping [cookies] on Python 3.13+ (rookiepy unavailable). Use 'notebooklm login' interactively."
fi
If
pip installerrors withexternally-managed-environment(modern macOS / Debian system Python, PEP 668), retry withuv tool install "notebooklm-py[browser]"orpipx install "notebooklm-py[browser]"— isolated installs that don't touch system Python. Inside an active virtualenv,pipworks as-is.
Why two separate calls (not [browser,cookies]): the combined form is atomic — if rookiepy fails to compile, the whole install fails and the user gets nothing. Splitting means [browser] always succeeds; [cookies] is recoverable.
Skill install (separate from the Python package):
notebooklm skill install # writes to ~/.claude/skills/, ~/.agents/skills/
# OR (alternative ecosystem):
npx skills add teng-lin/notebooklm-py
If the agent is reading SKILL.md from inside an already-installed location (e.g. ~/.claude/skills/notebooklm/SKILL.md), the skill is already present — you only need the Python package install + auth.
Authentication — notebooklm login is the primary path:
notebooklm login # primary: opens browser, user signs in to Google once
After login, storage_state.json persists at ~/.notebooklm/profiles/default/storage_state.json and is reused on every subsequent run. Verify with notebooklm auth check --test --json (require "status": "ok" AND "checks.token_fetch": true — bare auth check --json only proves the file parses, not that the cookies still authenticate against Google).
Headless / sandboxed agent contexts (no display, can't open a browser): use the cookie-extraction path instead, requires the [cookies] extra installed in step 2:
notebooklm login --browser-cookies auto # rookiepy autodetects an installed browser
If the agent is in a no-display sandbox AND [cookies] isn't installed (Python 3.13+ skipped it), ask the user to run notebooklm login on a workstation and copy the resulting ~/.notebooklm/profiles/default/storage_state.json to the agent's environment (or set NOTEBOOKLM_AUTH_JSON).
Sandboxed agents (Claude Cowork)
Claude Cowork (Anthropic's sandboxed desktop agent for non-developers) and similar no-display sandboxes are a special case of the headless path above: there is no browser for notebooklm login, and the sandbox resets between sessions. Two adjustments make everything except login work:
- Bootstrap each session with the base install —
[browser]/Playwright is not needed here, only forlogin(which you run elsewhere, once):pip install notebooklm-py # queries/generation/download need no extras - Reuse a host-generated
storage_state.json. Runnotebooklm loginonce on a machine with a display, then bring the file into a sandbox-accessible folder. Point at it with the root--storageflag orNOTEBOOKLM_AUTH_JSON— the same mechanism as Persona D:notebooklm --storage /path/to/storage_state.json list # per-invocation flag # OR inline via env var (no file needed — e.g. from a Cowork-stored secret): export NOTEBOOKLM_AUTH_JSON="$(cat /path/to/storage_state.json)" notebooklm list
⚠️ Security:
storage_state.jsonandNOTEBOOKLM_AUTH_JSONare bearer credentials for your Google account — store the file0600or in the sandbox's secret store, never commit or log them, andunset NOTEBOOKLM_AUTH_JSONafter use.
Pass an explicit -n/--notebook <id> on notebook-scoped commands — the selected-notebook context does not survive a session reset. If Cowork reads ~/.claude/skills/, notebooklm skill install registers the skill there; otherwise build the uploadable archive on the host with notebooklm skill package (writes notebooklm-skill.zip; see cli-reference.md) and add it via Claude Settings → Capabilities.
Verification (machine-parseable):
notebooklm --version # text version
notebooklm auth check --json # JSON: {"status": "ok", "checks": {...}}
notebooklm auth check --test --json # same + network token-fetch validation
notebooklm list --json # JSON list (may be empty for new accounts)
Important:
notebooklm statusis context state (selected notebook), NOT auth. Do not grep its output for auth signals.
Error strings the agent should grep:
"Playwright not installed"→ install[browser]"rookiepy"(in stderr ofpip install) → expected on Python 3.13+; skip[cookies]and use interactivenotebooklm login"status": "ok"(inauth check --json) → auth file present and parses; pair with--testfor network validation
B. End user
Occasional CLI use.
Prerequisites: Python 3.10+ already installed.
Recommended — isolated install (macOS / Linux / Windows):
uv tool install "notebooklm-py[browser]"
# OR, with pipx:
pipx install "notebooklm-py[browser]"
Both put notebooklm on your PATH in a dedicated environment, so they work even where the system Python is locked down — modern macOS (Homebrew) and Debian/Ubuntu reject a plain pip install into it with error: externally-managed-environment (PEP 668). (If you don't have uv yet: https://docs.astral.sh/uv/getting-started/installation/.)
Plain pip is fine inside a virtualenv, or on Windows (python.org's Python is not externally-managed):
pip install "notebooklm-py[browser]"
Post-install: Run notebooklm login once. The CLI auto-installs Chromium on first run (~170 MB, 30–90 s, no progress bar — be patient).
Verify:
notebooklm --version
notebooklm login # opens Chromium for Google sign-in
notebooklm auth check --test # confirms auth roundtrip, with explicit success message
C. Library user
Embedding notebooklm-py in a Python application.
Recommended: pip install notebooklm-py (in your app's venv).
Post-install: None for runtime use. To programmatically run interactive login from your app, add [browser] and run playwright install chromium.
Why no extras by default: all RPC traffic uses httpx; auth is cookie-based (src/notebooklm/auth.py). Apps can ship a pre-generated storage_state.json and never touch Playwright.
Verify:
import notebooklm
print(notebooklm.__version__)
Production deployment patterns (tracked in #417). Production-grade FastAPI/Django integration — client lifetime in a
lifespanhandler, httpx pool sizing, behavior under concurrent CSRF refresh, multi-tenantstorage_state.jsonrotation, a service-shaped Dockerfile, and structured rate-limit/backoff patterns — is not yet covered indocs/python-api.md. These were intentionally deferred from the install-docs consolidation (PR #416) to keep its scope focused. See #417 for the gap inventory and acceptance criteria.
D. Headless server or CI
Recommended: pip install notebooklm-py
Post-install (3-step recipe — Playwright is not required on the server):
-
On a workstation with a display, install with
[browser]and log in once:pip install "notebooklm-py[browser]" playwright install chromium notebooklm login # writes ~/.notebooklm/profiles/default/storage_state.json -
Move the auth file to the server. Either ship it as a file:
scp ~/.notebooklm/profiles/default/storage_state.json \ user@server:~/.notebooklm/profiles/default/storage_state.jsonor stuff the contents into a CI / deployment env var (preferred for ephemeral runners):
export NOTEBOOKLM_AUTH_JSON="$(cat ~/.notebooklm/profiles/default/storage_state.json)"CI env-var notes:
storage_state.jsonis typically 4–15 KB — well under GitHub Actions' 48 KB single-secret cap.- Watch for trailing newlines: pipe with
tr -d '\n'if your secret-set tool adds one (cat ... | tr -d '\n' | gh secret set NOTEBOOKLM_AUTH_JSON). - For ephemeral runners (GitHub Actions, GitLab CI — no persistent disk between runs), the layer-5 in-process refresh from troubleshooting.md cannot persist rotated cookies. Run
notebooklm auth refreshperiodically on a workstation cron and push the refreshed file withgh secret set NOTEBOOKLM_AUTH_JSON < ~/.notebooklm/profiles/default/storage_state.json.
-
On the server, run any non-
logincommand:notebooklm list notebooklm auth check --test # verifies the cookies still authenticate against Google
Why no extras: reduces the install surface to 4 deps (httpx, click, rich, filelock); avoids 200+ MB Chromium download in CI images.
For runtime configuration (env vars, profiles, parallel agents), see configuration.md#headless-servers--containers.
Alternative: master-token auth (no cookie file to ship, survives expiry)
The cookie-copy recipe above ships a storage_state.json that eventually
expires (cookies are short-lived; ephemeral CI runners can't persist rotations).
The master-token path instead holds one durable Google master token and
mints fresh web cookies from it on demand — no browser per session, and an
expired session re-mints automatically (no manual re-login). One browser
sign-in, then headless forever.
pip install "notebooklm-py[headless]" # adds gpsoauth (pure-Python)
# One-time bootstrap (a visible browser opens Google's EmbeddedSetup; sign in
# with a DEDICATED/throwaway account, and the single-use oauth_token is captured
# automatically). Add [browser] for the auto-capture, or paste it with
# --oauth-token <value> on a headless box.
notebooklm login --master-token --account you@gmail.com
# Ship master_token.json to the server instead of storage_state.json:
scp ~/.notebooklm/profiles/default/master_token.json \
user@server:~/.notebooklm/profiles/default/master_token.json
# On the server, just run commands — cookies are minted/refreshed as needed:
notebooklm list
# Force a re-mint by hand (or from cron) any time:
notebooklm login --master-token-refresh
When a master_token.json sits beside a profile's storage_state.json, an
expired session is recovered by re-minting from the master token in-process
(after the normal homepage/RotateCookies/headless ladder is exhausted) — so
long-lived headless workers self-heal.
⚠️ Security: the master token is full-account, durable, and infostealer-grade — a materially larger blast radius than an expiring
storage_state.json(it survives password changes until explicitly revoked). Use a dedicated/throwaway Google account only, store it0600(the CLI does), and never commit or log it. This path uses Google's Android auth flow (gpsoauth) and is unofficial/ToS-grey, like the rest of this client. See ADR-0023 for the design and rationale.
E. Contributor
Working on this repo.
Recommended (respects the checked-in uv.lock):
git clone https://github.com/teng-lin/notebooklm-py.git
cd notebooklm-py
uv sync --frozen --extra browser --extra dev --extra markdown
source .venv/bin/activate
uv run playwright install chromium
pre-commit install
Why uv sync --frozen and not uv pip install -e ".[all]": the repo has a checked-in uv.lock. uv sync --frozen enforces the lockfile and fails fast on drift; uv pip install ignores the lockfile and re-resolves transitively (will silently get newer versions of playwright, ruff, etc.).
Why three extras and not [all]: [all] is pip extras semantics. uv sync --extra X is the uv equivalent. The three extras here are the contributor subset of [all] = [browser, dev, markdown, mcp, server]. cookies is intentionally excluded (rookiepy build issues on Python 3.13+), and mcp / server are omitted from the default contributor flow because those adapters are not needed for the standard local suite; opt in via --extra cookies / --extra mcp / --extra server if needed.
Why browser is part of the contributor install: the default local test suite includes unit tests that import and patch playwright.sync_api, even though they do not launch a real browser. uv sync --frozen --extra dev installs pytest/ruff/mypy but not Playwright, so uv run pytest will fail with ModuleNotFoundError: No module named 'playwright'. Use the full contributor command above before running the default test suite.
Linux only: uv run playwright install-deps chromium (scoped form, matches test.yml).
Pre-commit checklist (run before every commit):
uv run ruff format --check . && \
uv run ruff check . && \
uv run mypy src/notebooklm --ignore-missing-imports && \
uv run pytest --cov=src/notebooklm --cov-report=term-missing --cov-fail-under=90
Verify:
notebooklm --version
uv run pytest --cov=src/notebooklm --cov-report=term-missing --cov-fail-under=90
uv run pre-commit run --all-files
F. Power user
Non-default browsers, cookie extraction, markdown source dumps.
Why this section uses the combined
[browser,cookies]form — unlike Persona A, which uses two separatepip installcalls so arookiepybuild failure doesn't leave the user with nothing: power users explicitly opted in, know whatrookiepyis, and prefer the all-or-nothing tradeoff (single command, no wrapping logic).
⚠️ Don't use
[all]for power-user setups.[all]deliberately excludescookies(see § All vs All-Extras). If youpip install "notebooklm-py[all]"and then try--browser-cookies, you'll get an opaquerookiepyimport error. For everything-and-the-kitchen-sink, usepip install "notebooklm-py[browser,cookies,markdown]"explicitly (Python ≤ 3.12 only).
--browser-cookies(no Playwright login):pip install "notebooklm-py[browser,cookies]". Caveat:rookiepymay fail to install on Python 3.13/3.14; use Python 3.12 or accept the risk. See cli-reference.md#authentication-login for the full--browser-cookiessyntax, includingchrome::<profile-name-or-directory>for one Chromium user-profile andfirefox::<container>for Firefox Multi-Account Containers (on every OS — not just macOS). Usenotebooklm auth inspect --browser <browser>for previewing available accounts before import.- Markdown source dumps:
pip install "notebooklm-py[markdown]"fornotebooklm source fulltext -f markdown. - Edge instead of Chromium: install Microsoft Edge from microsoft.com/edge first —
--browser msedgedoes NOT auto-install Edge (only--browser chromiumauto-installs). Thennotebooklm login --browser msedge. - Multi-account (personal + work): see configuration.md#multiple-accounts. Common power-user flow:
notebooklm profile create work && notebooklm -p work login --browser-cookies edge --account work@corp.com. Use--all-accountsto bootstrap profiles for every signed-in Google account in one command.
Optional extras matrix
Source of truth: pyproject.toml [project.optional-dependencies].
| Extra | What it adds | When you need it | pip command | uv (in your project) |
|---|---|---|---|---|
| (none) | httpx, click, rich, filelock |
All RPC operations, all CLI commands except login. Suffices when you ship a storage_state.json. |
pip install notebooklm-py |
uv add notebooklm-py |
browser |
playwright>=1.40.0 |
notebooklm login (interactive). |
pip install "notebooklm-py[browser]" |
uv add "notebooklm-py[browser]" |
cookies |
rookiepy>=0.1.0 |
notebooklm login --browser-cookies <browser>, notebooklm auth inspect. |
pip install "notebooklm-py[cookies]" |
uv add "notebooklm-py[cookies]" |
headless |
gpsoauth>=1.1.0 |
notebooklm login --master-token — headless auth that mints/refreshes web cookies from a durable master token, no per-session browser. Pure-Python (in all). See § D. |
pip install "notebooklm-py[headless]" |
uv add "notebooklm-py[headless]" |
impersonate |
curl_cffi>=0.11 |
Experimental. Browser TLS/JA3 impersonation transport — set NOTEBOOKLM_TRANSPORT=curl_cffi to route the authenticated API surface through a Chrome-fingerprinted connection (insurance vs TLS fingerprint-gating); override the profile with NOTEBOOKLM_IMPERSONATE (default chrome, e.g. safari, chrome131). Native wheels. |
pip install "notebooklm-py[impersonate]" |
uv add "notebooklm-py[impersonate]" |
markdown |
markdownify>=0.14.1 |
notebooklm source fulltext -f markdown. |
pip install "notebooklm-py[markdown]" |
uv add "notebooklm-py[markdown]" |
mcp |
fastmcp>=2.14 |
Run the MCP server (notebooklm-mcp) so an MCP client/agent can drive NotebookLM as tools. |
pip install "notebooklm-py[mcp]" |
uv add "notebooklm-py[mcp]" |
server |
fastapi, uvicorn[standard], python-multipart |
The localhost REST API server (notebooklm-server, experimental). See § REST API server. |
pip install "notebooklm-py[server]" |
uv add "notebooklm-py[server]" |
dev |
pytest stack, mypy, ruff (==0.15.15 exact pin), pre-commit (>=4.5.1), vcrpy |
Contributor tooling only. Not sufficient for this repo's default uv run pytest; add browser too because some unit tests import Playwright. |
pip install "notebooklm-py[dev]" |
uv add "notebooklm-py[dev]" (in your project) — but contributors to this repo use the Persona E uv sync flow instead |
all |
Resolves to browser + dev + headless + markdown + mcp + server (not cookies) |
Contributors who do not need rookiepy. |
pip install "notebooklm-py[all]" |
uv add "notebooklm-py[all]" (in your project) — see All vs All-Extras |
Note on
uvcolumns: theuv (in your project)column is for users addingnotebooklm-pyas a dependency in their own project (requires apyproject.tomlin that project). Contributors working inside this repo use the Persona E flow (uv sync --frozen --extra ...), governed by this repo'suv.lock. Do not runuv syncoutside a project — it errors withNo pyproject.toml found.
REST API server
⚠️ Experimental. Like the MCP adapter, the REST server is experimental: the
/v1surface and behavior may change in a minor release, and it is excluded from the public-API compatibility gate. Pin a version before relying on it for automation. The server also logs an experimental warning on every startup.
A single-tenant, localhost REST API over the same transport-neutral core as the CLI — the natural shape for scripting and agent automation (feed a notebook, generate an artifact, pull it down) without spawning a CLI process per call.
uv tool install "notebooklm-py[server]" # fastapi + uvicorn + python-multipart
# OR, with pipx: pipx install "notebooklm-py[server]" (or plain pip inside a venv)
Prerequisite: a provisioned account (storage_state.json) from notebooklm login. The server holds one account for the process; it does not run browser login itself.
Launch:
export NOTEBOOKLM_SERVER_TOKEN="$(openssl rand -hex 32)" # REQUIRED — the server refuses to start without it
notebooklm-server --host 127.0.0.1 --port 8000 # loopback-only by default
Configuration is read from NOTEBOOKLM_SERVER_* env vars (overridable by the matching flags):
| Variable | Default | Purpose |
|---|---|---|
NOTEBOOKLM_SERVER_TOKEN |
(unset) | Bearer token every request must present. Required — fail-closed if unset. |
NOTEBOOKLM_SERVER_HOST |
127.0.0.1 |
Bind host. Non-loopback is refused unless the elevated-risk override below is set. |
NOTEBOOKLM_SERVER_PORT |
8000 |
Bind port. |
NOTEBOOKLM_SERVER_ALLOW_EXTERNAL_BIND |
(unset) | ⚠️ Set to 1 to bind a non-loopback interface. Only behind a trusted reverse proxy — this exposes account-fronting credentials to the network. |
NOTEBOOKLM_SERVER_SOURCE_MUTATION_CONCURRENCY |
4 |
Max concurrent source create/rename/delete/batch handlers. |
NOTEBOOKLM_SERVER_SOURCE_WAIT_CONCURRENCY |
4 |
Max concurrent source wait handlers. |
NOTEBOOKLM_SERVER_GENERATION_CONCURRENCY |
2 |
Max concurrent artifact generation/retry handlers. |
NOTEBOOKLM_SERVER_DOWNLOAD_CONCURRENCY |
2 |
Max concurrent artifact download handlers. |
NOTEBOOKLM_SERVER_RESEARCH_CONCURRENCY |
2 |
Max concurrent research start/cancel/import handlers. |
NOTEBOOKLM_SERVER_CHAT_CONCURRENCY |
4 |
Max concurrent blocking chat ask handlers. |
The concurrency knobs are route-group backpressure for expensive work. They do not gate /healthz or cheap read/list/poll routes.
Surface: every route is under /v1 and requires Authorization: Bearer <token> plus a loopback Host header (a DNS-rebinding guard). /healthz is the one public, token-less route. The auto-generated /docs / /openapi.json schema UI is disabled (it would otherwise be reachable token-less).
TOKEN=$NOTEBOOKLM_SERVER_TOKEN
BASE=http://127.0.0.1:8000
curl $BASE/healthz # {"ok": true} (no token)
curl -H "Authorization: Bearer $TOKEN" $BASE/v1/notebooks # list notebooks
curl -H "Authorization: Bearer $TOKEN" -d '{"title":"My NB"}' \
-H 'Content-Type: application/json' $BASE/v1/notebooks # create
curl -H "Authorization: Bearer $TOKEN" -d '{"url":"https://example.com"}' \
-H 'Content-Type: application/json' $BASE/v1/notebooks/<id>/sources/url
curl -H "Authorization: Bearer $TOKEN" -d '{"question":"Summarize"}' \
-H 'Content-Type: application/json' $BASE/v1/notebooks/<id>/chat # blocking answer
curl -H "Authorization: Bearer $TOKEN" $BASE/v1/notebooks/<id>/share # sharing status
Endpoints: /v1/notebooks (list/get/create/delete); /v1/notebooks/{id}/sources (list/get/add via url·text·file/delete); /v1/notebooks/{id}/notes (list/get/create/update via PUT/delete); /v1/notebooks/{id}/chat (blocking ask, no streaming); /v1/notebooks/{id}/artifacts (list / generate / poll / download); /v1/notebooks/{id}/share (status / public link / users / view level). Long-running work (source ingest, artifact generation) is poll-the-resource: the create call returns immediately and the matching GET reports pending until the resource is ready (200), 404 for an id the server never created, 409/410 for a failed/removed artifact.
Artifacts & uploads:
# Generate (non-blocking → 202 + task_id). Omit source_ids to use ALL sources
# (like the CLI); pass them to scope. Some types (quiz/flashcards) need at least one source.
curl -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"type":"quiz"}' $BASE/v1/notebooks/<id>/artifacts # → {"task_id": ...}
curl -H "Authorization: Bearer $TOKEN" $BASE/v1/notebooks/<id>/artifacts/<task_id> # poll
curl -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"type":"audio"}' $BASE/v1/notebooks/<id>/artifacts/download -o out.mp3 # download
# File upload is multipart (the original filename + content-type are preserved):
curl -H "Authorization: Bearer $TOKEN" -F 'file=@./notes.pdf' \
$BASE/v1/notebooks/<id>/sources/file
Error envelope: every failure is {"error": {"category": "...", "message": "..."}} with a category-derived HTTP status — not_found→404, validation→400/422, auth→401/403, rate_limited→429, notebook_limit→409, server/network→502, timeouts→504. The category is classified once by _app.errors.classify, shared with the CLI.
Post-install steps
playwright install chromium — when required, when auto-installed
- Required: when you'll use
notebooklm login(the interactive Playwright flow), unless the CLI auto-installs Chromium for you (it does — seeensure_chromium_installed()incli/services/playwright_login.py, which runspython -m playwright install chromiumon first login if Chromium is missing). - Not required: for headless servers (Persona D), library use (Persona C), or
--browser-cookies-based auth (Persona A/F with[cookies]).
playwright install-deps chromium — Linux system libraries
On Debian/Ubuntu, Playwright needs system libs for Chromium. Run after playwright install chromium:
playwright install-deps chromium # scoped to chromium; matches CI
Works without sudo if you're root or have passwordless sudo. Otherwise sudo playwright install-deps chromium.
First-time notebooklm login
notebooklm login # opens Chromium for Google sign-in
notebooklm auth check --test # verify
The login command:
- Auto-installs Chromium if missing (Persona A/B/E).
- Saves cookies to
~/.notebooklm/profiles/<profile>/storage_state.json. - Uses a persistent browser profile so subsequent logins are faster.
notebooklm skill install — for AI agents (Persona A)
Registers the skill into local agent skill directories:
notebooklm skill install # writes ~/.claude/skills/notebooklm/, ~/.agents/skills/notebooklm/
Optional — only needed if your agent harness reads from those directories and the skill isn't already present.
Running the MCP server (mcp extra)
The MCP server ships behind the optional mcp extra (see the extras matrix above) and exposes the same _app/ business logic over the Model Context Protocol.
notebooklm-mcp # installed console script (stdio transport)
uvx --from "notebooklm-py[mcp]" notebooklm-mcp # no install — run straight from PyPI
Wire it into an MCP client with either:
notebooklm mcp install <client>— auto-writes the server config forclaude-desktop,claude-code,cursor, orwindsurf; or- the one-click
.mcpbdesktop bundle — download it from the latest release (Assets) and use Claude Desktop's "Install Extension". Each stable release attaches a prebuilt, version-matched bundle; seedesktop-extension/README.md.
Full usage walkthrough (auth, transports, the 35 tools, workflows, troubleshooting): mcp-guide.md.
Verifying your install
| Command | What it checks | Use when |
|---|---|---|
notebooklm --version |
Package installed correctly. | Always. |
notebooklm auth check --json |
Auth file parses; SID cookie present. Returns {"status": "ok"|"error", "checks": {...}}. |
Agents (machine-parseable). |
notebooklm auth check --test |
Same + network token-fetch validates that cookies still authenticate against Google. | End users (after login). |
notebooklm auth check --test --json |
Both. | Agents that need to confirm the cookies aren't stale. |
notebooklm list |
Package + auth + RPC roundtrip all work. | After login, as a smoke test. |
Important:
notebooklm statusreports context state (which notebook is selected). It is not an auth check. See Common gotchas.
Your first end-to-end run:
notebooklm create "My First Notebook"
notebooklm source add 'https://en.wikipedia.org/wiki/Python_(programming_language)'
notebooklm ask "Summarize the sources in three sentences"
For the full CLI surface, see cli-reference.md.
Platform notes
| Platform | Install-time notes | Diagnostic detail |
|---|---|---|
| macOS | Chromium auto-downloads on first login. --browser-cookies from Chrome/Edge/Brave/Opera may prompt for Keychain access. |
troubleshooting.md#macos |
| Linux | (a) playwright install-deps chromium for system libs (Debian/Ubuntu). (b) Known bug: playwright > 1.57 may fail with TypeError: onExit is not a function — pin playwright==1.57.0. |
troubleshooting.md#linux |
| Windows | The library auto-configures WindowsSelectorEventLoopPolicy and PYTHONUTF8=1. Prefer plain pip install (uv/pipx less common on Windows). |
troubleshooting.md#windows |
| WSL | The browser opens in the Windows host (expected); storage_state.json lives in the WSL filesystem. |
troubleshooting.md#wsl |
Upgrading and uninstalling
pip install --upgrade notebooklm-py # latest patch
pip install --upgrade "notebooklm-py[browser]" # preserves your extras
For pinning patterns and version-stability guarantees, see stability.md.
To uninstall:
pip uninstall notebooklm-py
rm -rf ~/.notebooklm # optional: remove auth state
Common gotchas (appendix)
All vs All-Extras
⚠️
pip install ".[all]"anduv sync --all-extrasare not equivalent.
pyproject.tomldefines:all = ["notebooklm-py[browser,dev,markdown,mcp,server]"]— a self-referential extras string that resolves to browser + dev + markdown + mcp + server only. It deliberately excludescookiesbecauserookiepyhas install issues on Python 3.13+ (CHANGELOG[0.4.1]).uv sync --all-extrasinstalls every extra includingcookies, and may fail on Python 3.13/3.14.- In this repo, prefer
uv sync --frozen --extra browser --extra dev --extra markdown.
uv pip install vs uv sync
uv pip install -e ".[all]"ignores the checked-inuv.lock— it re-resolves dependencies and may pull newer versions ofplaywright,ruff, etc. than the lock specifies.uv sync --frozenenforces the lockfile and fails fast on drift. This is what contributors should use.uv sync(no--frozen) silently updatesuv.lockifpyproject.tomlhas changed. Use only when intentionally bumping deps.
notebooklm status ≠ auth
notebooklm status reports the currently selected notebook (context). It does NOT report whether you are authenticated. For auth, use notebooklm auth check (or --json / --test --json for machine output and network validation).