---
name: package
description: "nbformat package guide for reading, validating, converting, and signing Jupyter notebooks in Python"
metadata:
  languages: "python"
  versions: "5.10.4"
  revision: 1
  updated-on: "2026-03-12"
  source: maintainer
  tags: "nbformat,jupyter,notebook,ipynb,validation,jsonschema"
---

# nbformat Python Package Guide

## Install

Pin the package version your project expects:

```bash
python -m pip install "nbformat==5.10.4"
```

Common alternatives:

```bash
uv add "nbformat==5.10.4"
poetry add "nbformat==5.10.4"
```

PyPI currently lists Python 3.8 through 3.12 classifiers for `5.10.4`.

## Setup And Version Selection

Notebook files are JSON documents with top-level `metadata`, `nbformat`, `nbformat_minor`, and `cells` keys. In Python, `nbformat` works with `NotebookNode` objects, which behave like dictionaries with attribute access.

Check the notebook format version your installed library understands:

```python
import nbformat

print(nbformat.current_nbformat)
print(nbformat.current_nbformat_minor)
```

When reading notebooks, always pass an explicit `as_version`:

```python
import nbformat

nb = nbformat.read("analysis.ipynb", as_version=4)
```

Use:

- `as_version=4` when your code expects the current 4.x notebook structure
- `as_version=nbformat.NO_CONVERT` when you want to preserve the on-disk version exactly

## Core Usage

### Create A Notebook Programmatically

```python
from pathlib import Path

import nbformat
from nbformat.v4 import new_code_cell, new_markdown_cell, new_notebook

nb = new_notebook(
    metadata={
        "kernelspec": {
            "name": "python3",
            "display_name": "Python 3",
        },
        "language_info": {
            "name": "python",
            "version": "3.12",
        },
    },
    cells=[
        new_markdown_cell("# Demo notebook"),
        new_code_cell("answer = 40 + 2\nprint(answer)"),
    ],
)

nbformat.validate(nb)
nbformat.write(nb, Path("demo.ipynb"))
```

Use the `nbformat.v4` constructors instead of hand-building cell dictionaries. They create the required shape for notebook and cell objects.

### Read, Validate, And Update A Notebook

```python
import nbformat
from nbformat import ValidationError

path = "analysis.ipynb"
nb = nbformat.read(path, as_version=4)

try:
    nbformat.validate(nb)
except ValidationError as exc:
    raise RuntimeError(f"Invalid notebook: {exc}") from exc

nb.cells.append(nbformat.v4.new_markdown_cell("Generated by automation."))
nbformat.write(nb, path)
```

If you want validation details without raising during `read()` or `write()`, pass a mutable dict to `capture_validation_error`.

### Work From A Raw Dict Or JSON String

```python
import nbformat

payload = {
    "cells": [],
    "metadata": {},
    "nbformat": 4,
    "nbformat_minor": 5,
}

nb = nbformat.from_dict(payload)
nbformat.validate(nb)

text = nbformat.writes(nb)
round_tripped = nbformat.reads(text, as_version=4)
```

`from_dict()` only converts nested dictionaries into `NotebookNode` objects. It does not make invalid notebook content valid, so call `validate()` before trusting the result.

### Convert Notebook Versions

```python
import nbformat

nb = nbformat.read("legacy.ipynb", as_version=nbformat.NO_CONVERT)

if nb.nbformat != 4:
    nb = nbformat.convert(nb, 4)
    nbformat.validate(nb)
    nbformat.write(nb, "legacy-v4.ipynb")
```

`convert()` works across major notebook versions. It is useful when migration code needs a stable 4.x shape before making edits.

### Sign And Trust Notebooks

Use notebook signing only if your workflow participates in Jupyter's trust model for outputs.

```python
import nbformat
from nbformat.sign import NotebookNotary

path = "report.ipynb"
nb = nbformat.read(path, as_version=4)

notary = NotebookNotary()

if not notary.check_signature(nb):
    notary.sign(nb)
    notary.mark_cells(nb, True)
    nbformat.write(nb, path)
```

`NotebookNotary` uses an SQLite-backed signature store by default when SQLite bindings are available, and an in-memory store otherwise.

## Configuration Notes

- `nbformat` does not use network auth, API keys, or service credentials.
- The main runtime choice is validator behavior. The upstream repository notes that `fastjsonschema` is the default validator in current releases; set `NBFORMAT_VALIDATOR=jsonschema` if you need the `jsonschema` backend instead.
- Treat notebook metadata as shared namespace. For custom metadata, use a unique prefix such as `metadata.my_tool.*`.
- Notebook files on disk may store multi-line strings as either a string or a list of strings. The Python API normalizes these back to strings when reading.

## Common Pitfalls

- Do not read notebooks without `as_version`. The API expects you to declare which structure your code knows how to handle.
- Do not treat arbitrary dictionaries as valid notebooks just because `from_dict()` succeeded. Validation is separate.
- Do not depend on `validate()` to silently repair notebooks. That behavior was deprecated in `5.5.0`.
- Do not generate duplicate or malformed cell IDs. Since the 4.5 schema, every cell must have a unique `id` matching the schema rules.
- Do not assume all metadata is meaningful. The format explicitly allows optional metadata that tools may ignore.
- Do not parse notebook JSON manually unless you really need raw control. The library already handles version conversion, NotebookNode wrapping, and validation.

## Version-Sensitive Notes

- `5.10.4` is a maintenance release. The upstream changelog does not list API-level behavior changes for this patch.
- Since `5.5.0`, `validate()` no longer represents the "fix up the notebook for me" path. If you truly need normalization, call the validator module's `normalize()` helper explicitly, but upstream still recommends generating valid notebooks directly.
- Since `5.3.0`, `fastjsonschema` is the default validator backend.
- Since `5.1.0` and the 4.5 notebook schema, cell IDs are part of the spec. Older blog posts and old handcrafted notebook fixtures often miss them.

## Official Sources

- Docs root: `https://nbformat.readthedocs.io/en/latest/`
- Python API: `https://nbformat.readthedocs.io/en/latest/api.html`
- Notebook format description: `https://nbformat.readthedocs.io/en/latest/format_description.html`
- Changelog: `https://nbformat.readthedocs.io/en/latest/changelog.html`
- PyPI: `https://pypi.org/project/nbformat/`
