A chart stuck in a pop-up window can’t go in a report. To save a Matplotlib graph as PNG, call savefig() with a .png filename before plt.show().
In this tutorial, we’ll cover the matplotlib save graph as png workflow from one file to a whole folder, including resolution, clipped legends and saving without a window.
Quick answer: the file extension picks the format, and dpi sets the sharpness:
fig.savefig("revenue.png", dpi=300, bbox_inches="tight")
I ran every script here on Matplotlib 3.11.2 on Python 3.12.5, so each output block is real.
What You Need Before You Start
You’ll need Python and Matplotlib, and our guide to installing Matplotlib covers setup. We’ll reuse one dataset, six months of made-up 2026 revenue for a Chicago coffee shop. Save it as shop_sales.py:
months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun"]
revenue = [18450, 17920, 21300, 22875, 24610, 26140]
target = [18000, 18500, 20000, 22000, 24000, 25000]
Step 1: Save a Matplotlib Graph with savefig
We use fig.savefig() because it always targets that figure, while plt.savefig() uses whichever figure is current.
import matplotlib.pyplot as plt
from PIL import Image
from shop_sales import months, revenue
fig, ax = plt.subplots(figsize=(7, 4))
ax.bar(months, revenue, color="#2a6f97")
ax.set_title("Chicago coffee shop revenue, 2026 (USD)")
fig.savefig("revenue.png")
print("Saved:", Image.open("revenue.png").size)
plt.show()
Output:
Saved: (700, 400)

revenue.png now holds.A 7 x 4 inch figure at the default 100 dpi gives a 700 x 400 pixel file. Change the extension and the same call writes other formats, as our post on how to save a Matplotlib graph as PDF shows.
Step 2: Set the PNG Resolution with dpi
Same chart, four resolutions. Pixel size is simply inches multiplied by dpi:
import matplotlib.pyplot as plt
from PIL import Image
from shop_sales import months, revenue
fig, ax = plt.subplots(figsize=(7, 4))
ax.bar(months, revenue, color="#2a6f97")
for dpi in (72, 100, 150, 300):
name = f"revenue_{dpi}.png"
fig.savefig(name, dpi=dpi)
print(dpi, "dpi ->", Image.open(name).size)
Output:
72 dpi -> (504, 288)
100 dpi -> (700, 400)
150 dpi -> (1050, 600)
300 dpi -> (2100, 1200)

We’d use 150 for web pages and 300 for print. If your data is already an image, skip the figure and save the NumPy array as a PNG.
Step 3: Keep Legends Inside the PNG with bbox_inches
Now we add a target line and move the legend outside the axes. A default save cuts it off, while bbox_inches="tight" resizes the output to fit everything:
import matplotlib.pyplot as plt
from PIL import Image
from shop_sales import months, revenue, target
fig, ax = plt.subplots(figsize=(7, 4))
ax.plot(months, revenue, marker="o", label="Revenue")
ax.plot(months, target, "--", label="Target")
ax.set_title("Revenue vs. target, 2026 (USD)")
ax.legend(bbox_to_anchor=(1.02, 1), loc="upper left")
fig.savefig("default.png")
fig.savefig("tight.png", bbox_inches="tight", pad_inches=0.2)
print("default:", Image.open("default.png").size)
print("tight: ", Image.open("tight.png").size)
Output:
default: (700, 400)
tight: (767, 393)

Only the tight file, at 767 x 393 pixels, holds the whole legend. pad_inches controls the remaining margin, and there’s more on removing it in our guide to a PNG without a border.
Step 4: Save Multiple Charts to a Folder Without a Window
Same revenue, one PNG per month. The Agg backend draws to files only, so this runs on servers and in scheduled jobs:
from pathlib import Path
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
from shop_sales import months, revenue
out = Path("monthly_charts")
out.mkdir(exist_ok=True)
for month, value in zip(months, revenue):
fig, ax = plt.subplots(figsize=(4, 3))
ax.bar([month], [value], color="#2a6f97")
fig.savefig(out / f"revenue_{month.lower()}.png", dpi=150)
plt.close(fig)
files = sorted(p.name for p in out.glob("*.png"))
print(len(files), "PNG files:", files[0], "...", files[-1])
print("Open figures:", len(plt.get_fignums()))
Output:
6 PNG files: revenue_apr.png ... revenue_may.png
Open figures: 0

plt.close(fig) matters in loops, because each open figure holds memory. To export one panel of a grid instead, see how to save a single axis as PNG.
Matplotlib savefig Options Cheat Sheet
| Goal | Argument |
|---|---|
| Sharper file | dpi=300 |
| Fit labels and legends | bbox_inches="tight" |
| See-through background | transparent=True |
| Write to memory | format="png" |
Transparency has its own catches, covered in saving a PNG with a transparent background.
Can You Save a Matplotlib Figure Without Creating a File?
Yes. Pass a BytesIO buffer and set format, since there’s no extension to read. That’s handy for web apps and email attachments:
from io import BytesIO
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.plot([1, 2, 3], [18450, 21300, 26140])
buffer = BytesIO()
fig.savefig(buffer, format="png")
print("PNG header:", buffer.getvalue()[1:4].decode())
Output:
PNG header: PNG
The buffer starts with the real PNG header, so it’s a complete image in memory.
Common Mistakes to Avoid When Saving PNG Files
Saving after show: once the window closes, the figure is gone. We reproduced it with the same sales data:
import matplotlib.pyplot as plt
from PIL import Image
from shop_sales import months, revenue
plt.bar(months, revenue)
plt.show()
plt.savefig("late.png") # too late: this is a new, empty figure
colors = Image.open("late.png").convert("RGB").getcolors()
print("Distinct colors:", len(colors))
Output:
Distinct colors: 1
One distinct color means a blank image. Our savefig blank image fix walks through the other causes.
A missing folder: savefig() won’t create directories for you:
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
try:
fig.savefig("reports/q2/revenue.png")
except FileNotFoundError as err:
print(type(err).__name__, "-", err.strerror)
Output:
FileNotFoundError - No such file or directory

mkdir.Next Steps After Exporting Your Charts
Next, bundle your charts into one report: learn to save multiple pages to a PDF.
We also recommend these export guides:
Frequently asked questions
How do I save a Matplotlib plot as a PNG?
Call fig.savefig("chart.png") or plt.savefig("chart.png") before plt.show().
Why is my saved PNG blank?
It’s usually because savefig() ran after plt.show(). Save first, then show.
What dpi should I use?
Use 150 for the web and 300 for print. Higher values make larger files.
How do I stop labels from being cut off?
Pass bbox_inches="tight" to savefig().
Can I save a chart without displaying it?
Yes. Skip plt.show(), and use the Agg backend on headless machines.
Where is savefig documented?
See the savefig reference in the official docs.

Bijay Kumar is a 13-time Microsoft MVP with more than 18 years in software development, and the founder of Python Guides and TSinfo Technologies. He started out building .NET and SharePoint solutions at HP, TCS and KPIT before moving into Python, machine learning and AI, and he also builds web apps with TypeScript and React. He writes the tutorials here himself, and every example is run before publishing so you see the real output. More about Bijay · Microsoft MVP profile · LinkedIn