A video that recently began circulating again under the title “How to connect ChatGPT to Jupyter Notebooks for free” looks like it is describing a modern integration between ChatGPT and Jupyter.
But there is an important detail: the original material was published by Alex The Analyst on April 25, 2023.
That changes the technical interpretation quite a bit.
In 2023, ChatGPT was still a product much more isolated from the development ecosystem. The extension shown in the tutorial solved a real problem in a fairly clever way: it reused ChatGPT’s authenticated browser session and allowed prompts to be sent from a notebook.
The idea is still excellent.
The implementation, not so much.
Three years later, the ecosystem around notebooks, APIs, and agents has evolved enough that there are now several cleaner and more powerful architectures.
And the most interesting part is not simply “having ChatGPT inside Jupyter.”
The truly powerful idea is something else:
a model that can observe a notebook, generate code, execute it, inspect the result, and continue reasoning becomes an analysis agent.
That pattern is much more important than the specific extension in the video.
What the 2023 extension actually did
The project used in that tutorial is called ChatGPT for Jupyter.
Its architecture looked approximately like this:
Jupyter Notebook
↓
Browser extension
↓
reuses the ChatGPT session
↓
ChatGPT web service
↓
response
↓
the extension creates cells in the notebook
The user wrote a prompt inside a Markdown cell with a special marker:
##### chat
Write a Python function that calculates prime numbers.
When the cell was executed, the extension sent the text to ChatGPT.
When the model returned code, the extension could extract that block and automatically place it into a new executable cell.
For 2023, that was a surprisingly advanced experience.
But the most important technical detail appears in the project’s own README: the extension reused the bearer token obtained by signing in to ChatGPT and acted as a kind of privileged proxy from the browser.
That made sense at the time because the official ChatGPT API did not yet offer the path we now take for granted.
The extension was also designed mainly for the classic Jupyter Notebook interface, not the modern JupyterLab architecture.
Why that method was “free”
The word free in the title has a concrete explanation.
The extension was not making normal OpenAI API calls billed by usage.
Instead, it used the web session the user already had open in ChatGPT.
Conceptually:
User pays for or uses ChatGPT
↓
signs in at chatgpt.com
↓
browser obtains session credentials
↓
extension reuses that session
↓
Jupyter can converse with ChatGPT
That avoided having to configure an API key and pay separately for calls.
Clever, yes.
But also fragile.
The code depended on internal details of the web product: authentication, endpoints, tokens, browser restrictions, and mechanisms that could change without a public compatibility contract.
It is not the kind of dependency we would build a serious platform on today.
The pattern is still right; the integration changed
What did survive perfectly was the idea of placing an LLM close to the execution environment.
A notebook is a particularly good place to do that because it combines three things:
code
+
data
+
execution state
A traditional text document only contains static information.
A notebook also contains a live environment.
For example:
import pandas as pd
df = pd.read_csv("sales.csv")
df.describe()
The important value is not only the code.
Also available are:
- the actual DataFrame columns;
- the data types;
- errors produced by Python;
- variables currently loaded;
- results of previous cells;
- generated charts;
- the accumulated state of the kernel.
For an AI model, that is much richer than simply receiving a .py file.
Option 1: ChatGPT already has a notebook behind it
There is an interesting irony in all of this.
While that tutorial tried to bring ChatGPT into Jupyter, today ChatGPT already uses a Jupyter environment for certain data-analysis tasks.
When a user uploads a CSV, Excel, JSON, or other compatible files and asks for analysis, ChatGPT can write and execute Python inside a stateful notebook environment.
The conceptual architecture looks like this:
User
↓
ChatGPT
↓
model
↓
Python / Jupyter
↓
file + pandas + calculations
↓
result
↓
model interprets result
This matters because it removes much of the friction from the original tutorial.
For many exploratory analyses, there is no longer a need to install an extension so the model can execute Python.
The product itself can do it.
But this solution has an important limitation: the notebook executed by ChatGPT is a managed environment, not necessarily your local notebook with all of your dependencies, private files, internal services, and custom kernels.
That is where local integrations become interesting again.
Option 2: Jupyter AI
One of the most natural solutions today is Jupyter AI, a project from the Jupyter ecosystem itself.
Jupyter AI integrates generative models directly into JupyterLab and supports multiple providers.
The experience can include:
JupyterLab
├── notebook
├── AI chat
├── file access
├── notebook context
└── external or local models
The modern version of Jupyter AI even includes concepts such as personas, notebook tools, and custom MCP servers.
That is already far beyond the simple browser extension from 2023.
We are entering the territory of agentic notebooks.
Magic commands
Jupyter AI also allows working directly from cells through IPython magic commands.
The corresponding extension can be installed with:
pip install jupyter-ai-magic-commands
Then:
%load_ext jupyter_ai_magic_commands
And available models can be discovered with:
%ai list
Or providers can be filtered:
%ai list openai
A cell can then become a prompt:
%%ai <provider>/<model>
Explain why this distribution has such a long tail.
The important detail is that we are no longer tied to a single ChatGPT interface.
Jupyter becomes the interaction layer and the model provider can change.
Option 3: call the OpenAI API directly
For an integration we control ourselves, we often do not even need an extension.
We can use the official OpenAI SDK directly from the notebook.
First install the package:
pip install openai
The API key should be configured as an environment variable, not written directly into the notebook:
export OPENAI_API_KEY="..."
Then, from Python:
from openai import OpenAI
client = OpenAI()
response = client.responses.create(
model="gpt-5.6-sol",
input="Explain what logistic regression does in intuitive terms."
)
print(response.output_text)
This architecture is much more stable than reusing the browser session:
Notebook
↓
official SDK
↓
Responses API
↓
model
It also lets us control exactly what information we send to the model.
But another possible interpretation of the old tutorial needs to be corrected here:
the API should not be assumed to be free.
OpenAI operates the API as a separate product with its own usage and billing. Promotional credits or special account conditions may exist, but a real application should be designed assuming that calls have a cost.
Option 4: genuinely free with local models
If the goal is to completely avoid per-call cost, there is a much cleaner alternative than hijacking a browser session:
run the model locally.
Jupyter AI supports local providers such as Ollama.
The architecture becomes:
JupyterLab
↓
Jupyter AI
↓
Ollama
↓
local model
↓
user CPU / GPU
In this case there is no external provider charging tokens for each call.
The cost shifts to hardware, electricity, memory, and compute time.
For experimentation, education, analysis of private data, or disconnected work, it can be a very attractive option.
It also has another advantage: some datasets never need to leave the machine.
From coding assistant to notebook agent
So far we have described a chatbot inside Jupyter.
But that is only the first level.
Imagine this flow:
User
↓
"find out why sales fell"
↓
LLM
↓
generates Python
↓
Jupyter executes
↓
result
↓
LLM inspects result
↓
generates a new query
↓
Jupyter executes again
Now we have a loop:
reason
↓
act
↓
observe
↓
reason again
That is essentially the anatomy of an agent.
The notebook stops being only an interactive notebook for humans.
It also becomes a tool runtime for the model.
A simple example
Suppose we have:
import pandas as pd
df = pd.read_csv("sales.csv")
A conventional assistant might receive something like:
I have a DataFrame with sales. What analysis should I do?
The model responds with recommendations.
A real agent can do something more interesting.
First it inspects:
df.columns
It gets:
date
product
region
units
price
Then it calculates:
df.groupby("region")["units"].sum()
It observes an anomaly.
Then it generates another query:
miami = df[df["region"] == "Miami"]
miami.groupby(miami["date"].str[:7])["units"].sum()
Finally, it might conclude something like:
The overall decline comes mainly from Miami and began in June.
Product X explains roughly two thirds of that reduction.
The difference is enormous.
In the first case, the model talks about the data.
In the second, it operates on it.
The notebook as a tool
We can abstract the idea even further.
From the model’s perspective, a Python kernel can be seen as a tool:
python_execute(code)
For example:
{
"code": "df.groupby('region')['sales'].sum()"
}
The tool returns:
Miami 125000
Orlando 82000
Tampa 67000
The model uses that result to decide the next step.
In a more advanced implementation we could expose several tools:
run_python(code)
read_dataframe(name)
inspect_variables()
render_chart(spec)
read_file(path)
query_database(sql)
The notebook stops being simply a graphical interface.
It becomes an environment for agents.
MCP takes this idea even further
Recent versions of Jupyter AI already contemplate integration with custom MCP servers.
That means the notebook can become a point where several capabilities converge:
model
↓
Jupyter AI
├── Python kernel
├── files
├── notebook
├── databases
└── MCP servers
├── GitHub
├── internal services
├── APIs
└── enterprise tools
We are moving from:
"ChatGPT inside Jupyter"
to:
"Jupyter as an operational workspace for agents"
That is a much more important conceptual evolution.
The problem with generated code
Giving a model execution access also introduces risks.
An LLM can generate:
import shutil
shutil.rmtree("/data")
Or install unexpected dependencies:
pip install unknown-package
Or try to send information to external services.
That is why a serious agentic notebook should think about isolation.
A reasonable architecture can include:
LLM
↓
policy / approval gate
↓
sandbox
↓
Python kernel
↓
restricted filesystem
Useful protections include:
- ephemeral kernels;
- containers;
- limited filesystem access;
- blocking network access when it is not needed;
- CPU and memory limits;
- human approval for sensitive actions;
- complete logging of executed code;
- environment snapshots;
- separation between analysis and production.
The same power that turns a notebook into an agent also increases the impact of a mistake.
An interesting detail about ChatGPT Data Analysis
Current OpenAI documentation explains that the Python environment used by ChatGPT for data analysis cannot make arbitrary external Internet requests.
That may look like a limitation, but it also illustrates an important architectural decision.
The code runtime is separated from open network access.
Conceptually:
model
↓
notebook sandbox
├── available files
├── pandas
├── Python
└── no arbitrary web access
Separating reasoning, tools, and privileges is exactly the kind of design we need when agents begin executing real code.
So what would I use today?
It depends on the goal.
I want to analyze a file quickly
I would use ChatGPT’s data-analysis capabilities directly.
file → ChatGPT → Python → analysis
It is the path with the least infrastructure.
I want AI integrated into my JupyterLab
I would use Jupyter AI.
JupyterLab → Jupyter AI → model provider
It is a native integration from the Jupyter ecosystem and allows providers to be changed.
I want to build my own workflow
I would use the official SDK and the Responses API.
notebook → Python SDK → model
That gives us full control over prompts, tools, state, and policies.
I want to avoid per-token cost
I would use a compatible local model, for example through Ollama.
Jupyter → Jupyter AI → Ollama → local model
I want to build a data agent
I would not think only about “integrating a chatbot.”
I would explicitly design the loop:
objective
↓
model
↓
generates action
↓
notebook executes
↓
result
↓
model evaluates
↓
next action
And I would place a sandbox around the runtime.
The lesson from the 2023 tutorial
The video remains interesting because it captures a specific moment in the evolution of LLMs.
At the time, connecting ChatGPT to Jupyter required lateral engineering:
browser extension
+
web session
+
injected JavaScript
Today we have much more formal building blocks:
official APIs
Jupyter AI
model providers
local LLMs
MCP
notebook tools
sandboxes
agent loops
But the tutorial got one fundamental thing right before all this infrastructure matured:
the natural place for an AI used for programming and analysis is not necessarily a separate chat window.
It can be the same environment where the code, data, and results live.
The full evolution can be summarized like this:
2023
ChatGPT as chatbot
↓
extension connects Jupyter
2026
Jupyter as agentic environment
↓
LLM + tools + code + data + execution
And that is probably the part of the old tutorial most worth preserving.
Not the technique for reusing a browser session.
But the intuition to place the model inside the execution loop.
Sources
- MSN, republished video: How to connect ChatGPT to Jupyter Notebooks for free
- Alex The Analyst, YouTube: How to Integrate ChatGPT in Jupyter Notebooks for Free!
- GitHub: jflam/chat-gpt-jupyter-extension
- OpenAI Help Center: Data analysis with ChatGPT
- OpenAI Platform: Developer quickstart
- Jupyter AI: User Guide
- Jupyter AI: Magic commands