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——SIDHSID__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_quizgenerate_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 模擬點擊更接近正確答案。瀏覽器自動化是模擬人,但你不是人。


延伸閱讀