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
jupyter nbconvert --to html --embed-images analysis.ipynb
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 and a 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 — 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 htmlwithout--embed-imagesplus 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.