notebooklm

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 17, 2026 License: Apache-2.0 Imports: 15 Imported by: 0

README

notebooklm-go

Unofficial pure-Go client for Google NotebookLM's internal RPC. Reverse-engineered from the browser bundle. Use at your own risk. Not affiliated with or endorsed by Google.

notebooklm-go lets Go programs (and via the bundled notebooklm CLI, shell / Python / JS / cron / Makefile users too) drive NotebookLM programmatically — list notebooks, add sources (PDF / URL / text), trigger Audio Overviews / Reports / Mind Maps / Slide Decks / Deep Research, and download generated artifacts. No browser automation, no Selenium — direct HTTPS calls to the same batchexecute RPC that the official JS bundle uses.

go install github.com/LocalKinAI/notebooklm-go/cmd/notebooklm@latest
notebooklm login          # paste Google session cookie
notebooklm list           # list your notebooks

Why this exists

NotebookLM is one of the highest-quality consumer LLM products Google ships: feed it PDFs/URLs, get back a 10–15 min conversational audio overview, a mind map, a slide deck, a deep research report. The model runs free for logged-in users (no API, no metering, no token bill).

There is, deliberately, no public API. Google's product economics require it stay UI-only — opening an API would commoditize the most expensive feature in their consumer line.

Yet programmatic access has obvious uses: bulk-processing a research library, archiving your own workflow, plugging NotebookLM into an agent system, building content pipelines.

The official client (the JS bundle) talks to notebooklm.google.com/ _/LabsTailwindUi/data/batchexecute over plain HTTPS. This library talks to the same endpoint, using your existing session cookie.

⚠️ Disclaimer

  • Reverse-engineered. Endpoints and RPC method IDs may change without notice. The library will break when Google changes them; pin to a release tag, not master.
  • Personal use only. High-volume / commercial scraping likely violates Google's Terms of Service. We don't recommend running this at fleet scale against accounts you don't own.
  • Not endorsed by Google. No warranty. No support contract. No promises about availability. If Google decides this protocol isn't reachable from non-browser clients, the library breaks.
  • Bring your own session. This library uses your logged-in Google session cookie. Anything you can do in the UI, the library can do — including delete your notebooks. Use a dedicated account if you're nervous.

Install

As a Go library
go get github.com/LocalKinAI/notebooklm-go@latest
package main

import (
    "fmt"
    "log"

    notebooklm "github.com/LocalKinAI/notebooklm-go"
)

func main() {
    // Reads cookies from ~/.config/notebooklm-go/auth.json by default.
    client, err := notebooklm.NewClient(notebooklm.DefaultStoragePath())
    if err != nil {
        log.Fatal(err)
    }

    notebooks, err := client.ListNotebooks()
    if err != nil {
        log.Fatal(err)
    }
    for _, nb := range notebooks {
        fmt.Printf("%s\t%s\n", nb.ID, nb.Title)
    }
}

See examples/ for runnable variants: list_notebooks, add_pdf_source, generate_audio_overview.

As a CLI
go install github.com/LocalKinAI/notebooklm-go/cmd/notebooklm@latest

notebooklm login                                # opens browser for one-time OAuth
notebooklm list                                 # list notebooks
notebooklm create "Paper Reading List"          # create a new notebook
notebooklm add <id> https://arxiv.org/pdf/X.pdf # add a URL source
notebooklm gen audio <id>                       # request Audio Overview
notebooklm download <id> <artifact-id> -o out.mp3

Authentication

NotebookLM uses standard Google session cookies. This library supports two flows:

Open NotebookLM in your browser, log in, then run:

notebooklm login

The CLI prints exactly which cookies it needs (SID, HSID, SSID, __Secure-1PSID etc.) and where to find them in Chrome DevTools. Paste the values; they're stored at ~/.config/notebooklm-go/auth.json (0600 perms).

2. Interactive: chromedp OAuth

If you have Chrome installed and don't mind a browser opening once, the Login helper pops Chrome, waits for you to sign in, then extracts and persists the session cookies to storagePath — after which NewClient reads them back like any other auth.json:

path := notebooklm.DefaultStoragePath()
if err := notebooklm.Login(path); err != nil {  // opens Chrome, you sign in once
    log.Fatal(err)
}
client, err := notebooklm.NewClient(path)

The notebooklm login CLI subcommand wraps this same flow.

RPC method coverage

rpc.go declares the known method IDs. All of them are reachable through the typed Client API:

Surface Methods covered
Notebooks List / Create / Get / Rename / Delete
Sources Add (URL/PDF/text) / Delete / Get / Refresh / Update
Summarize Get summary / Get source guide
Artifacts Create (Audio Deep Dive / Brief / Critique / Debate, Report, Video, Quiz, Mind Map, Infographic, Slide Deck, Data Table) / List / Delete / Export
Conversations Last conv ID / Conv turns
Notes Generate Mind Map / Create note / Get notes
Research Start Fast / Start Deep / Poll / Import
Sharing Share notebook / Get share status
Settings Get user settings

The batchexecute protocol (≈1 paragraph)

Google's frontends talk to backends via a universal envelope called batchexecute. Every "tool call" the JS bundle makes maps to an RPC identified by a 6-character method ID (e.g. wXbhsf for list-notebooks). A request is a triple-nested JSON: [[[rpc_id, params_json_string, null, "generic"]]]. The response is )]}'-prefixed JSON the body of which is also stringly-encoded inner JSON. Authentication is via cookies (SAPISIDHASH derivation) plus a 32-character at token grabbed from the page HTML. This library handles all of that — you call typed Go methods, it handles the encoding/decoding.

If you want to add a new RPC: open NotebookLM in Chrome, DevTools → Network → filter batchexecute, take whatever action you want to reverse-engineer, copy the request payload from the request body, and add a constant + typed wrapper to rpc.go / client.go. PRs welcome.

Why pure Go and not Python?

The existing Python equivalent is [notebooklm-py](https://github.com/ teng-lin/notebooklm-py) — same protocol, different language. We picked Go because:

  • One static binary. No pip install, no virtualenv, no missing system libraries. Cross-compile to Linux ARM64 → drop on a Raspberry Pi and it just runs.
  • Goroutines for concurrent scraping. Batch-add 50 sources, batch- generate 50 audio overviews, in parallel without thread-safety worries.
  • CLI binary is built-in. No python -m notebooklm_py.cli. Just notebooklm in your $PATH.
  • Embeddable. Other Go projects can vendor this without dragging in a Python runtime.

We're standing on notebooklm-py's shoulders — the protocol they reverse-engineered is exactly the protocol we use. Independent Go re-implementation, no shared code.

Production usage

This library is the audio backbone of LocalKin's content publishing pipeline. As of v2.6 of content_director.soul, NotebookLM audio generation is pure-Go via this library (we dropped Playwright + Chromium and saved ~500 MB RAM per pipeline run).

If you're building a similar pipeline, the [LocalKin papers](https:// localkin.dev/papers) cover the architecture — particularly papers #2 (Heart cron) and #8 (Swarm Genesis).

Versioning

Pre-1.0 — the RPC method IDs can change with Google's frontend bundle updates. We tag releases when we hit breakage in production and have patched it. Pin to a release tag:

// go.mod
require github.com/LocalKinAI/notebooklm-go v0.1.0

If you hit a breakage, file an issue with the failing request payload (redact your at token + cookies first).

Contributing

PRs welcome — especially for:

  • New RPC methods (Mind Map node manipulation, custom artifact prompts, etc.)
  • Better headless auth flows
  • Test coverage (currently rpc_test.go covers envelope encoding only)
  • CLI command coverage
  • Documentation in examples/

See examples/ for working programs.

Acknowledgments

  • notebooklm-py — the Python project that first reverse-engineered the batchexecute protocol for NotebookLM. This Go port re-implements independently but borrows the protocol understanding.
  • chromedp — the headless Chrome library used for the interactive OAuth path.

License

Apache License 2.0. See LICENSE.

Author

LocalKinAI — building AI agents that run on local hardware. See also [11 research papers on Zenodo](https:// localkin.dev/papers) for the architectural thesis.

Documentation

Overview

Package notebooklm provides a Go client for Google NotebookLM's internal RPC API. Reverse-engineered from the notebooklm-py project (github.com/teng-lin/notebooklm-py).

This is an unofficial client using undocumented Google APIs. Use at your own risk — endpoints may change without notice.

Index

Constants

View Source
const (
	// Notebook operations
	RPCListNotebooks  = "wXbhsf"
	RPCCreateNotebook = "CCqFvf"
	RPCGetNotebook    = "rLM1Ne"
	RPCRenameNotebook = "s0tc2d"
	RPCDeleteNotebook = "WWINqb"

	// Source operations
	RPCAddSource     = "izAoDd"
	RPCDeleteSource  = "tGMBJ"
	RPCGetSource     = "hizoJc"
	RPCRefreshSource = "FLmJqe"
	RPCUpdateSource  = "b7Wfje"

	// Summary and query
	RPCSummarize      = "VfAZjd"
	RPCGetSourceGuide = "tr032e"

	// Artifact operations
	RPCCreateArtifact = "R7cb6c"
	RPCListArtifacts  = "gArtLc"
	RPCDeleteArtifact = "V5N4be"
	RPCExportArtifact = "Krh3pd"

	// Conversation
	RPCGetLastConvID = "hPTbtc"
	RPCGetConvTurns  = "khqZz"

	// Notes & mind maps
	RPCGenerateMindMap = "yyryJe"
	RPCCreateNote      = "CYK0Xb"
	RPCGetNotes        = "cFji9"

	// Research
	RPCStartFastResearch = "Ljjv0c"
	RPCStartDeepResearch = "QA9ei"
	RPCPollResearch      = "e3bVqc"
	RPCImportResearch    = "LBwxtb"

	// Sharing
	RPCShareNotebook  = "QDyure"
	RPCGetShareStatus = "JFMDGd"

	// Settings
	RPCGetUserSettings = "ZwVcOc"
)

RPC method IDs — obfuscated identifiers used by Google's batchexecute API.

View Source
const (
	ArtifactAudio     = 1
	ArtifactReport    = 2
	ArtifactVideo     = 3
	ArtifactQuiz      = 4
	ArtifactMindMap   = 5
	ArtifactInfogr    = 7
	ArtifactSlideDeck = 8
	ArtifactDataTable = 9
)

Artifact type codes

View Source
const (
	AudioDeepDive = 1
	AudioBrief    = 2
	AudioCritique = 3
	AudioDebate   = 4
)

Audio format codes

Variables

This section is empty.

Functions

func DefaultStoragePath

func DefaultStoragePath() string

DefaultStoragePath returns the default path for the storage state file.

func LoadCookieJarFromStorage

func LoadCookieJarFromStorage(path string) (http.CookieJar, error)

LoadCookieJarFromStorage builds a Go cookiejar from Playwright storage_state.json, preserving domain / path / secure / httpOnly info.

This is REQUIRED for media downloads: NotebookLM hands out URLs on domains like `googleusercontent.com`, and a single cross-domain Cookie header (the shortcut used by RPC calls on notebooklm.google.com) won't be honored. A proper jar sends the right cookies per destination host as the client follows redirects.

func LoadCookiesFromStorage

func LoadCookiesFromStorage(path string) (string, error)

LoadCookiesFromStorage reads a Playwright storage_state.json file and returns the Cookie header string for Google domains.

func Login

func Login(storagePath string) error

Login opens a browser window for the user to sign into Google, then captures cookies and saves them as a Playwright-compatible storage state.

Types

type Artifact

type Artifact struct {
	ID        string `json:"id"`
	Title     string `json:"title"`
	TypeCode  int    `json:"type_code"`
	Status    int    `json:"status"`
	CreatedAt int64  `json:"created_at,omitempty"`
}

Artifact represents a NotebookLM artifact (audio, video, quiz, etc.).

Field mapping to the raw RPC response array (verified against notebooklm-py v0.3 Artifact.from_api_response, 2026-04-20):

data[0]  → ID       (string UUID)
data[1]  → Title    (string; CJK chars → zh, else en)
data[2]  → TypeCode (1=video, 3=audio, 7=infographic, etc.)
data[4]  → Status
data[15][0] → CreatedAt (unix seconds)

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client is the Go client for Google NotebookLM's internal RPC API.

func NewClient

func NewClient(storagePath string) (*Client, error)

NewClient creates a new NotebookLM client from a storage state file.

func (*Client) AddTextSource

func (c *Client) AddTextSource(notebookID, title, content string) error

AddTextSource adds plain text as a source to a notebook.

func (*Client) AddURLSource

func (c *Client) AddURLSource(notebookID, sourceURL string) error

AddURLSource adds a web URL as a source to a notebook.

func (*Client) AddYouTubeSource

func (c *Client) AddYouTubeSource(notebookID, youtubeURL string) error

AddYouTubeSource adds a YouTube video as a source to a notebook.

func (*Client) Chat

func (c *Client) Chat(notebookID, question string) (string, error)

Chat sends a question to a notebook and returns the response. Uses NotebookLM's GenerateFreeFormStreamed endpoint (not batchexecute).

func (*Client) CheckAuth

func (c *Client) CheckAuth() error

CheckAuth verifies that cookies and tokens are valid by making a test request.

func (*Client) CreateNotebook

func (c *Client) CreateNotebook(title string) (string, error)

CreateNotebook creates a new notebook with the given title.

func (*Client) DeleteNotebook

func (c *Client) DeleteNotebook(notebookID string) error

DeleteNotebook deletes a notebook by ID.

func (*Client) DownloadArtifact

func (c *Client) DownloadArtifact(notebookID, artifactID, outputPath string) error

DownloadArtifact downloads an artifact to a local file.

Architecture note (2026-04-20): the previous implementation called a separate RPC `RPCExportArtifact` to get a download URL — that RPC is for exporting reports/data-tables to Google Docs/Sheets, NOT for media download. Its response contains no URL, so `could not extract download URL` was the inevitable outcome.

The correct mechanism (per notebooklm-py's download_audio/video/infographic in _artifacts.py:955+) is: the ListArtifacts response already contains media URLs embedded in type-specific positions:

  • Audio (type 1): artifact[6][5] — list of {url, ?, mime} tuples, find mime="audio/mp4"
  • Video (type 3): artifact[8] — list of lists, first URL with mime="video/mp4" preferred (quality tag == 4)
  • Infographic (type 7): artifact[N][2][0][1][0] for N scanned from end of entry — raw PNG url

So "downloading" is really: list → find by ID → parse URL from raw → HTTP GET.

func (*Client) GenerateArtifact

func (c *Client) GenerateArtifact(notebookID string, typeCode int) (string, error)

GenerateArtifact creates any artifact type (video, quiz, slides, etc.).

func (*Client) GenerateAudio

func (c *Client) GenerateAudio(notebookID string, format int) (string, error)

GenerateAudio creates an audio overview (podcast) for a notebook.

func (*Client) GetNotebookMetadata

func (c *Client) GetNotebookMetadata(notebookID string) (json.RawMessage, error)

GetNotebookMetadata returns detailed notebook info including sources.

func (*Client) GetShareStatus

func (c *Client) GetShareStatus(notebookID string) (json.RawMessage, error)

GetShareStatus returns the sharing state of a notebook.

func (*Client) GetSourceGuide

func (c *Client) GetSourceGuide(notebookID string) (string, error)

GetSourceGuide retrieves the auto-generated summary for a notebook's sources.

func (*Client) ListArtifacts

func (c *Client) ListArtifacts(notebookID string) ([]Artifact, error)

ListArtifacts returns all artifacts in a notebook.

RPC params (verified against notebooklm-py v0.3 _artifacts.py:263, 2026-04-20):

params = [[2], notebook_id, "NOT artifact.status = \"ARTIFACT_STATUS_SUGGESTED\""]

Passing only [notebook_id] makes Google's backend return a null payload (the outer envelope says "generic" but the inner data slot is null).

Response envelope is typically [[<artifact-entry>, ...]] — a single-element outer list wrapping the actual entries. Each entry is:

[ id, title, typeCode, _, status, _, _, _, _, [..variant..], _, _, _, _, _, [ts_sec, ts_ns], ... ]

We read fields 0 (id), 1 (title), 2 (type), 4 (status), 15[0] (createdAt).

func (*Client) ListNotebooks

func (c *Client) ListNotebooks() ([]Notebook, error)

ListNotebooks returns all notebooks in the account.

func (*Client) StartResearch

func (c *Client) StartResearch(notebookID, query, mode string) (string, error)

StartResearch begins a web research query and auto-imports discovered sources. mode: "fast" or "deep"

type Notebook

type Notebook struct {
	ID    string `json:"id"`
	Title string `json:"title"`
}

Notebook represents a NotebookLM notebook.

type Tokens

type Tokens struct {
	CSRF      string // SNlM0e value
	SessionID string // FdrFJe value
}

Tokens holds the CSRF and session tokens needed for RPC calls.

func FetchTokens

func FetchTokens(cookieHeader string) (*Tokens, error)

FetchTokens makes an authenticated GET request to NotebookLM and extracts CSRF (SNlM0e) and session (FdrFJe) tokens from the page HTML.

Directories

Path Synopsis
cmd
notebooklm command
notebooklm — CLI wrapper for github.com/LocalKinAI/notebooklm-go.
notebooklm — CLI wrapper for github.com/LocalKinAI/notebooklm-go.
examples
add_pdf_source command
add_pdf_source — add a remote PDF (or any URL NotebookLM accepts) to an existing notebook.
add_pdf_source — add a remote PDF (or any URL NotebookLM accepts) to an existing notebook.
generate_audio_overview command
generate_audio_overview — request a "Deep Dive" audio overview for a notebook, poll until ready, download the MP3.
generate_audio_overview — request a "Deep Dive" audio overview for a notebook, poll until ready, download the MP3.
list_notebooks command
list_notebooks — the simplest possible notebooklm-go program.
list_notebooks — the simplest possible notebooklm-go program.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL