Google NotebookLM 沒有公開 API。

按理說,這種情況下你只剩兩條路:要嘛開瀏覽器自動化點按鈕,要嘛放棄自動化。但是 GitHub 上有個 14.9k star 的 repo 選了第三條——它打開 Chrome DevTools,把網頁裡每一次點擊背後的 XHR 請求抓下來,逆向出 Google 內部的 RPC 端點和那些被混淆過的 method ID,然後包成一個 Python SDK。

這個專案叫 notebooklm-py。它今天還在合併 PR,是少見的「非官方 Google 服務客戶端」做到專業到誇張的案例:6,186 unit tests、90% coverage、嚴格 SemVer、完整 ADR 記錄。Teng Lin 一個人寫的。

很多人聽到「沒有 API」會反射性想到 Selenium、Playwright、Puppeteer 那一套。但這個 repo 給了一個更值得想清楚的答案:瀏覽器自動化是為了當人,不是為了當機器。如果你能直接跟對方的 RPC 講話,為什麼還要假裝自己是個會點滑鼠的人?


它到底怎麼跟 NotebookLM 講話

你打開 NotebookLM 網頁時,Chrome DevTools 看到的所有 XHR 請求都打到同一個 endpoint:

1
https://notebooklm.google.com/_/LabsTailwindUi/data/batchexecute

這是 Google 內部用的 batchexecute RPC 通訊協定。每個操作對應一個被混淆過的 method ID——例如「建立 notebook」是某串看起來像亂碼的字串、「上傳 source」是另一串、「產生 podcast」又是另一串。這些 ID 是作者透過抓網路流量逆向出來的。

notebooklm-py 做的事情其實很單純:把這些 ID 包成 Python method,加上 retry、auth refresh、idempotency 保護,就成了一個完整的 SDK。

1
2
3
4
5
6
7
8
9
10
11
import asyncio
from notebooklm import NotebookLMClient

async def main():
async with NotebookLMClient.from_storage() as client:
notebook = await client.notebooks.create("週報研究")
await client.sources.add_url(notebook.id, "https://example.com/article")
audio = await client.audio.generate(notebook.id, language="zh-TW")
print(audio.download_url)

asyncio.run(main())

注意整個 API 是純 async。NotebookLMClient 綁定到開啟它的 event loop,跨 loop 重用會直接 raise RuntimeError、也不 thread-safe——每個 thread 或每個 loop 都要建自己的 client。這個設計選擇背後的理由很明顯:高頻 RPC 場景下,async 是必選項,不是裝飾品。


Playwright 只用在登入那一刻

這個 repo 最值得學的設計決策,是它沒有讓 Playwright 變成主流程。

很多人寫到「沒有 API」就會反射性 pip install playwright,然後從那一刻開始所有事情都會在瀏覽器裡跑。但 notebooklm-py 的選擇是:只有第一次互動式登入時用 Playwright——開個 Chromium 視窗讓你登 Google、把 cookies 抽出來存到 storage_state.json,之後所有 RPC 都直接走 httpx,根本不需要瀏覽器。

1
2
3
notebooklm login
# 開瀏覽器 → 登入 Google → cookies 存到
# ~/.notebooklm/profiles/default/storage_state.json

如果你本地已經登入過 NotebookLM 的 Chrome、Edge、Firefox、Safari、Brave,還可以用 rookiepy 直接從本地瀏覽器 cookie store 抽憑證,連 Playwright 都不用裝:

1
notebooklm login --browser-cookies chrome

把瀏覽器當「拿到 cookies 的工具」,而不是「跑 RPC 的執行環境」。這個分界線一畫下去,整個系統的可靠性立刻不一樣——RPC 走 httpx 是純 HTTP 呼叫,可以放進 CI、可以併發、可以背景跑;如果整條路都靠 Playwright,光是 Chromium 佔的記憶體就會讓你不想開太多平行任務。


9 種 web UI 不給你的 artifact

如果 SDK 提供的功能跟網頁上一樣,那其實用 Playwright 也可以做。notebooklm-py 真正值錢的地方,是它把 NotebookLM 後端能生但前端不給下載的東西全部打開了:

Artifact Web UI 給嗎 SDK 給的格式
Podcast (audio) 限線上播放 MP3 直接下載
Video overview 限線上看 MP4 直接下載
Briefing doc 給 Markdown
Mind map 限線上展開 樹狀 JSON 結構
Study guide 給 Markdown
Quiz 限線上做 JSON / Markdown / HTML
Flashcards 限線上看 JSON 結構化資料
Data table 限線上看 CSV
Slide deck 限線上展示 PPTX 檔

PPTX、Quiz JSON、Mind map 樹狀結構這三個是關鍵。web UI 是不給你的——你只能在頁面上看。如果你要把 NotebookLM 生成的內容餵到別的系統,例如把 quiz 匯進 Anki、把 mind map 接到 Obsidian 的知識圖譜、把 slide deck 套到自家簡報模板——這個 SDK 才是出口。

換句話說:NotebookLM 自己的網頁是給「個人用戶看完就好」的介面。notebooklm-py 是給「想把 NotebookLM 當生產線」的人用的。兩種使用者要的東西本來就不一樣,只是 Google 沒興趣同時服務後者。


認證機制是這個 repo 最棘手的部分

沒有 OAuth、沒有 API key,全靠 Google 帳號 cookies——SID、HSID、__Secure-1PSID、__Secure-1PSIDTS 這幾個。三種取得方式:

  1. 互動式登入:notebooklm login 開 Playwright 視窗,登完存 storage_state.json
  2. 抽本地瀏覽器:notebooklm login --browser-cookies chrome
  3. 環境變數:NOTEBOOKLM_AUTH_JSON 直接放 JSON(CI/CD 用)

棘手的地方在於 cookies 會過期,特別是 __Secure-1PSIDTS 會不定期旋轉。SDK 處理了這幾件事:

  • 每次 RPC 前自動檢查 CSRF token 是否仍有效
  • Cold-start 時主動呼叫 accounts.google.com/RotateCookies 補齊失效 token
  • 長時間執行的 client 可傳 keepalive=<seconds> 啟動背景 task 定期 rotate
  • CLI 提供 notebooklm auth refresh,搭配 cron / launchd / systemd 排程跑

Headless server 沒辦法跑 Playwright 登入,標準做法是先在有顯示器的機器登一次,把 storage_state.json 搬到伺服器或塞進 CI secret。storage_state.json 在系統裡的權重等同密碼——POSIX 系統記得 chmod 600,CI 一律走 secret。

如果你要管多個 Google 帳號,profile 機制把 storage_state 隔開來:

1
2
3
notebooklm profile create work          # 建立 "work" profile
notebooklm -p work login # 用 work profile 登入
notebooklm -p work notebooks list # 用 work 帳號列 notebooks

或設 NOTEBOOKLM_PROFILE=work 環境變數切換。


Middleware Chain:可靠性是設計出來的

這個 repo 有一套 7-stage middleware chain 包在每個 RPC 外面:

1
Drain → Metrics → Semaphore → Retry → AuthRefresh → ErrorInjection → Tracing

每一層都解決一個具體問題。預設行為:

  • 429 限流:尊重 Retry-After header(capped 300 秒),fallback 指數退避 min(2^attempt, 30)s + ±20% jitter,重試 3 次
  • 5xx / 網路錯誤:同樣指數退避 3 次
  • 併發控制:max_concurrent_rpcs=16(一般 RPC)、max_concurrent_uploads=4(上傳,防 FD 耗盡)
  • Idempotency:建立類 RPC(建 notebook、加 source)用 probe-then-retry——網路斷線導致回應丟失時,先 list 一次找出已建立的資源,避免重複建立

add_text 是個值得單獨拉出來講的例外:它無法 dedupe,預設標記為 non-idempotent。如果你想強制重試,要明確傳 idempotent=True,否則會 raise NonIdempotentRetryError。這個設計擋住了一個常見的工程意外——同樣的文字被重試到變成三份。


端到端範例:從主題到 Podcast 一條龍

repo 附帶的 docs/examples/research-to-podcast.py 是個經典的串接範例。輸入一個主題,自動深度研究後生 podcast:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import asyncio
import sys
from notebooklm import NotebookLMClient

async def main(topic: str):
async with NotebookLMClient.from_storage() as client:
# 1. 建 notebook
notebook = await client.notebooks.create(f"研究:{topic}")
# 2. 啟動深度研究(會自動爬 20+ 來源加進來)
research = await client.research.deep(notebook.id, topic)
await research.wait()
# 3. 用累積起來的 sources 生 podcast
audio = await client.audio.generate(
notebook.id,
language="zh-TW",
style="deep_dive",
)
print(f"Podcast: {audio.download_url}")

asyncio.run(main(sys.argv[1]))

單一指令 python research-to-podcast.py "量子計算最新進展" 就能生出一集 podcast。主流程全程不到 30 行——而這 30 行做的事,如果你硬走 Playwright 路線,光是處理「等深度研究跑完」這個 polling 就會寫到一百行還處理不完。


它真正適合的使用情境

寫完 SDK 不代表所有人都該用。比較值得花力氣串的場景有這幾個:

自動化 Podcast 工廠:餵入 RSS 或新聞 URL 清單,自動跑 generate_audio 產出 MP3,上傳到 podcast host。每日新聞摘要、研究週報廣播這種重複性高、人工成本不划算的內容,跑這個流程很適合。

企業知識庫研究 pipeline:使用者輸入主題 → 跑深度 research → 自動 import 20+ 來源 → 同時產 briefing doc + mind map + study guide → 寫進 Confluence 或 Notion。比走 RAG 系統好的地方是 source citation 是 NotebookLM 親自爬的,引用準確度比 fragmented chunk 強很多。

教育與學習工具:學生上傳教科書 PDF,自動 generate_quiz 加 generate_flashcards 產生練習題,匯出 JSON 餵 Anki 或自家學習平台。把「讀完一章 → 出題」的時間從幾小時壓到幾分鐘。

競爭情報監控:排程定期爬競品官網跟部落格,加進 NotebookLM 後問「跟上週相比訊息策略有什麼變化?」自動產生週報。NotebookLM 對長文做差異比較和總結的能力,比一般 LLM 直接接 prompt 強得多,因為它有完整的 source-grounded context。

AI Agent 整合:repo 附帶 SKILL.md(35KB 完整使用說明),可以註冊給 Claude Code、OpenClaw、Codex 這些 agent,讓 LLM 用自然語言驅動 NotebookLM:

1
2
notebooklm skill install
# 會寫入 ~/.claude/skills/notebooklm 與 ~/.agents/skills/notebooklm

一句話「幫我把這 5 個 URL 變成 30 分鐘的 deep-dive podcast」就能跑完。

多語言內容本地化:英文 source 直接 generate_audio(language="ja") 產日語 podcast,支援 50+ 語言。比走「英文 podcast → 字幕翻譯 → TTS 重錄」的傳統 pipeline 短得多。


該擔心的事情

帳號封鎖風險是這個 SDK 最大的不確定性。用未公開 API 加自動高頻 RPC,Google 可能視為自動化濫用。建議用次要帳號跑 batch,主帳號只做手動操作。

API 隨時可能變動:Google 的 RPC method ID 是逆向出來的,端點變了 SDK 就掛了。repo 有個聰明的設計——NOTEBOOKLM_RPC_OVERRIDES 環境變數允許社群熱修補不需等發版,但這也代表你不能假設「SDK 永遠跟得上 Google」。

不穩定的操作有幾類:audio、video、quiz、flashcard、infographic、slide-deck 容易因 Google rate limit 而 GENERATION_FAILED,需要 5-10 分鐘後重試。處理時間長:audio 10-20 分鐘、video 15-45 分鐘、deep research 15-30+ 分鐘。生產環境建議用 subagent 背景等待,不要在主流程同步阻塞使用者請求。

Source 上限依方案而定:Standard 50、Plus 100、Pro 300、Ultra 600。批次 import 前先查方案配額,跑到一半被 cap 住會很尷尬。

Python 3.13 + cookies extra 編譯失敗(已知限制),需要 rookiepy 的話請用 3.10–3.12。


真正值得帶走的觀察

「沒有公開 API」不等於「不能程式化」。RPC 端點都在那邊,只是 Google 沒貼說明書。如果一個服務的 web 介面跑得動,背後一定有 HTTP 流量可以分析。問題只是「值不值得花這個逆向工程的力氣」。

notebooklm-py 證明了一件事:當 web UI 卡住生產力的時候,把 Chrome DevTools Network panel 打開來看,往往比繼續用 Playwright 模擬點擊更接近正確答案。瀏覽器自動化是模擬人,但你不是人。


延伸閱讀