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 executert=cforces chunked transfer encoding for the responsesource-pathand 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 insrc/notebooklm/rpc/types.pyvia theRPCMethodenum. - Request encoding wraps parameters into a triple-nested array
[[[method_id, json_params, null, "generic"]]]viaencode_rpc_request. - URL construction injects the method ID into the
rpcidsquery parameter and forces chunked responses withrt=c. - Response decoding strips the anti-XSSI prefix, parses alternating byte-count and JSON chunks, and extracts results by matching the
wrb.frRPC 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →