Last Updated: 2026-09-02
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)
- 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).
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 "notebooklm-py[browser,cookies,markdown]" |
The Android backend is an opt-in runtime choice: install
notebooklm-py[android] (plus [browser] for the one-time interactive
bootstrap), create the profile's durable credential with
notebooklm login --master-token --account EMAIL, then select Android with --backend android,
NOTEBOOKLM_BACKEND=android, or backend="android" in Python. It mints
short-lived mobile bearer tokens from master_token.json; typed namespace
operations do not use Web cookies or fall back to the Web transport. Android
dependencies are excluded from [all]. See the
Android backend guide for setup and security details.
The selection workflow summarizes the
argument, environment, and profile precedence used by every frontend.
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
pip install "notebooklm-py[cookies]" # optional browser-cookie extractionIf
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.
The [cookies] extra is optional; install it only when importing cookies from an
already signed-in browser. Interactive notebooklm login needs only [browser].
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-pyIf 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 onceAfter 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 # rookie-cookies autodetects an installed browserIf the agent is in a no-display sandbox and [cookies] isn't installed, 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).
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 for ordinary RPC use. It is needed only for interactiveloginor the explicitly enabled browser-backed L3 recovery path: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]"rookie-cookies"→ install or repair the[cookies]extra when using--browser-cookies"status": "ok"(inauth check --json) → auth file present and parses; pair with--testfor network validation
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 messageEmbedding 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. The optional browser-backed L3 recovery rung is unavailable unless [browser] is installed and explicitly enabled.
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.
Recommended: pip install notebooklm-py
Post-install (3-step recipe — Playwright is not required on the server unless you explicitly opt into browser-backed L3 recovery):
-
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.json
For CI, ship the master token — not a cookie snapshot. A cookie snapshot is superseded by any other active client within ~10 minutes and is rejected shortly after; use the session-refresh guidance in Troubleshooting, so a secret exported on a workstation is routinely dead before the run starts. A master token does not rotate. Write it to a file and mint a session per run:
# one-off, on the workstation: mint the master token, then ship it. # Plain `notebooklm login` (step 1) does NOT create master_token.json. pip install "notebooklm-py[headless]" notebooklm login --master-token --account you@example.com # writes ~/.notebooklm/profiles/default/master_token.json gh secret set NOTEBOOKLM_MASTER_TOKEN_JSON < ~/.notebooklm/profiles/default/master_token.json # in the job, before anything that authenticates. # [headless] pulls gpsoauth, without which the mint below cannot run. pip install "notebooklm-py[headless]" umask 077 mkdir -p ~/.notebooklm/profiles/default printf '%s' "$NOTEBOOKLM_MASTER_TOKEN_JSON" > ~/.notebooklm/profiles/default/master_token.json chmod 600 ~/.notebooklm/profiles/default/master_token.json unset NOTEBOOKLM_MASTER_TOKEN_JSON # `login` refuses to run while inline env auth is set notebooklm login --master-token-refresh # bootstraps storage_state.json from the token
CI notes:
- The mint bootstraps
storage_state.jsonwhen none exists, so the token is the only credential you ship. Layer-4 then covers mid-run expiry. master_token.jsonis a full-account credential — it mints OAuth for many Google services and does not rotate. It survives a password change: the only remediation for a leaked token is explicit revocation (Google Account → Security → Your devices → remove the device/session), so do not treat a password reset as containment. Use a dedicated/throwaway account, keep it in a protected environment,0600on disk, andunsetit from the environment once written (a file is not inherited by child processes; an env var is). See the security warning under master-token auth.- Requires the
[headless]extra (gpsoauth) on the runner. - This repo's four secret-bearing workflows all use exactly this shape and ship no cookie snapshot (development.md).
- Inline
NOTEBOOKLM_AUTH_JSONstill works for a single short-lived invocation, but it never engages the layer-4 re-mint and cannot persist a rotation, so it is not suitable for scheduled or long-lived jobs. See master-token auth.
- The mint bootstraps
-
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.
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
# Mint initial storage if only the token was shipped; --verify reuses the
# mandatory passive validation.
notebooklm auth refresh --verify
# Then run commands normally; dead existing sessions re-mint as needed.
notebooklm list
# Legacy escape hatch: force a re-mint unconditionally.
notebooklm login --master-token-refreshWhen storage is absent, auth refresh mints it from the exact sibling token and
passively validates once. When both files exist, cold or mid-session loading
re-mints only after the homepage/RotateCookies/headless ladder is exhausted.
The legacy login flag remains the unconditional forced route.
⚠️ Security: the master token is full-account, durable, and infostealer-grade — a materially larger blast radius than an expiringstorage_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.
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 installWhy 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. [all] itself expands to six extras — [browser, dev, headless, markdown, mcp, server] — but the command above installs only three of them (browser, dev, markdown), the contributor subset. cookies is intentionally excluded because browser-cookie extraction is optional; headless / mcp / server are the other three [all] extras, omitted from the default contributor flow because those adapters are not needed for the standard local suite. Opt in via --extra headless / --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=90Verify:
notebooklm --version
uv run pytest --cov=src/notebooklm --cov-report=term-missing --cov-fail-under=90
uv run pre-commit run --all-filesNon-default browsers, cookie extraction, markdown source dumps.
The [cookies] extra is optional and can be installed alongside [browser] when
you want browser-cookie extraction.
Don't use
[all]for power-user setups.[all]deliberately excludescookies(see § All vs All-Extras). Add the explicit[cookies]extra when using--browser-cookies.
--browser-cookies(no Playwright login):pip install "notebooklm-py[browser,cookies]". Thecookiesextra installsrookie-cookiesand supports Python 3.13+. See cli-reference.md#authentication-login for the full syntax, includingchrome::<profile-name-or-directory>andfirefox::<container>. 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.
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 |
rookie-cookies>=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 --account EMAIL — 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]" |
android |
exact pinned grpcio + protobuf, plus gpsoauth>=1.1.0 |
Android backend runtime dependencies. Install before selecting backend="android" / --backend android; all public namespaces stay on Android transport with no Web fallback. Requires the selected profile's master_token.json. Excluded from all. |
pip install "notebooklm-py[android]" |
uv add "notebooklm-py[android]" |
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==3.4.2 (exact pin) |
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.16.1 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 the android, cookies, or impersonate extra) |
Contributors using the established web stack. Because it includes dev, it still installs the pinned protobuf compiler/runtime used to verify checked-in stubs; it does not select an Android backend. |
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.
⚠️ 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.
The REST subsystem diagram shows its lifespan-owned client, route guards, concurrency limits, and response projection.
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 defaultConfiguration 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) | 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 /v1 route requires Authorization: Bearer <token> plus a loopback Host header (a DNS-rebinding guard). /healthz is the one public, token-less liveness probe — it returns {"ok": true} even when the NotebookLM client failed to open. Readiness would be a separate contract. The auto-generated /docs / /openapi.json schema UI is disabled (it would otherwise be reachable token-less). Operator threat model: SECURITY.md.
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 statusEndpoints: /v1/notebooks (list/get/create/delete); /v1/notebooks/{id}/sources (list/get/add via url·text·file·batch/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.
Routes own Pydantic input, HTTP status, and JSON/error projection. REST and MCP use the same central
builder and per-kind option table to construct exact frozen generation requests. The CLI constructs
the same typed variants but maps most Click inputs directly; all three adapters reuse neutral domain
validation, and the transport-neutral cores contain no HTTP or presentation policy. The batch source
route validates local members, sends all valid URLs once, and returns one ordered result per input
with commit_state confirmed, rejected, unknown, or not_sent. Continuation follows that
typed evidence rather than the HTTP status. Inputs and titles are capped and credential-redacted
before public and JSON projection.
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.m4a # 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/fileError 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.
- Required: when you'll use
notebooklm login(the interactive Playwright flow) or explicitly enable browser-backed layer-3 recovery. Login auto-installs Chromium when it is missing; the probe lives incli/services/playwright_login.pyand runspython -m playwright install chromium. - Not required: for headless servers (Persona D) and library users (Persona C) that supply storage state without opting into L3, or for
--browser-cookies-based auth (Persona A/F with[cookies]).
On Debian/Ubuntu, Playwright needs system libs for Chromium. Run after playwright install chromium:
playwright install-deps chromium # scoped to chromium; matches CIWorks without sudo if you're root or have passwordless sudo. Otherwise sudo playwright install-deps chromium.
notebooklm login # opens Chromium for Google sign-in
notebooklm auth check --test # verifyThe 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.
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.
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 PyPIWire 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.
For a self-hosted remote connector (claude.ai / mobile / ChatGPT over a tunnel), each release also
attaches a version-baked docker-compose.yml + env.example — no repo clone: curl them, fill
.env, docker compose --profile cloudflare up -d. See deploy/README.md.
Full usage walkthrough (auth, transports, the 38 tools, workflows, troubleshooting): mcp-guide.md.
| 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 | 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 |
pip install --upgrade notebooklm-py # latest patch
pip install --upgrade "notebooklm-py[browser]" # preserves your extrasFor pinning patterns and version-stability guarantees, see stability.md.
To uninstall:
pip uninstall notebooklm-py
rm -rf ~/.notebooklm # optional: remove auth state
⚠️ pip install ".[all]"anduv sync --all-extrasare not equivalent.
pyproject.tomldefines:all = ["notebooklm-py[browser,dev,headless,markdown,mcp,server]"]— a self-referential extras string that resolves to browser + dev + headless + markdown + mcp + server only. It omits theandroid,cookies, andimpersonateextra names. Sincedevcarries the exact protobuf regeneration toolchain,allstill resolves its pinned gRPC/protobuf packages; no Android backend is selected. The cookies extra remains separate because browser-cookie extraction is optional.uv sync --all-extrasinstalls every extra, including the optionalcookiesextractor.- In this repo, prefer
uv sync --frozen --extra browser --extra dev --extra markdown.
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 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).