How the batchexecute RPC Protocol Works with Obfuscated Method IDs in notebooklm-py

The batchexecute RPC protocol uses short obfuscated strings like "wXbhsf" as method identifiers, encoding them into triple-nested JSON arrays and URL parameters to invoke Google Notebook LM's private API endpoints.

The batchexecute RPC protocol powers Google Notebook LM's undocumented API, relying on cryptic method IDs to route requests. In the open-source notebooklm-py client, these obfuscated identifiers are centrally managed and processed through a rigorous encode-decode pipeline. Understanding this flow reveals how the client translates high-level Python calls into valid batchexecute network requests.

How Obfuscated Method IDs Are Defined and Stored

All valid RPC operations are catalogued in the RPCMethod enum located in src/notebooklm/rpc/types.py. This centralized registry maps human-readable operation names to their corresponding obfuscated identifiers.

Centralized Method Registry

The RPCMethod class functions as the single source of truth for method IDs. For example, the operation to list notebooks uses the identifier "wXbhsf", while other operations use similarly random-looking strings. When Google updates the protocol, developers only need to update the string value in this enum to restore functionality.

Encoding Requests for the batchexecute Endpoint

Once a method is selected, the client must format the request payload according to Google's specific expectations. The encode_rpc_request function in src/notebooklm/rpc/encoder.py handles this transformation.

The Triple-Nested Array Structure

The batchexecute protocol requires parameters wrapped in a specific JSON structure: [[[method_id, json_params, null, "generic"]]]. The encoder JSON-serializes the parameters without whitespace, then embeds them alongside the method ID, a null placeholder, and the string "generic" to indicate the response format.

Building the Request Body

The build_request_body function constructs the final form-encoded payload. It creates the f.req= field containing the encoded RPC array, optionally appends the at= CSRF token for authenticated requests, and ensures proper URL encoding for transmission via HTTP POST.

Constructing the HTTP Request

With the payload ready, the client assembles the full HTTP request in src/notebooklm/_core.py.

The _build_url method constructs the batchexecute endpoint URL with specific query parameters:

  • rpcids=<method_id> identifies which RPC to execute
  • rt=c forces chunked transfer encoding for the response
  • source-path and session cookies maintain authentication context

The rpc_call method then executes the POST request, attaching the Cookie header containing authentication tokens and handling network-level errors through custom exception mapping.

Decoding the batchexecute Response

Response handling occurs in src/notebooklm/rpc/decoder.py, where raw HTTP responses are transformed back into usable Python data structures.

Stripping Security Prefixes

Google prefixes responses with )]}' to prevent cross-site script inclusion (XSSI) attacks. The strip_anti_xssi function removes this prefix before parsing begins.

Parsing Chunked Transfer Encoding

When rt=c is specified, responses arrive in an alternating format of byte-count lines and JSON payloads. The parse_chunked_response function walks through these lines, decodes each JSON chunk, and validates that fewer than 10% of chunks are malformed—exceeding this threshold signals a potential API break.

Extracting Results by Method ID

The extract_rpc_result function searches through parsed chunks for wrb.fr entries matching the requested method ID. It handles special cases like embedded UserDisplayableError objects, which trigger RateLimitError exceptions, and returns the decoded JSON result when found.

Handling Stale Method IDs

When Google rotates method identifiers, the decoder provides clear diagnostic feedback. If extract_rpc_result cannot locate the expected ID, decode_response raises an RPCError containing the specific ID sought and a complete list of IDs found in the response.

This explicit error message enables rapid identification of protocol changes. Developers can compare the missing ID against the RPCMethod enum, update the string value in src/notebooklm/rpc/types.py, and restore functionality without reverse-engineering the entire API.

Practical Implementation Example

The following example demonstrates the complete flow from method selection to result extraction:

import asyncio
from notebooklm.client import NotebookLMClient
from notebooklm.rpc.types import RPCMethod

async def list_notebooks():
    # Initialise client (tokens are loaded from stored Chrome cookies)

    async with await NotebookLMClient.from_storage() as client:
        # The RPC ID for "list notebooks" is hidden inside RPCMethod

        notebooks = await client.notebooks.list()
        for nb in notebooks:
            print(f"{nb.id}: {nb.title}")

# Run the example

asyncio.run(list_notebooks())

Under the hood, client.notebooks.list() invokes ClientCore.rpc_call with RPCMethod.LIST_NOTEBOOKS, triggering the encoding, URL construction, HTTP POST, and response decoding pipeline described above.

Summary

  • Obfuscated method IDs like "wXbhsf" are centrally defined in src/notebooklm/rpc/types.py via the RPCMethod enum.
  • Request encoding wraps parameters into a triple-nested array [[[method_id, json_params, null, "generic"]]] via encode_rpc_request.
  • URL construction injects the method ID into the rpcids query parameter and forces chunked responses with rt=c.
  • Response decoding strips the anti-XSSI prefix, parses alternating byte-count and JSON chunks, and extracts results by matching the wrb.fr RPC ID.
  • Error handling provides explicit diagnostics when method IDs change, listing all IDs found in the response to facilitate rapid updates.

Frequently Asked Questions

What is the batchexecute RPC protocol?

The batchexecute RPC protocol is Google's internal mechanism for handling multiple remote procedure calls through a single HTTP endpoint. It uses obfuscated string identifiers to route requests to specific backend services, encodes parameters in a specific triple-nested JSON structure, and returns responses in a chunked format prefixed with an anti-XSSI string. The notebooklm-py client reverse-engineers this protocol to provide programmatic access to Google Notebook LM's private API.

Why does notebooklm-py use obfuscated method IDs?

Google obfuscates method IDs to prevent unauthorized API usage and allow internal refactoring without breaking external contracts. The notebooklm-py client captures these identifiers in the RPCMethod enum to provide a stable, human-readable interface for developers. When Google rotates the underlying strings, developers simply update the enum values in src/notebooklm/rpc/types.py rather than rewriting application logic.

How does the client handle API changes or updated method IDs?

When a method ID becomes stale, the extract_rpc_result function in src/notebooklm/rpc/decoder.py fails to locate the expected identifier in the response chunks. The client raises an RPCError that explicitly lists the requested ID and all IDs actually found in the response. This clear diagnostic enables developers to identify the protocol change, inspect the new method ID through browser dev tools, and update the RPCMethod enum to restore functionality.

What security measures does the batchexecute protocol use?

The protocol implements several security layers: responses begin with the )]}' prefix to prevent cross-site script inclusion (XSSI) attacks, requiring clients to strip this before parsing. Requests require valid authentication cookies and optional CSRF tokens (at parameter) to prevent unauthorized access. The chunked transfer encoding (rt=c) complicates simple scraping attempts, while the obfuscated method IDs prevent easy discovery of API endpoints through static analysis.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →