# How to Handle CSRF Token Expiration and Refresh Authentication in notebooklm-py

> Learn to handle CSRF token expiration and refresh authentication in notebooklm-py. The library automatically re-fetches tokens and updates headers for seamless recovery.

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

---

**NotebookLM-py automatically detects expired CSRF tokens and session IDs, then refreshes them by re-fetching the homepage and updating the HTTP client headers, with built-in retry logic for seamless authentication recovery.**

When working with the unofficial notebooklm-py client, handling authentication state is critical for maintaining uninterrupted API access. The library manages two essential tokens—a CSRF token (`SNlM0e`) and a session ID (`FdrFJe`)—that expire after periods of inactivity. This guide explains how notebooklm-py handles CSRF token expiration and refresh authentication automatically, plus how to manually trigger refreshes when needed.

## Understanding CSRF Token Expiration in notebooklm-py

### The AuthTokens System (SNlM0e and FdrFJe)

NotebookLM's web interface requires two tokens for every RPC request: `SNlM0e` (the CSRF token) and `FdrFJe` (the session identifier). These are extracted from the HTML of the NotebookLM homepage and stored in an `AuthTokens` object within the client's core. According to the source code in [`src/notebooklm/client.py`](https://github.com/teng-lin/notebooklm-py/blob/main/src/notebooklm/client.py), these tokens are initially obtained during client initialization and are bound to the HTTP client's cookie jar.

### When Tokens Expire

Tokens typically expire when a user's session times out due to inactivity or when Google invalidates the session server-side. When this happens, RPC requests return HTTP 401 or 403 errors, or raise an `AuthError`. The `is_auth_error` helper function in [`src/notebooklm/_core.py`](https://github.com/teng-lin/notebooklm-py/blob/main/src/notebooklm/_core.py) specifically checks for these conditions to distinguish authentication failures from other API errors.

## Automatic Token Refresh Mechanism

### The refresh_auth() Method

The primary method for handling token expiration is `NotebookLMClient.refresh_auth()` in [`src/notebooklm/client.py`](https://github.com/teng-lin/notebooklm-py/blob/main/src/notebooklm/client.py). This method explicitly reloads the NotebookLM homepage, extracts fresh `SNlM0e` and `FdrFJe` values using regex parsing, and updates the shared `ClientCore` authentication object. If the homepage HTML does not contain the expected tokens, the method raises a `ValueError` indicating that the underlying Google account session may have expired completely.

### update_auth_headers() Integration

Once new tokens are obtained, `refresh_auth()` calls `self._core.update_auth_headers()` (defined in [`src/notebooklm/_core.py`](https://github.com/teng-lin/notebooklm-py/blob/main/src/notebooklm/_core.py)). This method injects the updated CSRF and session values into the `httpx.AsyncClient` instance used for subsequent RPC calls. Specifically, it reconstructs the `Cookie` header with the new `SNlM0e` value, ensuring that the next request presents valid authentication credentials.

### Automatic Retry on Authentication Failure

All RPC calls in notebooklm-py route through `ClientCore.rpc_call()` in [`src/notebooklm/_core.py`](https://github.com/teng-lin/notebooklm-py/blob/main/src/notebooklm/_core.py). This method implements automatic retry logic: when an HTTP 401/403 or `AuthError` is detected (via `is_auth_error`), and if a `_refresh_callback` is configured, the method invokes the callback (which points to `NotebookLMClient.refresh_auth`) and retries the original request exactly once. This seamless handling means that for most use cases, you do not need to manually manage token expiration.

## Manual Token Refresh Methods

While automatic refresh handles most scenarios, you may want to manually refresh tokens before a critical operation or after detecting a long idle period.

```python
import asyncio
from notebooklm.client import NotebookLMClient

async def main():
    client = await NotebookLMClient.from_storage()
    await client.__aenter__()          # Open the HTTP client manually

    try:
        # Force a token refresh (e.g., after a long idle period)

        refreshed = await client.refresh_auth()
        print("New CSRF:", refreshed.csrf_token)

        # Continue with normal API calls

        notebooks = await client.notebooks.list()
        print(notebooks)
    finally:
        await client.__aexit__(None, None, None)  # Close the client

asyncio.run(main())

```

The `refresh_auth()` method returns the updated `AuthTokens` object, allowing you to inspect the new `csrf_token` and `session_id` values if needed.

## Detecting Token Expiration in Custom Code

If you are making raw HTTP calls outside the high-level client, you can reuse the same authentication helpers to detect and handle expiration.

```python
from notebooklm.auth import fetch_tokens, load_auth_from_storage

# Load existing cookies from Playwright storage state

cookies = load_auth_from_storage()

# Get fresh tokens (CSRF + session) directly from the homepage

csrf, session_id = await fetch_tokens(cookies)

# Build a request header for subsequent calls

headers = {"Cookie": f"SID={cookies['SID']}; SNlM0e={csrf}"}

```

The `fetch_tokens` function performs the same homepage request and regex parsing that `refresh_auth` uses internally, making it ideal for custom authentication flows.

## Summary

- **Automatic handling**: The `NotebookLMClient` automatically detects CSRF token expiration via `is_auth_error` and triggers `refresh_auth()` to fetch new tokens from the NotebookLM homepage.
- **Core components**: Token refresh relies on three coordinated pieces: `refresh_auth()` in [`src/notebooklm/client.py`](https://github.com/teng-lin/notebooklm-py/blob/main/src/notebooklm/client.py), `update_auth_headers()` in [`src/notebooklm/_core.py`](https://github.com/teng-lin/notebooklm-py/blob/main/src/notebooklm/_core.py), and automatic retry logic in `rpc_call()`.
- **Manual control**: You can explicitly call `await client.refresh_auth()` to preemptively refresh tokens before critical operations or after long idle periods.
- **Custom implementations**: The `fetch_tokens` and `load_auth_from_storage` helpers in [`src/notebooklm/auth.py`](https://github.com/teng-lin/notebooklm-py/blob/main/src/notebooklm/auth.py) allow you to handle token expiration in custom HTTP clients.

## Frequently Asked Questions

### How does notebooklm-py detect when a CSRF token has expired?

The library uses the `is_auth_error` helper function in [`src/notebooklm/_core.py`](https://github.com/teng-lin/notebooklm-py/blob/main/src/notebooklm/_core.py) to check for HTTP 401/403 status codes or `AuthError` exceptions. When `rpc_call` detects these conditions, it triggers the automatic refresh callback before retrying the request.

### Can I disable automatic token refresh in notebooklm-py?

While the library is designed to handle authentication seamlessly, you can effectively disable automatic refresh by not using the high-level `NotebookLMClient` and instead making raw HTTP calls with manually managed tokens using the `fetch_tokens` helper from [`src/notebooklm/auth.py`](https://github.com/teng-lin/notebooklm-py/blob/main/src/notebooklm/auth.py).

### What happens if the refresh_auth() method fails to extract new tokens?

If `refresh_auth()` in [`src/notebooklm/client.py`](https://github.com/teng-lin/notebooklm-py/blob/main/src/notebooklm/client.py) cannot find the `SNlM0e` or `FdrFJe` tokens in the homepage HTML, it raises a `ValueError`. This typically indicates that the underlying Google account session has expired completely, requiring you to re-authenticate via Playwright and regenerate the storage state file.

### How often should I manually call refresh_auth() in long-running applications?

For most use cases, you do not need to manually refresh tokens because the automatic retry mechanism handles expiration transparently. However, if your application experiences long idle periods (e.g., hours between requests) or performs critical operations that cannot tolerate even a single retry delay, calling `await client.refresh_auth()` proactively before those operations is recommended.