Risk Factors When Using Undocumented Google RPC APIs: A Technical Analysis of notebooklm-py

Using undocumented Google RPC APIs exposes applications to method-ID volatility, authentication churn, and account sanctions because these internal endpoints lack versioning, stability guarantees, or official deprecation notices.

The notebooklm-py repository by teng-lin demonstrates exactly how precarious these integrations can be. The client communicates with Google’s NotebookLM service through reverse-engineered batchexecute RPC methods defined in src/notebooklm/rpc/types.py, bypassing any official API surface. Understanding the specific risk factors when using undocumented Google RPC APIs is essential before deploying such code in production environments.

Method-ID Volatility and Breaking Changes

The most immediate threat stems from method-ID volatility. Google’s internal RPC system uses obfuscated integer identifiers to route requests to specific handlers. In src/notebooklm/rpc/types.py, the RPCMethod enum catalogs these reverse-engineered IDs with an explicit warning: “Reverse-engineered from network traffic analysis”【/src/notebooklm/rpc/types.py†L11-L21】.

When Google modifies its internal routing table—something that can happen without warning—these IDs shift. A call that previously succeeded returns HTTP 404 or malformed payloads. Because the wrapper methods in _notebooks.py::list() or _sources.py::add_url() hardcode these IDs, every dependent function fails simultaneously. Unlike public APIs with deprecation headers and migration guides, these changes offer zero notice.

Parameter-Nesting Fragility in batchexecute Payloads

Undocumented RPCs rely on highly specific parameter nesting that mirrors internal UI state rather than stable data contracts. The batchexecute endpoint expects payloads with nesting levels ranging from single to quadruple brackets depending on the operation.

According to docs/rpc-reference.md, deleting a source requires [[[source_id]]] while listing artifacts uses [[source_id]]【/docs/rpc-reference.md†L70-L82】. A minor adjustment to Google’s frontend payload structure—adding or removing a wrapper array—causes immediate TypeError exceptions or silent data loss. Without access to Google’s internal schema definitions, developers must rely on brittle manual testing against live traffic.

Authentication and Security Risks

The authentication model for these internal endpoints introduces token churn and CSRF exposure that public APIs typically avoid. The client in src/notebooklm/auth.py relies on the same session cookies and CSRF tokens used by the web UI rather than OAuth2 access tokens with defined expiration behaviors【/src/notebooklm/auth.py】.

These tokens expire unpredictably, causing 401/403 errors that require manual intervention via refresh_auth(). Furthermore, src/notebooklm/_core.py constructs the X-CSRF-Token header from stored credentials【/src/notebooklm/_core.py†L78-L85】. Any accidental logging of HTTP headers or token leakage creates immediate request forgery vulnerabilities, as these tokens lack the scoped permissions and rotation mechanisms of official API credentials.

Operational Risks: Rate Limits and Lack of Versioning

Operating against undocumented endpoints means navigating opaque rate limiting without the safety rails of official quota management. Google enforces per-user and per-project quotas on the batchexecute endpoint, but undocumented calls do not return standard 429 error codes with Retry-After headers.

As noted in docs/stability.md, developers must manually implement “delays between bulk operations” to avoid triggering back-off mechanisms【/docs/stability.md†L31-L38】. Without published rate limits, automated retry logic risks compounding the problem through aggressive polling. Additionally, the absence of versioning means no migration path exists when Google deprecates functionality; the CLAUDE.md guide explicitly warns that “All RPC method IDs … are undocumented and subject to breakage”【/CLAUDE.md†L18-L22】.

Mitigation Strategies for Production Use

While the risks are inherent, defensive coding practices can reduce exposure to these undocumented Google RPC API vulnerabilities.

Implementing Resilient Authentication Handling

Wrap all RPC calls with automatic token refresh logic to handle authentication churn gracefully:

import asyncio
from notebooklm import NotebookLMClient
from notebooklm.rpc.types import RPCMethod
from notebooklm.exceptions import RPCError

async def list_notebooks():
    async with await NotebookLMClient.from_storage() as client:
        # Simple wrapper that retries once on auth error

        try:
            return await client.notebooks.list()
        except RPCError as exc:
            if exc.status_code == 401:          # token expired

                await client.refresh_auth()
                return await client.notebooks.list()
            raise

if __name__ == "__main__":
    print(asyncio.run(list_notebooks()))

