Three ways, depending on what you have. 1) `jupyter nbconvert --to script notebook.ipynb` — the official CLI; fast, but it keeps IPython magics like %matplotlib, so the .py won't run in plain Python as-is. 2) An online converter (like our Jupyter → Script tool) — no install, runs in your browser, and strips magics so the output is runnable. 3) A few lines of standard-library Python — a .ipynb is just JSON, so you can pull the code cells yourself. All three drop the saved cell outputs; only the code comes across.
A Jupyter notebook (.ipynb) is great for exploring, but eventually you need a plain .py — to run it as a script, import it as a module, put it in version control, or ship it. The conversion looks trivial, and mostly is, but there are two things that trip people up: IPython magics (%matplotlib inline, !pip install) that aren't valid Python, and the saved outputs baked into the file.
I tested the three common approaches on the same notebook — a markdown cell, imports with a magic, a code cell with saved output, and a function — to show exactly what each one produces. Here's the notebook, as the JSON it really is:
A .ipynb is just JSON
Before converting anything, it helps to know a notebook is a JSON document: a list of cells, each with a cell_type (code or markdown), its source (lines of text), and — for code cells — any saved outputs. That's the whole format. Our test notebook has four cells:
{"cells": [{"cell_type": "markdown", "source": ["# Sales analysis\n", "Quick exploration."]},{"cell_type": "code", "source": ["import pandas as pd\n", "%matplotlib inline"]},{"cell_type": "code","outputs": [{"output_type": "stream", "text": ["rows: 1000\n"]}],"source": ["df = pd.DataFrame({'x': range(1000)})\n", "print('rows:', len(df))"]},{"cell_type": "code", "source": ["def total(col):\n", " return col.sum()"]}],"nbformat": 4, "nbformat_minor": 5}
Method 1 — jupyter nbconvert (the official CLI)
If you have Jupyter installed, the built-in converter is one command:
$ jupyter nbconvert --to script sales.ipynb# writes sales.py (use --stdout to print instead)
Here's the actual output (nbconvert 7.17.1). It concatenates the code cells — but notice two things:
import pandas as pd%matplotlib inlinedf = pd.DataFrame({'x': range(1000)})print('rows:', len(df))def total(col):return col.sum()
The saved output (rows: 1000) is correctly gone — but %matplotlib inline is still there. nbconvert leaves IPython magics in place, so this .py raises a SyntaxError under plain python (it only runs via IPython). You'll need to strip the magic lines yourself.
Method 2 — an online converter (no install)
If you don't have Jupyter set up, or you want a runnable .py without hand-editing, a browser tool is the quickest path. Our free Jupyter Notebook → Python Script converter runs entirely in your browser: drop in the .ipynb, get a clean .py. It does what nbconvert doesn't — removes the IPython magics and shell escapes — so the result runs as ordinary Python.
Under the hood it's the same idea as Method 3 below (parse the JSON, keep the code) — it just happens client-side, so your notebook never leaves your machine.
Method 3 — a few lines of standard-library Python
Because a notebook is just JSON, you don't strictly need any package. This reads the cells, comments the markdown, drops the magics/shell lines, and skips outputs entirely:
import jsondef notebook_to_script(path, keep_markdown=True):nb = json.load(open(path))out = []for cell in nb.get('cells', []):src = ''.join(cell.get('source', []))if cell['cell_type'] == 'code':lines = [ln for ln in src.splitlines()if not ln.lstrip().startswith(('%', '!'))]out.append('\n'.join(lines))elif keep_markdown and cell['cell_type'] == 'markdown':out.append('\n'.join('# ' + ln for ln in src.splitlines()))return '\n\n'.join(b for b in out if b.strip()) + '\n'
# # Sales analysis# Quick exploration.import pandas as pddf = pd.DataFrame({'x': range(1000)})print('rows:', len(df))def total(col):return col.sum()
I verified the result with compile(script, '<nb>', 'exec') — it's valid Python (no leftover magic), the markdown became comments, and the saved rows: 1000 output was dropped. That's the clean, runnable conversion most people actually want.
Which method should you use?
| Method | Install needed | Magics | Best for |
|---|---|---|---|
nbconvert --to script | Jupyter | Left in (may not run) | You already run Jupyter |
| Online converter | None (browser) | Stripped → runnable | Quick one-off, no setup |
| Stdlib JSON parse | None | Stripped → runnable | Automation / your own pipeline |
Whichever you pick, remember what conversion doesn't do: it won't reproduce cell outputs, and code that relied on notebook state (out-of-order execution, In[]/Out[], display side-effects) may behave differently as a top-to-bottom script. Once you have the .py, you can run it in the online Python compiler or tidy it with the Python Formatter.
Convert your notebook — free
Drop an .ipynb in and get a clean, runnable .py — magics stripped, nothing uploaded. In your browser, no signup.
Open the Jupyter → Script toolFree tools mentioned here
Frequently asked questions
How do I convert a Jupyter notebook to a Python script?
Three common ways: run jupyter nbconvert --to script notebook.ipynb (official CLI, but it leaves IPython magics in the output); use an online converter like pyobfuscate.com's Jupyter → Script tool (no install, strips magics so the .py runs); or, since a .ipynb is just JSON, extract the code cells yourself with a few lines of standard-library Python. All three drop the saved cell outputs.
Does converting a notebook keep the cell outputs?
No. Conversion to a .py script extracts only the code (and optionally markdown as comments). The saved outputs — printed text, tables, plots — are not reproduced; you'd re-run the script to regenerate them. That's usually what you want, since outputs aren't executable code.
Why does my converted .py have %matplotlib or other errors?
IPython magics (%matplotlib, %timeit) and shell escapes (!pip install) are valid in notebooks but not in plain Python, and jupyter nbconvert leaves them in the script — so running it with python raises a SyntaxError. Remove those lines (any line starting with % or !), or use a converter that strips them automatically, like our online Jupyter → Script tool.
Can I convert an .ipynb to .py without installing Jupyter?
Yes. A notebook is a JSON file, so you can parse it and pull out the code cells with the standard-library json module in a few lines — no Jupyter or nbconvert required. Or use an in-browser converter that does it locally without any install; your notebook never gets uploaded.
How do I convert a Python script back into a notebook?
Use jupyter nbconvert or jupytext to go the other direction, or the p2j tool. jupytext in particular can pair a .py and .ipynb and keep them in sync, treating # %% comments as cell boundaries — handy if you want to edit as a script but run as a notebook.