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

> Discover how the batchexecute RPC protocol uses short obfuscated method IDs in notebooklm-py to access private API endpoints through triple-nested JSON and URL parameters.

- Repository: [Teng Lin/notebooklm-py](https://github.com/teng-lin/notebooklm-py)
- Tags: deep-dive
- Published: 2026-03-09

---

**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`](https://github.com/teng-lin/notebooklm-py/blob/main/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`](https://github.com/teng-lin/notebooklm-py/blob/main/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`](https://github.com/teng-lin/notebooklm-py/blob/main/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`](https://github.com/teng-lin/notebooklm-py/blob/main/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`](https://github.com/teng-lin/notebooklm-py/blob/main/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:

```python
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`](https://github.com/teng-lin/notebooklm-py/blob/main/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`](https://github.com/teng-lin/notebooklm-py/blob/main/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`](https://github.com/teng-lin/notebooklm-py/blob/main/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.