# Share a Jupyter Notebook as HTML — nbconvert to a Link

Canonical: https://commareports.com/share-jupyter-notebook-html
Published: 2026-08-24

> jupyter nbconvert --to html gives you a self-contained file nobody can open. Publish it to Comma for a URL that renders the plots, keeps the outputs, and lets a reviewer comment on cell 14.

# Share a Jupyter notebook as HTML

The notebook is done. The analysis holds up. Now comes the part the tooling
never solved: getting it in front of someone who does not have Jupyter, does
not want a repo checkout, and will read it on a phone in a meeting.

The usual answers all leak. Email the `.ipynb` and you have sent someone a
JSON file. Push it to GitHub and the render is read-only, interactive output
is stripped, and the repo is now a place where analysis goes to be forgotten.
Screenshot the chart and every number becomes unverifiable.

`nbconvert` already produces the right artifact. It just leaves it on disk.

## Convert, then publish

```bash
jupyter nbconvert --to html --embed-images analysis.ipynb
```

```python
import os, requests

r = requests.post(
    "https://commareports.com/api/v1/reports",
    headers={"Authorization": f"Bearer {os.environ['COMMA_API_TOKEN']}"},
    json={
        "title": "Q3 retention analysis",
        "description": "Cohort retention after the pricing change",
        "html": open("analysis.html").read(),
    },
)
r.raise_for_status()
print(r.json()["url"])
```

`--embed-images` inlines matplotlib and seaborn output as data URIs, so the
file is genuinely self-contained rather than pointing at a `_files/`
directory you forgot to send.

## Two versions, on purpose

- **`--no-input`** — markdown, charts and printed results, no code. This is
  the version for the person who needs the conclusion. It reads like a
  document because it is one.
- **Full output** — for the reviewer who is going to ask why the join is a
  left join. Publish it as a second report and link between them.

Both are one command apart, and giving each audience the right one is the
difference between "looks good" and an actual review.

## What survives

Report HTML is stored verbatim and rendered with scripts enabled inside a
sandboxed iframe (`allow-scripts`, no `allow-same-origin`):

- **Static plots** — matplotlib, seaborn, any image output: yes.
- **Plotly, Bokeh, Altair, Folium** — yes, as long as the library is inlined
  or loaded from jsdelivr, unpkg, cdnjs or esm.sh. See
  [sharing a Plotly chart](/share-plotly-html) and
  [a Folium map](/share-folium-map).
- **Live widgets bound to a kernel** — no, and not on nbviewer or GitHub
  either. Nothing is running the Python.

## Where the review actually happens

The reason to publish a notebook rather than host it is that hosting ends the
conversation and review starts it. Comments anchor to the rendered
paragraph — see [commenting on HTML](/comment-on-html) — so
"this cohort excludes trials, right?" lands on the cohort table instead of in
a thread nobody can map back to a cell.

Reruns PATCH the same report id, so the URL you pasted in January still shows
January's numbers in the revision history and today's numbers at the top.

## Limits

- **HTML body: 5 MB.** Embedded images add up; `--to html` without
  `--embed-images` plus the `_files/` folder as a bundle is the escape hatch
  (25 MB per file, 250 MB and 500 files per report).
- **60 requests/minute per token.**

## Try it

Comma is free — unlimited reports, unlimited commenters, unlimited revision
history.

**[Publish a notebook →](https://commareports.com/)**

### Related

- [Jupyter integration](/with/jupyter) · [Colab](/with/colab)
- [nbviewer alternatives](/alternatives/nbviewer-alternatives)
- [Share an EDA report](/share-eda-report) · [Share a Plotly chart](/share-plotly-html)
