Skip to content

Latest commit

 

History

History
783 lines (614 loc) · 22.7 KB

File metadata and controls

783 lines (614 loc) · 22.7 KB

Python API Reference

Status: Active Last Updated: 2026-01-08

Complete reference for the notebooklm Python library.

Quick Start

import asyncio
from notebooklm import NotebookLMClient

async def main():
    # Create client from saved authentication
    async with await NotebookLMClient.from_storage() as client:
        # List notebooks
        notebooks = await client.notebooks.list()
        print(f"Found {len(notebooks)} notebooks")

        # Create a new notebook
        nb = await client.notebooks.create("My Research")
        print(f"Created: {nb.id}")

        # Add sources
        await client.sources.add_url(nb.id, "https://example.com/article")

        # Ask a question
        result = await client.chat.ask(nb.id, "Summarize the main points")
        print(result.answer)

        # Generate a podcast
        status = await client.artifacts.generate_audio(nb.id)
        final = await client.artifacts.wait_for_completion(nb.id, status.task_id)
        print(f"Audio ready: {final.url}")

asyncio.run(main())

Core Concepts

Async Context Manager

The client must be used as an async context manager to properly manage HTTP connections:

# Correct - uses context manager
async with await NotebookLMClient.from_storage() as client:
    ...

# Also correct - manual management
client = await NotebookLMClient.from_storage()
await client.__aenter__()
try:
    ...
finally:
    await client.__aexit__(None, None, None)

Authentication

The client requires valid Google session cookies obtained via browser login:

# From storage file (recommended)
client = await NotebookLMClient.from_storage()
client = await NotebookLMClient.from_storage("/path/to/storage_state.json")

# From AuthTokens directly
from notebooklm import AuthTokens
auth = AuthTokens(
    cookies={"SID": "...", "HSID": "...", ...},
    csrf_token="...",
    session_id="..."
)
client = NotebookLMClient(auth)

Environment Variable Support:

The library respects these environment variables for authentication:

Variable Description
NOTEBOOKLM_HOME Base directory for config files (default: ~/.notebooklm)
NOTEBOOKLM_AUTH_JSON Inline auth JSON - no file needed (for CI/CD)

Precedence (highest to lowest):

  1. Explicit path argument to from_storage()
  2. NOTEBOOKLM_AUTH_JSON environment variable
  3. $NOTEBOOKLM_HOME/storage_state.json
  4. ~/.notebooklm/storage_state.json

CI/CD Example:

import os

# Set auth JSON from environment (e.g., GitHub Actions secret)
os.environ["NOTEBOOKLM_AUTH_JSON"] = '{"cookies": [...]}'

# Client automatically uses the env var
async with await NotebookLMClient.from_storage() as client:
    notebooks = await client.notebooks.list()

Error Handling

The library raises RPCError for API failures:

from notebooklm import RPCError

try:
    result = await client.notebooks.create("Test")
except RPCError as e:
    print(f"RPC failed: {e}")
    # Common causes:
    # - Session expired (re-run `notebooklm login`)
    # - Rate limited (wait and retry)
    # - Invalid parameters

Refreshing Authentication

Sessions can expire. Refresh without re-logging in:

async with await NotebookLMClient.from_storage() as client:
    # Refresh CSRF token and session ID
    await client.refresh_auth()

API Reference

NotebookLMClient

Main client class providing access to all APIs.

class NotebookLMClient:
    notebooks: NotebooksAPI    # Notebook operations
    sources: SourcesAPI        # Source management
    artifacts: ArtifactsAPI    # AI-generated content
    chat: ChatAPI              # Conversations
    research: ResearchAPI      # Web/Drive research
    notes: NotesAPI            # User notes

    @classmethod
    async def from_storage(cls, path: str = None) -> "NotebookLMClient"

    async def refresh_auth(self) -> AuthTokens

NotebooksAPI (client.notebooks)

Method Parameters Returns Description
list() - list[Notebook] List all notebooks
create(title) title: str Notebook Create a notebook
get(notebook_id) notebook_id: str Notebook Get notebook details
delete(notebook_id) notebook_id: str bool Delete a notebook
rename(notebook_id, new_title) notebook_id: str, new_title: str Notebook Rename a notebook
get_description(notebook_id) notebook_id: str NotebookDescription Get AI summary and topics
get_summary(notebook_id) notebook_id: str str Get raw summary text
share(notebook_id, settings=None) notebook_id: str, settings: dict Any Share notebook with settings
remove_from_recent(notebook_id) notebook_id: str None Remove from recently viewed
get_raw(notebook_id) notebook_id: str Any Get raw API response data

Example:

# List all notebooks
notebooks = await client.notebooks.list()
for nb in notebooks:
    print(f"{nb.id}: {nb.title} ({nb.sources_count} sources)")

# Create and rename
nb = await client.notebooks.create("Draft")
nb = await client.notebooks.rename(nb.id, "Final Version")

# Get AI-generated description (parsed with suggested topics)
desc = await client.notebooks.get_description(nb.id)
print(desc.summary)
for topic in desc.suggested_topics:
    print(f"  - {topic.question}")

# Get raw summary text (unparsed)
summary = await client.notebooks.get_summary(nb.id)
print(summary)

# Share a notebook
await client.notebooks.share(nb.id, settings={"public": True})

get_summary vs get_description:

  • get_summary() returns the raw summary text string
  • get_description() returns a NotebookDescription object with the parsed summary and a list of SuggestedTopic objects for suggested questions

SourcesAPI (client.sources)

Method Parameters Returns Description
list(notebook_id) notebook_id: str list[Source] List sources
get(notebook_id, source_id) str, str Source Get source details
add_url(notebook_id, url) str, str Source Add URL source
add_youtube(notebook_id, url) str, str Source Add YouTube video
add_text(notebook_id, title, content) str, str, str Source Add text content
add_file(notebook_id, path, mime_type=None) str, Path, str Source Upload file
add_drive(notebook_id, file_id, title, mime_type) str, str, str, str Source Add Google Drive doc
rename(notebook_id, source_id, new_title) str, str, str Source Rename source
refresh(notebook_id, source_id) str, str bool Refresh URL/Drive source
delete(notebook_id, source_id) str, str bool Delete source

Example:

# Add various source types
await client.sources.add_url(nb_id, "https://example.com/article")
await client.sources.add_youtube(nb_id, "https://youtube.com/watch?v=...")
await client.sources.add_text(nb_id, "My Notes", "Content here...")
await client.sources.add_file(nb_id, Path("./document.pdf"))

# List and manage
sources = await client.sources.list(nb_id)
for src in sources:
    print(f"{src.id}: {src.title} ({src.source_type})")

await client.sources.rename(nb_id, src.id, "Better Title")
await client.sources.refresh(nb_id, src.id)  # Re-fetch URL content

ArtifactsAPI (client.artifacts)

Core Methods

Method Parameters Returns Description
list(notebook_id, type=None) str, int list[Artifact] List artifacts
get(notebook_id, artifact_id) str, str Artifact Get artifact details
delete(notebook_id, artifact_id) str, str bool Delete artifact
rename(notebook_id, artifact_id, new_title) str, str, str None Rename artifact
poll_status(notebook_id, task_id) str, str GenerationStatus Check generation status
wait_for_completion(notebook_id, task_id, ...) str, str, ... GenerationStatus Wait for generation

Type-Specific List Methods

Method Parameters Returns Description
list_audio(notebook_id) str list[Artifact] List audio overview artifacts
list_video(notebook_id) str list[Artifact] List video overview artifacts
list_reports(notebook_id) str list[Artifact] List report artifacts (Briefing Doc, Study Guide, Blog Post)
list_quizzes(notebook_id) str list[Artifact] List quiz artifacts
list_flashcards(notebook_id) str list[Artifact] List flashcard artifacts
list_infographics(notebook_id) str list[Artifact] List infographic artifacts
list_slide_decks(notebook_id) str list[Artifact] List slide deck artifacts
list_data_tables(notebook_id) str list[Artifact] List data table artifacts

Generation Methods

Method Parameters Returns Description
generate_audio(...) See below GenerationStatus Generate podcast
generate_video(...) See below GenerationStatus Generate video
generate_report(...) See below GenerationStatus Generate report
generate_quiz(...) See below GenerationStatus Generate quiz
generate_flashcards(...) See below GenerationStatus Generate flashcards
generate_slide_deck(...) See below GenerationStatus Generate slide deck
generate_infographic(...) See below GenerationStatus Generate infographic
generate_data_table(...) See below GenerationStatus Generate data table
generate_mind_map(...) See below dict Generate mind map

Downloading Artifacts

Method Parameters Returns Description
download_audio(notebook_id, output_path, artifact_id=None) str, str, str str Download audio to file (MP4/MP3)
download_video(notebook_id, output_path, artifact_id=None) str, str, str str Download video to file (MP4)
download_infographic(notebook_id, output_path, artifact_id=None) str, str, str str Download infographic to file (PNG)
download_slide_deck(notebook_id, output_path, artifact_id=None) str, str, str str Download slide deck as PDF

Download Methods:

# Download the most recent completed audio overview
path = await client.artifacts.download_audio(nb_id, "podcast.mp4")

# Download a specific audio artifact by ID
path = await client.artifacts.download_audio(nb_id, "podcast.mp4", artifact_id="abc123")

# Download video overview
path = await client.artifacts.download_video(nb_id, "video.mp4")

# Download infographic
path = await client.artifacts.download_infographic(nb_id, "infographic.png")

# Download slide deck as PDF
path = await client.artifacts.download_slide_deck(nb_id, "./slides.pdf")
# Returns: "./slides.pdf"

Notes:

  • If artifact_id is not specified, downloads the first completed artifact of that type
  • Raises ValueError if no completed artifact is found
  • Some URLs require browser-based download (handled automatically)

Export Methods

Export artifacts to Google Docs or Google Sheets.

Method Parameters Returns Description
export_report(notebook_id, artifact_id, title, export_type) str, str, str, ExportType Any Export report to Google Docs/Sheets
export_data_table(notebook_id, artifact_id, title) str, str, str Any Export data table to Google Sheets
export(notebook_id, artifact_id, content, title, export_type) str, str, str, str, ExportType Any Generic export to Docs/Sheets

Export Types (ExportType enum):

  • ExportType.DOCS (1): Export to Google Docs
  • ExportType.SHEETS (2): Export to Google Sheets
from notebooklm import ExportType

# Export a report to Google Docs
result = await client.artifacts.export_report(
    nb_id,
    artifact_id="report_123",
    title="My Briefing Doc",
    export_type=ExportType.DOCS
)
# result contains the Google Docs URL

# Export a data table to Google Sheets
result = await client.artifacts.export_data_table(
    nb_id,
    artifact_id="table_456",
    title="Research Data"
)
# result contains the Google Sheets URL

# Generic export (e.g., export any artifact to Sheets)
result = await client.artifacts.export(
    nb_id,
    artifact_id="artifact_789",
    title="Exported Content",
    export_type=ExportType.SHEETS
)

Generation Methods:

# Audio (podcast)
status = await client.artifacts.generate_audio(
    notebook_id,
    source_ids=None,           # List of source IDs (None = all)
    instructions="...",        # Custom instructions
    audio_format=AudioFormat.DEEP_DIVE,  # DEEP_DIVE, BRIEF, CRITIQUE, DEBATE
    audio_length=AudioLength.DEFAULT,    # SHORT, DEFAULT, LONG
    language="en"
)

# Video
status = await client.artifacts.generate_video(
    notebook_id,
    source_ids=None,
    instructions="...",
    video_format=VideoFormat.EXPLAINER,  # EXPLAINER, BRIEF
    video_style=VideoStyle.AUTO_SELECT,  # AUTO_SELECT, CLASSIC, WHITEBOARD, KAWAII, ANIME, etc.
    language="en"
)

# Report
status = await client.artifacts.generate_report(
    notebook_id,
    source_ids=None,
    title="...",
    description="...",
    format=ReportFormat.STUDY_GUIDE,  # BRIEFING_DOC, STUDY_GUIDE, BLOG_POST, CUSTOM
    language="en"
)

# Quiz
status = await client.artifacts.generate_quiz(
    notebook_id,
    source_ids=None,
    instructions="...",
    quantity=QuizQuantity.STANDARD,    # FEWER, STANDARD
    difficulty=QuizDifficulty.MEDIUM,  # EASY, MEDIUM, HARD
    language="en"
)

Waiting for Completion:

# Start generation
status = await client.artifacts.generate_audio(nb_id)

# Wait with polling
final = await client.artifacts.wait_for_completion(
    nb_id,
    status.task_id,
    timeout=300,      # Max wait time in seconds
    poll_interval=5   # Seconds between polls
)

if final.is_complete:
    print(f"Download URL: {final.url}")
else:
    print(f"Failed or timed out: {final.status}")

ChatAPI (client.chat)

Method Parameters Returns Description
ask(notebook_id, question, ...) str, str, ... AskResult Ask a question
configure(notebook_id, ...) str, ... bool Set chat persona
get_history(notebook_id) str list[ConversationTurn] Get conversation

Example:

# Ask questions
result = await client.chat.ask(nb_id, "What are the main themes?")
print(result.answer)

# Continue conversation
result = await client.chat.ask(
    nb_id,
    "Can you elaborate on the first point?",
    conversation_id=result.conversation_id
)

# Configure persona
await client.chat.configure(
    nb_id,
    goal=ChatGoal.LEARNING_GUIDE,
    response_length=ChatResponseLength.LONGER,
    custom_prompt="Focus on practical applications"
)

ResearchAPI (client.research)

Method Parameters Returns Description
start(notebook_id, query, source, mode) str, str, str="web", str="fast" dict Start research (mode: "fast" or "deep")
poll(notebook_id) str dict Check research status
import_sources(notebook_id, task_id, sources) str, str, list list[dict] Import findings

Method Signatures:

async def start(
    notebook_id: str,
    query: str,
    source: str = "web",   # "web" or "drive"
    mode: str = "fast",    # "fast" or "deep" (deep only for web)
) -> dict:
    """
    Returns: {"task_id": str, "report_id": str, "notebook_id": str, "query": str, "mode": str}
    Raises: ValueError if source/mode combination is invalid
    """

async def poll(notebook_id: str) -> dict:
    """
    Returns: {"task_id": str, "status": str, "query": str, "sources": list, "summary": str}
    Status is "completed", "in_progress", or "no_research"
    """

async def import_sources(notebook_id: str, task_id: str, sources: list[dict]) -> list[dict]:
    """
    sources: List of dicts with 'url' and 'title' keys
    Returns: List of imported sources with 'id' and 'title'
    """

Example:

# Start fast web research (default)
result = await client.research.start(nb_id, "AI safety regulations")
task_id = result["task_id"]

# Start deep web research
result = await client.research.start(nb_id, "quantum computing", source="web", mode="deep")
task_id = result["task_id"]

# Start fast Drive research
result = await client.research.start(nb_id, "project docs", source="drive", mode="fast")

# Poll until complete
import asyncio
while True:
    status = await client.research.poll(nb_id)
    if status["status"] == "completed":
        break
    await asyncio.sleep(10)

# Import discovered sources
imported = await client.research.import_sources(nb_id, task_id, status["sources"][:5])
print(f"Imported {len(imported)} sources")

NotesAPI (client.notes)

Method Parameters Returns Description
list(notebook_id) str list[Note] List text notes (excludes mind maps)
create(notebook_id, title="New Note", content="") str, str, str Note Create note
get(notebook_id, note_id) str, str Optional[Note] Get note by ID
update(notebook_id, note_id, content, title) str, str, str, str None Update note content and title
delete(notebook_id, note_id) str, str bool Delete note
list_mind_maps(notebook_id) str list[Any] List mind maps in the notebook
delete_mind_map(notebook_id, mind_map_id) str, str bool Delete a mind map

Example:

# Create and manage notes
note = await client.notes.create(nb_id, title="Meeting Notes", content="Discussion points...")
notes = await client.notes.list(nb_id)

# Update a note
await client.notes.update(nb_id, note.id, "Updated content", "New Title")

# Delete a note
await client.notes.delete(nb_id, note.id)

Mind Maps:

Mind maps are stored internally using the same structure as notes but contain JSON data with hierarchical node information. The list() method excludes mind maps automatically, while list_mind_maps() returns only mind maps.

# List all mind maps in a notebook
mind_maps = await client.notes.list_mind_maps(nb_id)
for mm in mind_maps:
    mm_id = mm[0]  # Mind map ID is at index 0
    print(f"Mind map: {mm_id}")

# Delete a mind map
await client.notes.delete_mind_map(nb_id, mind_map_id)

Note: Mind maps are detected by checking if the content contains '"children":' or '"nodes":'` keys, which indicate JSON mind map data structure.


Data Types

Notebook

@dataclass
class Notebook:
    id: str
    title: str
    created_at: Optional[datetime]
    sources_count: int
    is_owner: bool

Source

@dataclass
class Source:
    id: str
    title: Optional[str]
    url: Optional[str]
    source_type: str  # "url", "youtube", "text", "pdf", "upload", etc.
    created_at: Optional[datetime]

Artifact

@dataclass
class Artifact:
    id: str
    title: Optional[str]
    artifact_type: StudioContentType
    status: str  # "in_progress", "completed", "failed"
    url: Optional[str]
    created_at: Optional[datetime]

AskResult

@dataclass
class AskResult:
    answer: str
    conversation_id: str
    sources_used: list[str]

Enums

Audio Generation

class AudioFormat(Enum):
    DEEP_DIVE = 1   # In-depth discussion
    BRIEF = 2       # Quick summary
    CRITIQUE = 3    # Critical analysis
    DEBATE = 4      # Two-sided debate

class AudioLength(Enum):
    SHORT = 1
    DEFAULT = 2
    LONG = 3

Video Generation

class VideoFormat(Enum):
    EXPLAINER = 1
    BRIEF = 2

class VideoStyle(Enum):
    AUTO_SELECT = 1
    CUSTOM = 2
    CLASSIC = 3
    WHITEBOARD = 4
    KAWAII = 5
    ANIME = 6
    WATERCOLOR = 7
    RETRO_PRINT = 8
    HERITAGE = 9
    PAPER_CRAFT = 10

Quiz/Flashcards

class QuizQuantity(Enum):
    FEWER = 1
    STANDARD = 2

class QuizDifficulty(Enum):
    EASY = 1
    MEDIUM = 2
    HARD = 3

Reports

class ReportFormat(Enum):
    BRIEFING_DOC = 1
    STUDY_GUIDE = 2
    BLOG_POST = 3
    CUSTOM = 4

Infographics

class InfographicOrientation(Enum):
    LANDSCAPE = 1
    PORTRAIT = 2
    SQUARE = 3

class InfographicDetail(Enum):
    CONCISE = 1
    STANDARD = 2
    DETAILED = 3

Slide Decks

class SlideDeckFormat(Enum):
    DETAILED_DECK = 1
    PRESENTER_SLIDES = 2

class SlideDeckLength(Enum):
    DEFAULT = 1
    SHORT = 2

Export

class ExportType(Enum):
    DOCS = 1    # Export to Google Docs
    SHEETS = 2  # Export to Google Sheets

Chat Configuration

class ChatGoal(Enum):
    DEFAULT = 1        # General purpose
    CUSTOM = 2         # Uses custom_prompt
    LEARNING_GUIDE = 3 # Educational focus

class ChatResponseLength(Enum):
    DEFAULT = 1
    LONGER = 4
    SHORTER = 5

class ChatMode(Enum):
    """Predefined chat modes for common use cases (service-level enum)."""
    DEFAULT = "default"          # General purpose
    LEARNING_GUIDE = "learning_guide"  # Educational focus
    CONCISE = "concise"          # Brief responses
    DETAILED = "detailed"        # Verbose responses

ChatGoal vs ChatMode:

  • ChatGoal is an RPC-level enum used with client.chat.configure() for low-level API configuration
  • ChatMode is a service-level enum providing predefined configurations for common use cases

Advanced Usage

Custom RPC Calls

For undocumented features, you can make raw RPC calls:

from notebooklm.rpc import RPCMethod

async with await NotebookLMClient.from_storage() as client:
    # Access the core client for raw RPC
    result = await client._core.rpc_call(
        RPCMethod.SOME_METHOD,
        params=[...],
        source_path="/notebook/123"
    )

Handling Rate Limits

Google rate limits aggressive API usage:

import asyncio
from notebooklm import RPCError

async def safe_create_notebooks(client, titles):
    for title in titles:
        try:
            await client.notebooks.create(title)
        except RPCError:
            # Wait and retry on rate limit
            await asyncio.sleep(10)
            await client.notebooks.create(title)
        # Add delay between operations
        await asyncio.sleep(2)

Streaming Chat Responses

The chat endpoint supports streaming (internal implementation):

# Standard (non-streaming) - recommended
result = await client.chat.ask(nb_id, "Question")
print(result.answer)

# Streaming is handled internally by the library
# The ask() method returns the complete response