File upload uses HTTP Resumable Upload – the reliable, headless method.
Uses Google's 3-step resumable upload protocol:
o4cbdc) → get SOURCE_ID/upload/_/) → get upload URLSourceMixin._register_file_source() - Step 1: Register and get SOURCE_IDSourceMixin._start_resumable_upload() - Step 2: Get upload URLSourceMixin._upload_file_streaming() - Step 3: Stream file contentSourceMixin.add_file() - Public API that orchestrates all 3 stepsPython:
from notebooklm_tools.core.client import NotebookLMClient
with NotebookLMClient() as client:
result = client.add_file(notebook_id="...", file_path="document.pdf")
# Returns: {"id": "source-id", "title": "document.pdf"}
CLI:
nlm source add <notebook-id> --file document.pdf
MCP:
source_add(notebook_id="...", source_type="file", file_path="/path/to/file.pdf")
.pdf, .txt, .md, .docx, .csv).epub).mp3, .m4a, .wav, .aac, .ogg, .opus).mp4).jpg, .jpeg, .png, .gif, .webp)# Create a test file
echo "Test content" > test.txt
# Upload the file
nlm source add <notebook-id> --file test.txt
# Verify it was added
nlm source list <notebook-id>
A browser-based upload fallback using Chrome automation was previously available (--browser flag) but has been removed. NotebookLM's UI now uses the File System Access API which cannot be automated via CDP. The HTTP method is more reliable anyway.