Automate Google NotebookLM with Python Using notebooklm-py

Opening

Imagine you’re building a research pipeline: you have 50 URLs to analyze, need audio summaries generated for each, and want everything exported to your local drive — every Monday morning, automatically. Doing this manually in the NotebookLM web UI would take hours of clicking. The web UI doesn’t even support batch downloads.

That’s the gap notebooklm-py fills. It’s an unofficial Python library (currently trending on GitHub with 9,000+ stars) that gives you full programmatic control over Google NotebookLM — including features the web interface doesn’t expose at all.

In this article, you’ll learn how to:

  • Install and authenticate with notebooklm-py
  • Create notebooks and import sources programmatically
  • Generate Audio Overviews (AI podcasts) from your content
  • Export and download generated artifacts locally
  • Trigger research agents and pull structured results
  • ⚠️ Heads up: This is an unofficial library using undocumented Google APIs. It’s best suited for personal projects, prototypes, and research pipelines — not production systems where Google API stability is critical.


    Core Content

    Installing and Authenticating

    Install the package from PyPI:

    pip install notebooklm-py
    

    Authentication uses your Google session cookies. The library needs your __Secure-1PSID and __Secure-1PSIDTS cookies from a logged-in NotebookLM browser session.

    from notebooklm import NotebookLM
    
    

    # Authenticate using environment variables (recommended)

    import os

    client = NotebookLM(

    cookie_1psid=os.environ["GOOGLE_1PSID"],

    cookie_1psidts=os.environ["GOOGLE_1PSIDTS"],

    )

    # Verify connection by listing notebooks

    notebooks = client.list_notebooks()

    for nb in notebooks:

    print(nb["id"], nb["title"])

    # Output:

    # abc123 My Research Notebook

    # def456 Competitor Analysis

    To get your cookies: open Chrome DevTools → Application → Cookies → notebooklm.google.com. Store them in environment variables, never hardcode them.


    Creating Notebooks and Importing Sources

    The real power starts when you automate notebook creation and bulk source importing.

    from notebooklm import NotebookLM
    

    import os

    client = NotebookLM(

    cookie_1psid=os.environ["GOOGLE_1PSID"],

    cookie_1psidts=os.environ["GOOGLE_1PSIDTS"],

    )

    # Create a new notebook

    notebook = client.create_notebook(title="Python Trends April 2026")

    notebook_id = notebook["id"]

    print(f"Created notebook: {notebook_id}")

    # Output: Created notebook: nb_x9f2k1...

    # Import multiple sources at once

    urls: list[str] = [

    "https://docs.python.org/3.13/whatsnew/3.13.html",

    "https://peps.python.org/pep-0730/",

    "https://github.com/astral-sh/uv/releases/tag/0.6.0",

    ]

    for url in urls:

    result = client.add_source(notebook_id=notebook_id, url=url)

    print(f"Added source: {result['title']}")

    # Output:

    # Added source: What's New In Python 3.13

    # Added source: PEP 730 – CPython on iOS

    # Added source: uv 0.6.0 release notes

    You can also import PDFs, YouTube videos, Google Drive files, and plain text. The add_source method accepts a file_path parameter for local files:

    # Import a local PDF
    

    import pathlib

    pdf_path = pathlib.Path("research/paper.pdf")

    result = client.add_source(

    notebook_id=notebook_id,

    file_path=str(pdf_path),

    display_name="Research Paper Q1 2026",

    )

    print(f"Imported: {result['title']}, ID: {result['id']}")

    # Output: Imported: Research Paper Q1 2026, ID: src_4k2m9...


    Generating Audio Overviews Programmatically

    This is where notebooklm-py really shines over the web UI — you can trigger Audio Overview generation in batch and download the results automatically.

    from notebooklm import NotebookLM, AudioFormat, AudioLength
    

    import os

    import time

    client = NotebookLM(

    cookie_1psid=os.environ["GOOGLE_1PSID"],

    cookie_1psidts=os.environ["GOOGLE_1PSIDTS"],

    )

    notebook_id = "nb_x9f2k1..." # from previous step

    # Request a deep-dive audio overview in English

    job = client.generate_audio_overview(

    notebook_id=notebook_id,

    format=AudioFormat.DEEP_DIVE,

    length=AudioLength.MEDIUM,

    language="en",

    )

    print(f"Audio job started: {job['job_id']}, status: {job['status']}")

    # Output: Audio job started: job_8h3p2..., status: pending

    # Poll for completion (usually 30-90 seconds)

    while True:

    status = client.get_audio_status(notebook_id=notebook_id)

    if status["state"] == "complete":

    print("Audio ready!")

    break

    elif status["state"] == "failed":

    raise RuntimeError("Audio generation failed")

    print(f"Status: {status['state']}, waiting 10s...")

    time.sleep(10)

    # Download the MP3

    output_path = client.download_audio(

    notebook_id=notebook_id,

    destination="./outputs/overview.mp3",

    )

    print(f"Downloaded to: {output_path}")

    # Output: Downloaded to: ./outputs/overview.mp3

    Available audio formats: DEEP_DIVE, BRIEF, CRITIQUE, DEBATE. Lengths: SHORT, MEDIUM, LONG. Over 50 languages are supported — including Malay, Indonesian, and Thai.


    Triggering Research Agents and Extracting Results

    Beyond storing sources you manually provide, NotebookLM has a research agent that can search the web and auto-import relevant content. notebooklm-py exposes this programmatically:

    from notebooklm import NotebookLM, ResearchMode
    

    import os

    client = NotebookLM(

    cookie_1psid=os.environ["GOOGLE_1PSID"],

    cookie_1psidts=os.environ["GOOGLE_1PSIDTS"],

    )

    notebook_id = "nb_x9f2k1..."

    # Run a web research query — auto-imports found sources

    research_result = client.run_research(

    notebook_id=notebook_id,

    query="Python async patterns best practices 2026",

    mode=ResearchMode.DEEP, # or ResearchMode.FAST

    auto_import=True,

    )

    print(f"Research complete. Imported {len(research_result['imported_sources'])} sources:")

    for src in research_result["imported_sources"]:

    print(f" - {src['title']} ({src['url']})")

    # Output:

    # Research complete. Imported 4 sources:

    # - Python AsyncIO Deep Dive 2026 (https://realpython.com/...)

    # - Async patterns in production (https://blog....)

    # - ...

    After research, you can chat with the notebook programmatically:

    response = client.chat(
    

    notebook_id=notebook_id,

    message="What are the 3 most important async patterns across my sources?",

    )

    print(response["answer"])

    # Output: Based on your sources, the three most important patterns are:

    # 1. TaskGroup for structured concurrency...


    Common Mistakes

    Mistake 1: Storing Cookies as Code Constants

    Wrong:

    # NEVER do this — cookies expire and you'll expose credentials in source control
    

    client = NotebookLM(

    cookie_1psid="APA91b...", # hardcoded secret

    cookie_1psidts="sidts_abc123", # hardcoded secret

    )

    Right:

    import os
    

    from dotenv import load_dotenv

    load_dotenv() # loads from .env file (add .env to .gitignore!)

    client = NotebookLM(

    cookie_1psid=os.environ["GOOGLE_1PSID"],

    cookie_1psidts=os.environ["GOOGLE_1PSIDTS"],

    )

    Why it matters: Google session cookies expire in days to weeks. Hardcoding them means your script breaks silently and, worse, commits them to version control where they might be exposed before expiry.


    Mistake 2: Not Handling API Instability

    Since notebooklm-py uses undocumented Google APIs, they can break without warning. Treating it like a stable SDK will cause painful production outages.

    Wrong:

    # No error handling — fails silently or crashes entire pipeline
    

    notebook = client.create_notebook(title="Weekly Research")

    client.add_source(notebook_id=notebook["id"], url=some_url)

    audio = client.generate_audio_overview(notebook_id=notebook["id"])

    Right:

    import logging
    

    from notebooklm.exceptions import NotebookLMAPIError, RateLimitError

    logger = logging.getLogger(__name__)

    def safe_create_pipeline(client: NotebookLM, title: str, url: str) -> dict | None:

    try:

    notebook = client.create_notebook(title=title)

    client.add_source(notebook_id=notebook["id"], url=url)

    return notebook

    except RateLimitError:

    logger.warning("Rate limit hit — waiting 60 seconds")

    time.sleep(60)

    return None

    except NotebookLMAPIError as e:

    logger.error(f"NotebookLM API error: {e}. Google may have changed endpoints.")

    return None

    Why it matters: defensive error handling keeps your pipeline running and gives you actionable logs when Google inevitably tweaks something.


    Wrap-up

    notebooklm-py unlocks a powerful workflow that the NotebookLM web UI simply can’t offer: batch source imports, programmatic audio generation, automated research agents, and local file exports — all from Python scripts or AI agents.

    The key steps: install via pip, authenticate with browser cookies stored as environment variables, create notebooks, import sources (URLs, PDFs, YouTube), and trigger content generation. Use proper error handling given the unofficial API nature.

    The logical next step: combine this with a task scheduler like APScheduler or a simple cron job to build a fully automated weekly research digest — your NotebookLM notebooks populated and audio summaries downloaded every Monday morning, zero manual effort.

    No comments yet. Be the first to leave a comment!

    Leave a Comment

    Your email address will not be published. Required fields are marked *