This pattern catches the most common failure mode—expired tokens—and re-authenticates without crashing the application.

Detecting Method-ID Breakage

Implement explicit error handling to surface when Google changes RPC method identifiers:

from notebooklm import NotebookLMClient
from notebooklm.rpc.types import RPCMethod
from notebooklm.exceptions import RPCError

async def safe_get_notebook(nb_id: str):
    async with await NotebookLMClient.from_storage() as client:
        try:
            return await client.notebooks.get(nb_id)
        except RPCError as e:
            # 404 can indicate a changed method ID

            if e.status_code == 404:
                raise RuntimeError(
                    f"RPC method {RPCMethod.GET_NOTEBOOK.value} may have changed. "
                    "Refresh the repository or check docs/rpc-reference.md."
                )
            raise

# usage

# asyncio.run(safe_get_notebook("abc123"))

This surfaces clear diagnostic information when undocumented endpoints shift, prompting immediate maintenance rather than silent failures.

Respecting Rate Limits in Bulk Operations

Add deliberate delays and exponential back-off to avoid triggering undocumented quota enforcement:

import asyncio
from notebooklm import NotebookLMClient
from notebooklm.exceptions import RPCError

async def bulk_add_urls(nb_id: str, urls: list[str], delay: float = 1.0):
    async with await NotebookLMClient.from_storage() as client:
        for url in urls:
            try:
                await client.sources.add_url(nb_id, url)
                await asyncio.sleep(delay)   # simple back-off

            except RPCError as e:
                if e.status_code == 429:
                    # exponential back-off on quota hit

                    await asyncio.sleep(delay * 2)
                    await client.sources.add_url(nb_id, url)

# asyncio.run(bulk_add_urls("nb123", ["https://example.com/a", "..."]))

This manual rate-limiting compensates for the lack of documented quota headers, reducing the risk of account throttling or suspension.

Summary

Integrating with undocumented Google RPC APIs, as demonstrated by the notebooklm-py client, requires accepting significant operational liabilities:

  • Method-ID volatility in src/notebooklm/rpc/types.py means endpoints can disappear without notice, causing immediate 404 errors across all dependent functions.
  • Parameter-nesting fragility demands exact array depths (single through quadruple nesting) that break when Google adjusts internal UI payloads.
  • Authentication churn via session cookies and CSRF tokens in src/notebooklm/auth.py creates unpredictable 401/403 failures requiring manual refresh cycles.
  • Opaque rate limiting without standard retry headers necessitates defensive delays to avoid account throttling or suspension.
  • Zero versioning guarantees mean no migration paths exist when functionality changes, as warned in CLAUDE.md and docs/rpc-reference.md.

Frequently Asked Questions

What makes undocumented Google RPC APIs different from official REST APIs?

Official Google REST APIs provide versioned endpoints, documented schemas, deprecation notices, and standard error codes like 429 for rate limiting. Undocumented RPC APIs, such as those used in notebooklm-py, rely on reverse-engineered method IDs stored in src/notebooklm/rpc/types.py and lack any stability guarantees, versioning, or migration paths, making them susceptible to sudden breaking changes.

How can I detect when Google changes an RPC method ID?

Monitor for 404 errors on calls that previously succeeded, as these often indicate the obfuscated method identifier has shifted. Implement explicit error handling that checks RPCError.status_code and compares against the current RPCMethod enum values from the repository. When a mismatch occurs, raise a diagnostic error prompting users to update src/notebooklm/rpc/types.py from the latest repository version.

Why do parameter nesting levels matter in batchexecute calls?

The batchexecute endpoint expects parameters wrapped in specific array depths—ranging from single brackets [] to quadruple brackets [[[[]]]]—depending on the operation type. These nesting patterns mirror internal Google UI state rather than logical data structures. If Google adjusts their frontend payload format, hardcoded nesting in methods like client.sources.add_url() or client.notebooks.delete() will cause TypeError exceptions or silent data corruption.

What are the consequences of hitting rate limits on undocumented endpoints?

Unlike official APIs that return 429 status codes with Retry-After headers, undocumented RPC endpoints may throttle requests silently or return generic errors. Repeated polling without exponential back-off can trigger account-level sanctions, including temporary throttling or permanent suspension. The notebooklm-py documentation in docs/stability.md recommends implementing manual delays between bulk operations to mitigate this risk.

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 →