# How to Handle Source Freshness Checking and Refresh Operations in NotebookLM-Py

> Learn how to manage source freshness checking and refresh operations in NotebookLM-Py. Use check_freshness() and refresh() to keep your data up-to-date.

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

---

**Use the `check_freshness()` method to query the backend for staleness, then call `refresh()` to trigger re-crawling when needed.**

The `notebooklm-py` library provides a Pythonic interface to Google's NotebookLM service, including robust tools to handle source freshness checking and refresh operations. Whether you're managing URLs, Google Drive files, or YouTube videos, the SDK abstracts the underlying RPC complexity into simple boolean checks and explicit refresh commands.

## Understanding Source Freshness in NotebookLM-Py

NotebookLM-Py treats a source as fresh until the backend reports that its content has changed. The freshness state depends on the source type—URLs, Drive documents, and videos each return different response structures from the NotebookLM API.

The SDK normalizes these idiosyncrasies in [`src/notebooklm/_sources.py`](https://github.com/teng-lin/notebooklm-py/blob/main/src/notebooklm/_sources.py), converting complex nested lists and null values into a straightforward boolean: `True` means fresh, `False` means stale and requires a refresh.

## Checking Source Freshness Programmatically

To verify whether a source needs updating, use the `check_freshness` method on the `Sources` manager. This method constructs an RPC payload and interprets the backend's heterogeneous response formats.

```python
from notebooklm import NotebookLMClient

async def is_source_fresh(notebook_id: str, source_id: str) -> bool:
    async with await NotebookLMClient.from_storage() as client:
        # Resolve partial IDs to full UUIDs

        nb_id = await client.notebooks.resolve_id(notebook_id)
        src_id = await client.sources.resolve_id(nb_id, source_id)
        
        # Check freshness

        fresh = await client.sources.check_freshness(nb_id, src_id)
        return fresh

```

### The check_freshness Method Implementation

In [`src/notebooklm/_sources.py`](https://github.com/teng-lin/notebooklm-py/blob/main/src/notebooklm/_sources.py) (lines 90-124), the `check_freshness` method builds the RPC payload as `params = [None, [source_id], [2]]` and calls `RPCMethod.CHECK_SOURCE_FRESHNESS` (ID `yR9Yof`).

The backend returns varying structures depending on source type:
- `[]` → fresh (URL sources)
- `[[null, true, [source_id]]]` → fresh (Drive sources)
- `True` → fresh (generic)
- `False` → stale

The method normalizes these into a single boolean by inspecting the response list length, nested values, or direct boolean returns (lines 106-124).

## Refreshing Stale Sources

When `check_freshness` returns `False`, trigger a backend re-crawl using the `refresh` method. This operation requests the NotebookLM service to update the indexed text for the specified source.

```python
async def refresh_if_stale(client, notebook_id: str, source_id: str):
    is_fresh = await client.sources.check_freshness(notebook_id, source_id)
    
    if not is_fresh:
        print("Source is stale – triggering refresh")
        success = await client.sources.refresh(notebook_id, source_id)
        if success:
            print("Refresh request accepted")
            # Optionally wait for processing to complete

            await client.sources.wait(notebook_id, source_id, timeout=180)
    else:
        print("Source is already fresh")

```

### The refresh Method Implementation

Located in [`src/notebooklm/_sources.py`](https://github.com/teng-lin/notebooklm-py/blob/main/src/notebooklm/_sources.py) (lines 71-88), the `refresh` method uses the same three-element parameter format `[None, [source_id], [2]]` but invokes `RPCMethod.REFRESH_SOURCE` (ID `FLmJqe`).

Unlike `check_freshness`, this call does not return data from the backend; the method simply returns `True` if the RPC request was accepted, indicating that the re-crawl has been queued.

## CLI Commands for Freshness and Refresh Operations

The command-line interface in [`src/notebooklm/cli/source.py`](https://github.com/teng-lin/notebooklm-py/blob/main/src/notebooklm/cli/source.py) provides direct access to these operations without writing Python code.

### Checking Freshness via CLI

Use `notebooklm source stale` to check freshness from the shell:

```bash
notebooklm source stale <SOURCE_ID>

```

This command:
- Resolves partial IDs to full UUIDs using `resolve_source_id`
- Runs `client.sources.check_freshness`
- Prints a green "fresh" or yellow "stale" message
- Exits with **code 1** for fresh sources and **code 0** for stale sources (lines 618-654 in [`source.py`](https://github.com/teng-lin/notebooklm-py/blob/main/source.py))

This exit code inversion is designed for shell scripting: a "true" (0) exit status means action is required (stale), allowing constructions like:

```bash
if notebooklm source stale "$SRC_ID"; then
    notebooklm source refresh "$SRC_ID"
fi

```

### Triggering Refresh via CLI

Use `notebooklm source refresh` to trigger re-crawling:

```bash
notebooklm source refresh <SOURCE_ID>

```

This command (lines 20-52 in [`source.py`](https://github.com/teng-lin/notebooklm-py/blob/main/source.py)):
- Resolves the source ID
- Invokes `client.sources.refresh`
- Reports success by printing the source ID or title
- Returns `True` when the backend accepts the request

## Complete Workflow Example

Combine freshness checking, conditional refreshing, and waiting for processing in a single script:

```python
import asyncio
from notebooklm import NotebookLMClient

async def ensure_fresh_source(notebook_id: str, source_id: str):
    async with await NotebookLMClient.from_storage() as client:
        # Resolve IDs

        nb = await client.notebooks.resolve_id(notebook_id)
        src = await client.sources.resolve_id(nb, source_id)
        
        # Check freshness

        if await client.sources.check_freshness(nb, src):
            print(f"Source {src} is fresh")
            return
        
        # Refresh if stale

        print(f"Source {src} is stale, refreshing...")
        await client.sources.refresh(nb, src)
        
        # Wait for completion

        await client.sources.wait(nb, src, timeout=300)
        print("Source refreshed and ready")

# Run

asyncio.run(ensure_fresh_source("nb-abc123", "src-xyz789"))

```

## Summary

- **Freshness checking** uses `RPCMethod.CHECK_SOURCE_FRESHNESS` (ID `yR9Yof`) in [`src/notebooklm/_sources.py`](https://github.com/teng-lin/notebooklm-py/blob/main/src/notebooklm/_sources.py) to return a normalized boolean indicating whether the source content has changed.
- **Refresh operations** trigger backend re-crawling via `RPCMethod.REFRESH_SOURCE` (ID `FLmJqe`), accepting the same `[None, [source_id], [2]]` payload format.
- **CLI integration** provides `notebooklm source stale` (exit code 0 for stale, 1 for fresh) and `notebooklm source refresh` for shell scripting.
- **Response normalization** handles heterogeneous backend formats—empty lists for URLs, nested structures for Drive files, and plain booleans—converting all to simple `True`/`False` values.

## Frequently Asked Questions

### How does NotebookLM-Py determine if a source is fresh or stale?

NotebookLM-Py queries the backend using `RPCMethod.CHECK_SOURCE_FRESHNESS` (ID `yR9Yof`) with a payload of `[None, [source_id], [2]]`. The backend returns varying structures depending on source type—empty lists for fresh URLs, nested `[null, true, [source_id]]` arrays for fresh Drive files, or plain booleans. The SDK normalizes these into a single boolean where `True` indicates fresh content and `False` indicates stale content requiring a refresh.

### What is the difference between check_freshness and refresh in the Python API?

`check_freshness` is a read-only operation that queries the backend state without modifying the source, returning a boolean indicating whether the indexed content matches the original. `refresh` is a write operation that triggers the backend to re-crawl the source URL or file and update the indexed text. While `check_freshness` returns `True` or `False` based on backend state, `refresh` returns `True` only to confirm that the refresh request was accepted and queued for processing.

### How can I automate freshness checks in shell scripts using the CLI?

Use the `notebooklm source stale` command, which returns exit code **0** when the source is stale (action required) and exit code **1** when the source is fresh. This inverted logic allows straightforward conditional execution: `if notebooklm source stale "$SRC_ID"; then notebooklm source refresh "$SRC_ID"; fi`. The command also prints colored output—green for fresh, yellow for stale—providing visual feedback during interactive use while maintaining scriptable exit codes for automation.

### What payload format does the SDK use when calling the NotebookLM RPC methods?

Both `check_freshness` and `refresh` use a three-element list payload: `[None, [source_id], [2]]`. The first element is always `None`, the second is a list containing the source UUID string, and the third is a list containing the integer `2`. This format is required by the NotebookLM backend RPC interface and is hardcoded in [`src/notebooklm/_sources.py`](https://github.com/teng-lin/notebooklm-py/blob/main/src/notebooklm/_sources.py) for both the `RPCMethod.REFRESH_SOURCE` (ID `FLmJqe`) and `RPCMethod.CHECK_SOURCE_FRESHNESS` (ID `yR9Yof`) method calls.