# Share a Bokeh Plot — Publish the HTML, Keep the Interaction

Canonical: https://commareports.com/share-bokeh-plot
Published: 2026-08-23

> Bokeh's save() writes a standalone HTML file that loads BokehJS from its own CDN. Publish it to Comma with INLINE resources and you get a URL where the tools, tooltips and linked brushing still work.

# Share a Bokeh plot

Bokeh has two modes and only one of them travels. A Bokeh _server_ app has a
Python process behind every widget callback. A Bokeh _standalone_ document is
one HTML file that carries its own data and its own JavaScript — it works from
any static host, or from an email attachment, or from nowhere, which is where
most of them end up.

Publishing the standalone file is the whole trick. The catch is which copy of
BokehJS the file asks for.

## Publish it

```python
from bokeh.plotting import figure, save
from bokeh.resources import INLINE
import requests, os

p = figure(title="Latency p95", x_axis_type="datetime")
p.line(df["ts"], df["p95"])

save(p, filename="plot.html", resources=INLINE, title="Latency p95")

requests.post(
    "https://commareports.com/api/v1/reports",
    headers={"Authorization": f"Bearer {os.environ['COMMA_API_TOKEN']}"},
    json={"title": "Latency p95", "html": open("plot.html").read()},
).raise_for_status()
```

`resources=INLINE` is the load-bearing argument. Bokeh's default writes a
`<script src>` pointing at `cdn.bokeh.org`, and report HTML in Comma runs
inside a sandboxed iframe whose script sources are limited to jsdelivr,
unpkg, cdnjs and esm.sh. Inlining sidesteps the question entirely and makes
the file reproducible three years from now, when that CDN has moved on.

## What the URL changes

- **The tools still work.** Box zoom, hover, reset, linked brushing across a
  gridplot — all client-side, all intact. See
  [interactive HTML reports](/interactive-html-reports).
- **Someone can argue with the chart in place.** Highlight the caption, pin
  the objection. See [commenting on HTML](/comment-on-html).
- **Reruns become revisions.** PATCH the same report id and the URL never
  changes while the history accumulates.
- **Scheduled refreshes.** A [routine](/docs/routines) can re-render the plot
  nightly against fresh data.

## Limits

- **HTML body: 5 MB** — inlined BokehJS is 1–2 MB of that. Aggregate wide
  series before saving.
- **Python callbacks need a Bokeh server** and will not fire in a standalone
  document. Use `CustomJS` for interactions that must survive publishing.
- **60 requests/minute per token.**

## Try it

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

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

### Related

- [Share a Plotly chart](/share-plotly-html) · [Share a Folium map](/share-folium-map)
- [Interactive HTML reports](/interactive-html-reports)
- [EDA reports](/share-eda-report) · [Jupyter](/with/jupyter)
