Skip to content

Latest commit

 

History

History
1440 lines (1128 loc) · 39.1 KB

File metadata and controls

1440 lines (1128 loc) · 39.1 KB

RPC & UI Reference

Status: Active Last Updated: 2026-01-18 Source of Truth: src/notebooklm/rpc/types.py Purpose: Complete reference for RPC methods, UI selectors, and payload structures

Note: Payload structures extracted from actual implementation in src/notebooklm/. Each payload includes a reference to its source file.


Quick Reference

RPC Method Status

RPC ID Method Purpose Implementation
wXbhsf LIST_NOTEBOOKS List all notebooks _notebooks.py
CCqFvf CREATE_NOTEBOOK Create new notebook _notebooks.py
rLM1Ne GET_NOTEBOOK Get notebook details + sources _notebooks.py
s0tc2d RENAME_NOTEBOOK Rename, chat config, share access _notebooks.py, _chat.py
WWINqb DELETE_NOTEBOOK Delete a notebook _notebooks.py
izAoDd ADD_SOURCE Add URL/text/YouTube source _sources.py
o4cbdc ADD_SOURCE_FILE Register uploaded file _sources.py
tGMBJ DELETE_SOURCE Delete a source _sources.py
b7Wfje UPDATE_SOURCE Rename source _sources.py
tr032e GET_SOURCE_GUIDE Get source summary _sources.py
R7cb6c CREATE_VIDEO Unified artifact generation _artifacts.py
gArtLc LIST_ARTIFACTS List/poll artifacts _artifacts.py
V5N4be DELETE_STUDIO Delete studio artifact _artifacts.py
hPTbtc GET_CONVERSATION_HISTORY Get chat history _chat.py
CYK0Xb CREATE_NOTE Create a note (placeholder) _notes.py
cYAfTb UPDATE_NOTE Update note content/title _notes.py
AH0mwd DELETE_NOTE Delete a note _notes.py
cFji9 GET_NOTES_AND_MIND_MAPS List notes and mind maps _notes.py
yyryJe ACT_ON_SOURCES Mind map generation _artifacts.py
VfAZjd SUMMARIZE Get notebook summary _notebooks.py
FLmJqe REFRESH_SOURCE Refresh URL/Drive source _sources.py
yR9Yof CHECK_SOURCE_FRESHNESS Check if source needs refresh _sources.py
Ljjv0c START_FAST_RESEARCH Start fast research _research.py
QA9ei START_DEEP_RESEARCH Start deep research _research.py
e3bVqc POLL_RESEARCH Poll research status _research.py
LBwxtb IMPORT_RESEARCH Import research results _research.py
rc3d8d RENAME_ARTIFACT Rename artifact _artifacts.py
Krh3pd EXPORT_ARTIFACT Export to Docs/Sheets _artifacts.py
RGP97b SHARE_ARTIFACT Toggle notebook sharing _notebooks.py
QDyure SHARE_NOTEBOOK Set notebook visibility (restricted/public) _notebooks.py
JFMDGd GET_SHARE_STATUS Get notebook share settings Not implemented
ciyUvf GET_SUGGESTED_REPORTS Get AI-suggested report formats _artifacts.py
v9rmvd GET_INTERACTIVE_HTML Fetch quiz/flashcard HTML content _artifacts.py
fejl7e REMOVE_RECENTLY_VIEWED Remove notebook from recent list _notebooks.py
ZwVcOc GET_USER_SETTINGS Get user settings including output language _settings.py
hT54vc SET_USER_SETTINGS Set user settings (e.g., output language) _settings.py

Content Type Codes (StudioContentType)

Code Type Used By
1 Audio Audio Overview
2 Report Briefing Doc, Study Guide, Blog Post
3 Video Video Overview
4 Quiz/Flashcards Quiz (variant=2), Flashcards (variant=1)
5 Mind Map Mind Map
7 Infographic Infographic
8 Slide Deck Slide Deck
9 Data Table Data Table

Using Selector Lists

Selectors are provided as Python lists of fallback options. Try each in order:

async def try_selectors(page, selectors: list[str], action="click", timeout=5000):
    """Try multiple selectors until one works."""
    for selector in selectors:
        try:
            element = page.locator(selector)
            if action == "click":
                await element.click(timeout=timeout)
            elif action == "fill":
                return element
            return True
        except Exception:
            continue
    raise Exception(f"None of the selectors worked: {selectors}")

# Example usage
await try_selectors(page, HOME_SELECTORS["create_notebook"])

Home / Notebook List

UI Selectors

HOME_SELECTORS = {
    "create_notebook": [
        "button:has-text('Create new')",
        "mat-card[role='button']:has-text('Create new notebook')",
    ],
    "notebook_card": [
        "mat-card:has(button:has-text('more_vert'))",
        "mat-card[role='button']:has(h3)",
    ],
    "notebook_menu": [
        "button[aria-label*='More options']",
    ],
}

RPC: LIST_NOTEBOOKS (wXbhsf)

Source: _notebooks.py::list()

params = [
    None,   # 0
    1,      # 1: Fixed value
    None,   # 2
    [2],    # 3: Fixed flag
]

RPC: CREATE_NOTEBOOK (CCqFvf)

Source: _notebooks.py::create()

params = [
    title,  # 0: Notebook title
    None,   # 1
    None,   # 2
    [2],    # 3: Fixed flag
    [1],    # 4: Fixed flag
]

RPC: DELETE_NOTEBOOK (WWINqb)

Source: _notebooks.py::delete()

params = [
    [notebook_id],  # 0: Single-nested notebook ID
    [2],            # 1: Fixed flag
]

RPC: GET_NOTEBOOK (rLM1Ne)

Source: _notebooks.py::get()

params = [
    notebook_id,  # 0
    None,         # 1
    [2],          # 2: Fixed flag
    None,         # 3
    0,            # 4: Fixed value
]

RPC: REMOVE_RECENTLY_VIEWED (fejl7e)

Source: _notebooks.py::remove_from_recent()

Remove a notebook from the recently viewed list (doesn't delete the notebook).

params = [notebook_id]  # Just the notebook ID

# No source_path needed
await rpc_call(
    RPCMethod.REMOVE_RECENTLY_VIEWED,
    params,
    allow_null=True,
)

# Response: None (no return value)

Sources Panel

UI Selectors

SOURCES_SELECTORS = {
    "add_sources": [
        "button:has-text('+ Add sources')",
        "button:has-text('Add sources')",
    ],
    "source_card": ".single-source-container",
    "source_menu": "button[aria-label*='More options']",
    "remove_source": "button:has-text('Remove source')",
    "rename_source": "button:has-text('Rename source')",
}

ADD_SOURCE_MODAL = {
    "modal": "[role='dialog']",
    "website_tab": "button:has-text('Website')",
    "url_input": "textarea[placeholder*='links']",
    "copied_text_tab": "button:has-text('Copied text')",
    "submit_button": "button:has-text('Insert')",
}

RPC: ADD_SOURCE (izAoDd) - URL

Source: _sources.py::_add_url_source()

# URL goes at position [2] in an 8-element array
params = [
    [[None, None, [url], None, None, None, None, None]],  # 0: Source config
    notebook_id,                                           # 1: Notebook ID
    [2],                                                   # 2: Source type flag
    None,                                                  # 3
    None,                                                  # 4
]

RPC: ADD_SOURCE (izAoDd) - Text

Source: _sources.py::add_text()

# [title, content] at position [1] in an 8-element array
params = [
    [[None, [title, content], None, None, None, None, None, None]],  # 0
    notebook_id,                                                      # 1
    [2],                                                              # 2
    None,                                                             # 3
    None,                                                             # 4
]

RPC: ADD_SOURCE (izAoDd) - YouTube

Source: _sources.py::_add_youtube_source()

# YouTube URL at position [7] in an 11-element array (different from regular URL!)
params = [
    [[None, None, None, None, None, None, None, [url], None, None, 1]],  # 0
    notebook_id,                                                          # 1
    [2],                                                                  # 2
    [1, None, None, None, None, None, None, None, None, None, [1]],      # 3: Extra config
]

RPC: DELETE_SOURCE (tGMBJ)

Source: _sources.py::delete()

IMPORTANT: notebook_id is passed via source_path, NOT in params!

params = [[[source_id]]]  # Triple-nested!

# Called with:
await rpc_call(
    RPCMethod.DELETE_SOURCE,
    params,
    source_path=f"/notebook/{notebook_id}",  # <-- notebook_id here
)

RPC: UPDATE_SOURCE / Rename (b7Wfje)

Source: _sources.py::rename()

# Different structure: None at [0], source_id at [1], title triple-nested at [2]
params = [
    None,               # 0
    [source_id],        # 1: Single-nested source ID
    [[[new_title]]],    # 2: Triple-nested title
]

RPC: GET_SOURCE_GUIDE (tr032e)

Source: _sources.py::get_guide()

# Quadruple-nested source ID!
params = [[[[source_id]]]]

Chat Panel

UI Selectors

CHAT_SELECTORS = {
    "message_input": [
        "textarea[placeholder='Start typing...']",
        "textarea[aria-label='Query box']",
    ],
    "send_button": "button[aria-label='Submit']",
    "configure_button": "button[aria-label='Configure notebook']",
    "chat_history": "[role='log']",
    "message_bubble": [
        ".to-user-container",      # AI messages
        ".from-user-container",    # User messages
    ],
}

CHAT_CONFIG = {
    "modal": "configure-notebook-settings",
    "goal_default": "button[aria-label='Default button']",
    "goal_learning_guide": "button[aria-label*='Learning Guide']",
    "goal_custom": "button[aria-label='Custom button']",
    "length_shorter": "button[aria-label*='Shorter']",
    "length_longer": "button[aria-label*='Longer']",
    "save_button": "button:has-text('Save')",
}

Query Endpoint (Streaming)

Chat queries use a separate streaming endpoint, not batchexecute:

POST /_/LabsTailwindUi/data/google.internal.labs.tailwind.orchestration.v1.LabsTailwindOrchestrationService/GenerateFreeFormStreamed

RPC: RENAME_NOTEBOOK (s0tc2d) - Rename Only

Source: _notebooks.py::rename()

# Just rename, no chat config
params = [
    notebook_id,                                    # 0
    [[None, None, None, [None, new_title]]],        # 1: Nested title at [[[3][1]]]
]

RPC: RENAME_NOTEBOOK (s0tc2d) - Configure Chat

Source: _chat.py::configure()

# Chat goal codes (ChatGoal enum)
CHAT_GOAL_DEFAULT = 1
CHAT_GOAL_CUSTOM = 2
CHAT_GOAL_LEARNING_GUIDE = 3

# Response length codes (ChatResponseLength enum)
CHAT_LENGTH_DEFAULT = 1
CHAT_LENGTH_LONGER = 4
CHAT_LENGTH_SHORTER = 5

# Build goal array
goal_array = [goal_value]                    # e.g., [1] for DEFAULT
# For CUSTOM: goal_array = [2, custom_prompt]

chat_settings = [goal_array, [response_length_value]]

params = [
    notebook_id,                                              # 0
    [[None, None, None, None, None, None, None, chat_settings]],  # 1: Settings at [[[7]]]
]

RPC: GET_CONVERSATION_HISTORY (hPTbtc)

Source: _chat.py::get_history()

params = [
    [],           # 0: Empty sources array
    None,         # 1
    notebook_id,  # 2
    limit,        # 3: Max conversations (e.g., 20)
]

Studio Panel - Artifact Generation

UI Selectors

STUDIO_SELECTORS = {
    "artifact_button": ".create-artifact-button-container",
    "customize_icon": ".option-icon",  # Click THIS for customization!
    "add_note": "button:has-text('Add note')",
    "artifact_list": ".artifact-library-container",
    "artifact_row": ".artifact-item-button",
    "artifact_menu": ".artifact-more-button",
}

ARTIFACT_MENU = {
    "rename": "button:has-text('Rename')",
    "download": "button:has-text('Download')",
    "delete": "button:has-text('Delete')",
}

Critical: Edit Icon vs Full Button

# ✅ Click edit icon for customization dialog
await page.locator(".create-artifact-button-container:has-text('Audio') .option-icon").click()

# ❌ Clicking full button starts generation with defaults (skips customization!)
await page.locator(".create-artifact-button-container:has-text('Audio')").click()

RPC: CREATE_VIDEO / Unified Artifact (R7cb6c)

All artifact types use R7cb6c with different content type codes and nested configs.

Source: _artifacts.py

Audio Overview (Type 1)

Source: _artifacts.py::generate_audio()

source_ids_triple = [[[sid]] for sid in source_ids]  # [[[s1]], [[s2]], ...]
source_ids_double = [[sid] for sid in source_ids]    # [[s1], [s2], ...]

params = [
    [2],                              # 0: Fixed
    notebook_id,                      # 1
    [
        None,                         # [0]
        None,                         # [1]
        1,                            # [2]: StudioContentType.AUDIO
        source_ids_triple,            # [3]
        None,                         # [4]
        None,                         # [5]
        [
            None,
            [
                instructions,         # Focus/instructions text
                length_code,          # 1=SHORT, 2=DEFAULT, 3=LONG
                None,
                source_ids_double,
                language,             # "en"
                None,
                format_code,          # 1=DEEP_DIVE, 2=BRIEF, 3=CRITIQUE, 4=DEBATE
            ],
        ],                            # [6]
    ],                                # 2: Source config
]

Video Overview (Type 3)

Source: _artifacts.py::generate_video()

params = [
    [2],
    notebook_id,
    [
        None,                         # [0]
        None,                         # [1]
        3,                            # [2]: StudioContentType.VIDEO
        source_ids_triple,            # [3]
        None,                         # [4]
        None,                         # [5]
        None,                         # [6]
        None,                         # [7]
        [
            None,
            None,
            [
                source_ids_double,
                language,             # "en"
                instructions,
                None,
                format_code,          # 1=EXPLAINER, 2=BRIEF
                style_code,           # 1=AUTO, 2=CUSTOM, 3=CLASSIC, 4=WHITEBOARD, etc.
            ],
        ],                            # [8]
    ],
]

Report (Type 2)

Source: _artifacts.py::generate_report()

params = [
    [2],
    notebook_id,
    [
        None,                         # [0]
        None,                         # [1]
        2,                            # [2]: StudioContentType.REPORT
        source_ids_triple,            # [3]
        None,                         # [4]
        None,                         # [5]
        None,                         # [6]
        [
            None,
            [
                title,                # "Briefing Doc" / "Study Guide" / etc.
                description,          # Short description
                None,
                source_ids_double,
                language,             # "en"
                prompt,               # Detailed generation prompt
                None,
                True,
            ],
        ],                            # [7]
    ],
]

Quiz (Type 4, Variant 2)

Source: _artifacts.py::generate_quiz()

params = [
    [2],
    notebook_id,
    [
        None,                         # [0]
        None,                         # [1]
        4,                            # [2]: StudioContentType.QUIZ_FLASHCARD
        source_ids_triple,            # [3]
        None,                         # [4]
        None,                         # [5]
        None,                         # [6]
        None,                         # [7]
        None,                         # [8]
        [
            None,
            [
                2,                    # Variant: 2=quiz, 1=flashcards
                None,
                instructions,
                None,
                None,
                None,
                None,
                [quantity_code, difficulty_code],  # quantity: 1=FEWER, 2=STANDARD
            ],                                     # difficulty: 1=EASY, 2=MEDIUM, 3=HARD
        ],                            # [9]
    ],
]

Flashcards (Type 4, Variant 1)

Source: _artifacts.py::generate_flashcards()

params = [
    [2],
    notebook_id,
    [
        None,                         # [0]
        None,                         # [1]
        4,                            # [2]: StudioContentType.QUIZ_FLASHCARD
        source_ids_triple,            # [3]
        None,                         # [4]
        None,                         # [5]
        None,                         # [6]
        None,                         # [7]
        None,                         # [8]
        [
            None,
            [
                1,                    # Variant: 1=flashcards (vs 2=quiz)
                None,
                instructions,
                None,
                None,
                None,
                [difficulty_code, quantity_code],  # Note: reversed order from quiz!
            ],
        ],                            # [9]
    ],
]

Infographic (Type 7)

Source: _artifacts.py::generate_infographic()

# Orientation: 1=LANDSCAPE, 2=PORTRAIT, 3=SQUARE
# Detail: 1=CONCISE, 2=STANDARD, 3=DETAILED

params = [
    [2],
    notebook_id,
    [
        None,                         # [0]
        None,                         # [1]
        7,                            # [2]: StudioContentType.INFOGRAPHIC
        source_ids_triple,            # [3]
        None, None, None, None, None, None, None, None, None, None,  # [4-13]
        [
            None,
            [instructions, language, None, orientation_code, detail_code],
        ],                            # [14]
    ],
]

Slide Deck (Type 8)

Source: _artifacts.py::generate_slide_deck()

# Format: 1=DETAILED_DECK, 2=PRESENTER_SLIDES
# Length: 1=DEFAULT, 2=SHORT

params = [
    [2],
    notebook_id,
    [
        None,                         # [0]
        None,                         # [1]
        8,                            # [2]: StudioContentType.SLIDE_DECK
        source_ids_triple,            # [3]
        None, None, None, None, None, None, None, None, None, None, None, None,  # [4-15]
        [[instructions, language, format_code, length_code]],  # [16]
    ],
]

Data Table (Type 9)

Source: _artifacts.py::generate_data_table()

params = [
    [2],
    notebook_id,
    [
        None,                         # [0]
        None,                         # [1]
        9,                            # [2]: StudioContentType.DATA_TABLE
        source_ids_triple,            # [3]
        None, None, None, None, None, None, None, None, None, None, None, None, None, None,  # [4-17]
        [None, [instructions, language]],  # [18]
    ],
]

Mind Map (Type 5) - Uses ACT_ON_SOURCES (yyryJe)

Source: _artifacts.py::generate_mind_map()

Note: Mind map uses a different RPC method than other artifacts.

# RPC: ACT_ON_SOURCES (yyryJe), NOT CREATE_VIDEO
params = [
    source_ids_nested,                            # 0: [[[sid]] for sid in source_ids]
    None,                                         # 1
    None,                                         # 2
    None,                                         # 3
    None,                                         # 4
    ["interactive_mindmap", [["[CONTEXT]", ""]], ""],  # 5: Mind map command
    None,                                         # 6
    [2, None, [1]],                               # 7: Fixed config
]

RPC: LIST_ARTIFACTS / POLL_STUDIO (gArtLc)

Source: _artifacts.py::list()

params = [
    [2],
    notebook_id,
    'NOT artifact.status = "ARTIFACT_STATUS_SUGGESTED"',  # Filter string
]

# Response contains artifacts array with status:
# status = 1 → Processing
# status = 2 → Pending
# status = 3 → Completed

Python API Note: artifacts.list() also fetches mind maps from GET_NOTES_AND_MIND_MAPS and includes them as Artifact objects (type=5). This provides a unified list of all AI-generated content. Mind maps with status=2 (deleted) are filtered out.


Notes

RPC: CREATE_NOTE (CYK0Xb)

Source: _notes.py::create()

Note: Google ignores title/content in CREATE_NOTE. Must call UPDATE_NOTE after to set actual content.

# Creates note with fixed placeholder values
params = [
    notebook_id,   # 0
    "",            # 1: Empty string (ignored)
    [1],           # 2: Fixed flag
    None,          # 3
    "New Note",    # 4: Placeholder title (ignored)
]
# Then call UPDATE_NOTE to set real title/content

RPC: UPDATE_NOTE (cYAfTb)

Source: _notes.py::update()

params = [
    notebook_id,                       # 0
    note_id,                           # 1
    [[[content, title, [], 0]]],       # 2: Triple-nested [content, title, [], 0]
]

RPC: DELETE_NOTE (AH0mwd)

Source: _notes.py::delete()

Important: This is a soft delete - it clears note content but does NOT remove the note from the list. The note remains with None content and a status flag of 2.

params = [
    notebook_id,   # 0
    None,          # 1
    [note_id],     # 2: Single-nested note ID
]

# BEFORE delete:
# ['note_id', ['note_id', 'content', [metadata], None, 'title']]

# AFTER delete:
# ['note_id', None, 2]  # Status 2 = deleted/cleared

Note: Same behavior applies to mind maps via delete_mind_map(). The Python API filters out items with status=2 in list() and list_mind_maps() to match UI behavior.

RPC: GET_NOTES_AND_MIND_MAPS (cFji9)

Source: _notes.py::_get_all_notes_and_mind_maps()

params = [notebook_id]

Note/Mind Map Data Structures

Notes and mind maps share the same storage system and are distinguished by content format.

Active Note Structure

[
    "note_id",           # Position 0: Note ID
    [
        "note_id",       # [1][0]: ID (duplicate)
        "content",       # [1][1]: Note content text
        [                # [1][2]: Metadata
            1,           # Type flag
            "user_id",   # User ID
            [ts, ns]     # [timestamp_seconds, nanoseconds]
        ],
        None,            # [1][3]: Unknown
        "title"          # [1][4]: Note title
    ]
]

Active Mind Map Structure

[
    "mind_map_id",       # Position 0: Mind map ID
    [
        "mind_map_id",   # [1][0]: ID (duplicate)
        '{"name": "Root", "children": [...]}',  # [1][1]: JSON with children/nodes
        [metadata],      # [1][2]: Same as notes
        None,            # [1][3]: Unknown
        "Mind Map Title" # [1][4]: Title
    ]
]

Deleted Item Structure (Status = 2)

["id", None, 2]  # Content cleared, status=2 indicates soft-deleted

The Python API:

  • notes.list() - Returns only active notes (excludes mind maps and status=2)
  • notes.list_mind_maps() - Returns only active mind maps (excludes status=2)
  • artifacts.list() - Includes mind maps as Artifact objects (excludes status=2)

Source ID Nesting Patterns

CRITICAL: Source IDs require different nesting levels depending on the method.

Pattern Structure Used By
Single [source_id] UPDATE_SOURCE position [1]
Double [[source_id]] Artifact source_ids_double
Triple [[[source_id]]] DELETE_SOURCE, Artifact source_ids_triple
Quadruple [[[[source_id]]]] GET_SOURCE_GUIDE
Array of Double [[s1], [s2], ...] Artifact generation
Array of Triple [[[s1]], [[s2]], ...] Artifact generation

Building nesting in Python:

source_ids = ["source_1", "source_2", "source_3"]

# Single: [source_id]
single = [source_ids[0]]

# Double: [[source_id]]
double = [[source_ids[0]]]

# Triple: [[[source_id]]]
triple = [[[source_ids[0]]]]

# Array of Double for artifacts
source_ids_double = [[sid] for sid in source_ids]
# Result: [["source_1"], ["source_2"], ["source_3"]]

# Array of Triple for artifacts
source_ids_triple = [[[sid]] for sid in source_ids]
# Result: [[["source_1"]], [["source_2"]], [["source_3"]]]

Notebook Summary & Sharing

RPC: SUMMARIZE (VfAZjd)

Source: _notebooks.py::get_summary(), _notebooks.py::get_description()

Gets AI-generated summary and suggested topics for a notebook.

params = [
    notebook_id,  # 0: Notebook ID
    [2],          # 1: Fixed flag
]

# Called with source_path:
await rpc_call(
    RPCMethod.SUMMARIZE,
    params,
    source_path=f"/notebook/{notebook_id}",
)

# Response structure:
# [
#     [summary_text],           # [0][0]: Summary string
#     [[                        # [1][0]: Suggested topics array
#         [question, prompt],   # Each topic has question and prompt
#         ...
#     ]],
# ]

RPC: GET_SHARE_STATUS (JFMDGd)

Source: Not yet implemented

Get the current share settings for a notebook.

params = [
    notebook_id,  # 0: Notebook ID
    [2],          # 1: Fixed flag
]

# Response: Share status information

RPC: SHARE_NOTEBOOK (QDyure)

Source: _notebooks.py::share()

Set notebook visibility (restricted vs anyone with link).

# visibility: [0] = restricted, [1] = anyone with link
# access: [0, ""] = chat only?, [1, ""] = full notebook?

params = [
    [
        [
            notebook_id,  # Notebook ID
            None,
            visibility,   # [0]=restricted, [1]=public link
            access,       # [0, ""] or [1, ""] - access level
        ]
    ],
    1,            # Fixed value
    None,
    [2],          # Fixed flag
]

RPC: SET_SHARE_ACCESS (via RENAME_NOTEBOOK s0tc2d)

Source: Not yet implemented (same RPC ID as RENAME_NOTEBOOK)

Set viewer access level (chat only vs full notebook).

Note: This uses the same RPC ID as RENAME_NOTEBOOK (s0tc2d) but with different parameter structure.

# The access toggle is at index [8] in a deeply nested array
params = [
    notebook_id,  # 0: Notebook ID
    [
        [
            None, None, None, None,   # indices 0-3
            None, None, None, None,   # indices 4-7
            [[access_level]],         # index 8: [[1]] for chat only?
        ]
    ],
]

Notebook Sharing Overview

Sharing is a notebook-level setting. When you share a notebook, ALL artifacts become accessible.

Notebooks have two independent sharing toggles:

  1. Visibility (SHARE_NOTEBOOK - QDyure or SHARE_ARTIFACT - RGP97b):

    • [0] = Restricted (only specific people)
    • [1] = Anyone with the link
  2. Access Level (SET_SHARE_ACCESS - s0tc2d):

    • [[1]] = Chat only (viewers can only use chat)
    • Other = Full notebook access

Share URLs:

  • Notebook: https://notebooklm.google.com/notebook/{notebook_id}
  • Artifact deep-link: https://notebooklm.google.com/notebook/{notebook_id}?artifactId={artifact_id}

The ?artifactId=xxx parameter creates a deep link that opens the notebook and navigates to that specific artifact. Mind Maps cannot be shared (no public URLs).


Source Refresh Operations

RPC: REFRESH_SOURCE (FLmJqe)

Source: _sources.py::refresh()

Refresh a source to get updated content (for URL/Drive sources).

params = [
    None,           # 0
    [source_id],    # 1: Single-nested source ID
    [2],            # 2: Fixed flag
]

# Called with source_path:
await rpc_call(
    RPCMethod.REFRESH_SOURCE,
    params,
    source_path=f"/notebook/{notebook_id}",
)

RPC: CHECK_SOURCE_FRESHNESS (yR9Yof)

Source: _sources.py::check_freshness()

Check if a source needs to be refreshed.

params = [
    None,           # 0
    [source_id],    # 1: Single-nested source ID
    [2],            # 2: Fixed flag
]

# Called with source_path:
await rpc_call(
    RPCMethod.CHECK_SOURCE_FRESHNESS,
    params,
    source_path=f"/notebook/{notebook_id}",
)

# Response: True = fresh, False = stale (needs refresh)

Research Operations

Research allows searching the web or Google Drive for sources to add to notebooks.

Source Type Codes

Code Source
1 Web
2 Google Drive

RPC: START_FAST_RESEARCH (Ljjv0c)

Source: _research.py::start() with mode="fast"

Start a fast research session.

# source_type: 1=Web, 2=Drive
params = [
    [query, source_type],  # 0: Query and source type
    None,                   # 1
    1,                      # 2: Fixed value
    notebook_id,            # 3: Notebook ID
]

# Called with source_path:
await rpc_call(
    RPCMethod.START_FAST_RESEARCH,
    params,
    source_path=f"/notebook/{notebook_id}",
)

# Response: [task_id, report_id, ...]

RPC: START_DEEP_RESEARCH (QA9ei)

Source: _research.py::start() with mode="deep"

Start a deep research session (web only, more thorough).

# Deep research only supports Web (source_type=1)
params = [
    None,                   # 0
    [1],                    # 1: Fixed flag
    [query, source_type],   # 2: Query and source type
    5,                      # 3: Fixed value
    notebook_id,            # 4: Notebook ID
]

# Called with source_path:
await rpc_call(
    RPCMethod.START_DEEP_RESEARCH,
    params,
    source_path=f"/notebook/{notebook_id}",
)

# Response: [task_id, report_id, ...]

RPC: POLL_RESEARCH (e3bVqc)

Source: _research.py::poll()

Poll for research results.

params = [
    None,          # 0
    None,          # 1
    notebook_id,   # 2: Notebook ID
]

# Called with source_path:
await rpc_call(
    RPCMethod.POLL_RESEARCH,
    params,
    source_path=f"/notebook/{notebook_id}",
)

# Response structure (per task):
# [
#     [task_id, [
#         ...,
#         query_info,           # [1]: [query_text, ...]
#         ...,
#         sources_and_summary,  # [3]: [[sources], summary_text]
#         status_code,          # [4]: 2=completed, other=in_progress
#     ]],
#     ...
# ]

RPC: IMPORT_RESEARCH (LBwxtb)

Source: _research.py::import_sources()

Import selected research sources into the notebook.

# Build source array from selected sources
source_array = []
for src in sources:
    source_data = [
        None,           # 0
        None,           # 1
        [url, title],   # 2: URL and title
        None,           # 3
        None,           # 4
        None,           # 5
        None,           # 6
        None,           # 7
        None,           # 8
        None,           # 9
        2,              # 10: Fixed value
    ]
    source_array.append(source_data)

params = [
    None,           # 0
    [1],            # 1: Fixed flag
    task_id,        # 2: Research task ID
    notebook_id,    # 3: Notebook ID
    source_array,   # 4: Array of sources to import
]

# Called with source_path:
await rpc_call(
    RPCMethod.IMPORT_RESEARCH,
    params,
    source_path=f"/notebook/{notebook_id}",
)

# Response: Imported sources with IDs

User Settings

Global user settings that affect all notebooks in an account.

RPC: GET_USER_SETTINGS (ZwVcOc)

Source: _settings.py::get_output_language()

Get user settings including the current output language.

params = [
    None,                                                    # 0
    [1, None, None, None, None, None, None, None, None, None, [1]],  # 1: Fixed config
]

# Called with root source_path:
await rpc_call(
    RPCMethod.GET_USER_SETTINGS,
    params,
    source_path="/",  # Global setting uses root path
)

# Response structure:
# [[
#     null,
#     [6, 500, 300, 500000],        # [0][1]: Limits/quotas
#     [true, null, null, true, ["ja"]],  # [0][2]: Settings (language at [4][0])
#     [[1]],                         # [0][3]: Unknown
#     [true, 1, 3, 2]               # [0][4]: Feature flags
# ]]
#
# Language code at: result[0][2][4][0]

RPC: SET_USER_SETTINGS (hT54vc)

Source: _settings.py::set_output_language()

Set user settings (currently used for output language).

Important: This is a GLOBAL setting that affects all notebooks in the account.

# Language code goes in a triple-nested structure
params = [
    [[None, [[None, None, None, None, [language]]]]],  # 0: Nested language config
]

# Called with root source_path:
await rpc_call(
    RPCMethod.SET_USER_SETTINGS,
    params,
    source_path="/",  # Global setting uses root path
)

# Response structure:
# [
#     null,
#     [6, 500, 300, 500000],              # [1]: Limits
#     [true, null, null, true, ["ja"]],   # [2]: Updated settings (language at [4][0])
#     ...
# ]
#
# Language code at: result[2][4][0]

Supported Languages:

Common language codes include:

  • en (English), ja (日本語), zh_Hans (中文简体), zh_Hant (中文繁體)
  • ko (한국어), es (Español), fr (Français), de (Deutsch), pt_BR (Português)
  • See cli/language.py::SUPPORTED_LANGUAGES for the full list of 80+ languages

Artifact Management

RPC: RENAME_ARTIFACT (rc3d8d)

Source: _artifacts.py::rename()

Rename an artifact.

params = [
    [artifact_id, new_title],  # 0: Artifact ID and new title
    [["title"]],               # 1: Field mask (update title)
]

# Called with source_path:
await rpc_call(
    RPCMethod.RENAME_ARTIFACT,
    params,
    source_path=f"/notebook/{notebook_id}",
)

RPC: EXPORT_ARTIFACT (Krh3pd)

Source: _artifacts.py::export_report(), _artifacts.py::export_data_table(), _artifacts.py::export()

Export an artifact to Google Docs or Sheets.

# Export types:
# 1 = Google Docs
# 2 = Google Sheets

params = [
    None,          # 0
    artifact_id,   # 1: Artifact ID
    content,       # 2: Content to export (optional, can be None)
    title,         # 3: Title for exported document
    export_type,   # 4: 1=Docs, 2=Sheets
]

# Called with source_path:
await rpc_call(
    RPCMethod.EXPORT_ARTIFACT,
    params,
    source_path=f"/notebook/{notebook_id}",
)

# Response: Export result with document URL

RPC: SHARE_ARTIFACT (RGP97b)

Source: _notebooks.py::share()

Toggle notebook sharing. Sharing is a notebook-level setting - when enabled, ALL artifacts in the notebook become accessible via their URLs.

Note: Mind Maps are NOT shareable (they don't have public URLs).

# share_options: [1] for public, [0] for private
# artifact_id is optional - used to generate a deep-link URL to that specific artifact
params = [
    share_options,  # 0: [1] for public link, [0] for private
    notebook_id,    # 1: Notebook ID
    artifact_id,    # 2: Optional - artifact ID for deep-link URL
]

# Called with source_path:
await rpc_call(
    RPCMethod.SHARE_ARTIFACT,
    params,
    source_path=f"/notebook/{notebook_id}",
)

# Share URL format:
# - Notebook: https://notebooklm.google.com/notebook/{notebook_id}
# - Artifact deep-link: https://notebooklm.google.com/notebook/{notebook_id}?artifactId={artifact_id}

Important: The ?artifactId=xxx URL is a deep link - it opens the shared notebook and navigates to that artifact. The artifact itself isn't independently shared.

RPC: GET_INTERACTIVE_HTML (v9rmvd)

Source: _artifacts.py::_get_artifact_content()

Fetch HTML content for quiz or flashcard artifacts. Used for downloading these artifact types in various formats.

params = [artifact_id]  # Just the artifact ID

# Called with source_path:
await rpc_call(
    RPCMethod.GET_INTERACTIVE_HTML,
    params,
    source_path=f"/notebook/{notebook_id}",
)

# Response structure:
# [[
#     ...,                    # indices 0-8: metadata
#     [html_content],         # index 9: HTML content array
#     ...
# ]]
#
# HTML content contains quiz questions or flashcard data
# that can be parsed into JSON, Markdown, or kept as HTML.

RPC: GET_SUGGESTED_REPORTS (ciyUvf)

Source: _artifacts.py::suggest_reports() (currently uses ACT_ON_SOURCES alternative)

Get AI-suggested report formats based on notebook content.

params = [
    [2],            # 0: Fixed flag (same pattern as LIST_ARTIFACTS)
    notebook_id,    # 1: Notebook ID
]

# Called with source_path:
await rpc_call(
    RPCMethod.GET_SUGGESTED_REPORTS,
    params,
    source_path=f"/notebook/{notebook_id}",
)

# Response structure:
# [[
#     [title, description, None, None, prompt, audience_level],
#     ...
# ]]
#
# Example response item:
# ["Research Paper", "An academic paper analyzing...", None, None,
#  "Write a research paper for an academic audience...", 2]
#
# audience_level: 1=Beginner, 2=Intermediate, 3=Advanced

Note: The current implementation uses ACT_ON_SOURCES with "suggested_report_formats" command as an alternative approach. This dedicated RPC method provides the same functionality.


Operation Timing Categories

Quick Operations

Most operations complete nearly instantly:

  • Notebook operations: list, create, rename, delete
  • Source metadata: list, rename, delete
  • Note operations: create, update, delete
  • Chat configuration
  • Artifact listing

Processing Operations

These require backend processing - wait for completion:

  • Add source (URL): Network fetch + text extraction
  • Add source (file): Upload + parsing
  • Add source (YouTube): Transcript extraction
  • Mind Map generation: Usually faster than other generation types

Generation Operations

AI-generated content takes significant time:

  • Audio Overview: Several minutes
  • Video Overview: Several minutes (longer than audio)
  • Reports/Study Guides: 1-2 minutes
  • Quiz/Flashcards: 1-2 minutes
  • Infographic/Slide Deck/Data Table: 1-2 minutes

Long-Running Operations

Some operations can run much longer:

  • Deep Research: Can take many minutes depending on query complexity

Implementation Note

When automating, poll for completion rather than using fixed timeouts. Check artifact status or source processing state periodically.


Legacy/Unused RPC Methods

These RPC method IDs exist in rpc/types.py but are either legacy (superseded by other methods) or not currently used in the implementation. Documented here for completeness.

RPC ID Method Status Notes
hizoJc GET_SOURCE Broken Code comments indicate this doesn't work; get() uses GET_NOTEBOOK instead
qXyaNe DISCOVER_SOURCES Unused Purpose unknown, not called in implementation
AHyHrd CREATE_AUDIO Legacy Superseded by unified CREATE_VIDEO (R7cb6c)
VUsiyb GET_AUDIO Legacy Superseded by LIST_ARTIFACTS (gArtLc)
sJDbic DELETE_AUDIO Legacy Superseded by DELETE_STUDIO (V5N4be)
xpWGLf CREATE_ARTIFACT Unused Purpose unknown, CREATE_VIDEO handles all artifact types
BnLyuf GET_ARTIFACT Unused Purpose unknown, LIST_ARTIFACTS provides artifact data
LfTXoe LIST_ARTIFACTS_ALT Unused Alternative list method, main LIST_ARTIFACTS (gArtLc) is used

Why keep these? These IDs are preserved in the codebase in case:

  1. Google re-enables or changes their functionality
  2. Future reverse-engineering reveals their purpose
  3. They become useful for specific edge cases

Note: The unified CREATE_VIDEO (R7cb6c) method handles all artifact generation (audio, video, reports, quizzes, etc.) despite its name. The legacy audio-specific methods were likely from an earlier API version.