# Share a Jupyter Notebook as HTML — and Get It Reviewed

Canonical: https://commareports.com/with/jupyter
Published: 2026-07-02

> Export your notebook with nbconvert, publish the HTML to Comma, and collect comments anchored to specific cells and table rows. Private by choice, revisioned, agent-friendly — an nbviewer alternative for notebooks that aren't public.

# Share a Jupyter notebook as HTML

The analysis is done. The notebook runs top to bottom. Now a PM, a data
lead, and two engineers need to read it and tell you what's wrong with it —
and none of them are going to `pip install` anything to do it.

The usual answers each solve half the problem. nbviewer renders notebooks
beautifully, but only ones that are already public, and nobody can comment.
GitHub renders `.ipynb` files in the repo, but statically, behind repo
access, with no way to discuss a specific output. Emailing the exported
`.html` file works exactly once, and then the feedback scatters across
reply-all.

Comma is the missing step: export the notebook to HTML, publish it, and
collect comments anchored to the exact cell or table row being discussed.

## The pipeline

### 1. Export with nbconvert

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

This snapshots the notebook as it last ran — markdown, code, rendered
outputs — into a single HTML file, with figure outputs embedded. Two flags
worth knowing:

- `--no-input` hides the code cells, which turns the export into a
  stakeholder-facing report instead of a wall of Python.
- `--embed-images` (recent nbconvert versions) inlines images your markdown
  cells reference from disk, so the file is fully self-contained.

### 2. Publish it to Comma

Three ways in, same result: paste the HTML into the app, drag-drop the
file, or POST it from a terminal. The create endpoint takes JSON with a
required `html` field plus optional `title` and `description`:

```bash
jq -Rs '{title: "Retention analysis — July", html: .}' analysis.html |
  curl -s -X POST https://commareports.com/api/v1/reports \
    -H "Authorization: Bearer $COMMA_API_TOKEN" \
    -H "Content-Type: application/json" \
    --data-binary @-
```

The response carries a `share_url`. Tokens come from
**Settings → API tokens**, are scoped and revocable, and the HTML limit is
5 MB — notebooks heavy with embedded figures can exceed that, in which case
attach the big images as [assets](/docs/api) (25 MB per file) instead.

Pass `"visibility": "private"` at create time to keep the report
invite-only, or set access later from the share dialog — private, your
team, any signed-in user, or anyone with the link. This is the part
nbviewer structurally can't do.

### 3. Review like a document

Reviewers open the link in a browser — no Jupyter, no environment, no
repo access. They highlight a sentence in your markdown commentary or a
row in a results table and pin a comment to it, Google-Docs style.
Threads, replies, resolve, reopen. Commenting is free and unlimited on
every plan; nobody needs a paid seat to say "cell 12's join looks wrong."

### 4. Re-run, republish, same link

When the comments come back, fix the notebook, export again, and PATCH:

```bash
jq -Rs '{html: .}' analysis.html |
  curl -s -X PATCH "https://commareports.com/api/v1/reports/$REPORT_ID" \
    -H "Authorization: Bearer $COMMA_API_TOKEN" \
    -H "Content-Type: application/json" \
    --data-binary @-
```

Comma appends a revision under the same URL, keeps the comment threads
attached, and can diff any two revisions — so "did the fix change the
numbers?" is one click, not an archaeology project.

## The honest comparison

|                          | **Comma**                                | **nbviewer**            | **GitHub rendering**          | **Emailing the .html**  |
| ------------------------ | ---------------------------------------- | ----------------------- | ----------------------------- | ----------------------- |
| **Private notebooks**    | Yes — private / invite / team / public   | No — public URLs only   | Behind repo access            | Whoever has the file    |
| **Comments**             | Anchored to cells, text, and table rows  | None                    | On JSON source lines, not outputs | Reply-all           |
| **Stable link**          | Yes, survives re-runs                    | Yes, but render-only    | Yes, but static               | No link at all          |
| **Revisions & diffs**    | Yes, built in                            | No                      | Git history of raw JSON       | Attachment archaeology  |
| **Reviewer needs**       | A browser                                | A browser               | A GitHub account + access     | A browser               |
| **Agent can publish**    | Yes — REST + MCP under one scoped token  | No                      | Via git, awkwardly            | No                      |

Where the alternatives win: nbviewer is zero-setup for notebooks that are
already public — if your notebook lives in a public gist and nobody needs
to comment, nbviewer is fine. GitHub rendering is unbeatable for
code-review of the notebook's *source*. Comma is for reviewing the
*findings*.

## What renders, honestly

Comma renders your export untouched inside an opaque-origin sandbox — no
reformatting, no restyling. One deliberate exception: the API strips
`<script>` tags before storage, as defense-in-depth on an endpoint that
accepts arbitrary HTML. For a Jupyter export that means markdown, code
listings, tables, and static figures (matplotlib/seaborn as PNG or SVG)
survive faithfully, while script-driven interactivity — ipywidgets, Plotly
and Bokeh tooltips — renders statically or not at all.

For review that's usually the right trade: a comment can anchor to a
rendered chart; it can't anchor to a hover state. Keep the live notebook
for exploration and publish the snapshot for review.

## From an agent, skip the curl

If Claude Code or Cursor is the thing running the notebook, use Comma's
MCP server instead of shell plumbing. The same `comma_sk_…` token gates
both surfaces, and the agent gets typed tools: `create_report`,
`update_report`, `list_comments`, `reply_to_comment`. The end of an
agentic task becomes "run the notebook, export, publish to Comma, return
the share link" — and the start of the next one becomes "read the open
comments and address them." See
[Use Comma with Claude Code](/mcp/claude-code) for the one-line install.

## Making it recur

Two ways to keep the report fresh:

- **Your scheduler, Comma as destination.** A cron job or
  [CI pipeline](/docs/ci) runs `jupyter nbconvert` and the PATCH call
  above. Works on every plan.
- **Comma routines.** Hosted scheduling that re-runs a skill and
  republishes at any cadence on every plan. You pay only for the AI
  compute — bring your own AWS Bedrock key for free, or draw down prepaid
  credits. See [Routines →](/features/routines).

## Try it

Publishing, collaboration, and routines are all free. Export a notebook
you've already run and publish it — the difference shows up in the first
comment, which lands on the row of the table it's about instead of in a
Slack thread describing where to look.

**[Create your first report →](https://commareports.com/)**

### Related

- [Use Comma with Quarto](/with/quarto) — the document-renderer pipeline
- [Use Comma with marimo](/with/marimo) — the reactive-notebook pipeline
- [How to share an HTML report](/share-html-report) — the general pattern
- [Publish from CI](/docs/ci) — automate the export-and-publish step
