Guide
How to Save a Pandas DataFrame to a CSV File
The whole operation is one method call on the DataFrame. Giveto_csv() a path, and the frame is written to disk as a CSV file:
df.to_csv("output.csv", index=False)That is how to save a Pandas DataFrame to CSV in the simplest case: the first argument is the destination, index=False keeps the row numbers out of the file, and everything else has a sensible default. The rest of this page covers the decisions that come next — where the file lands, what it contains, and how to confirm it is right.
The df.to_csv() call, argument by argument
The signature startsto_csv(path_or_buf=None, *, sep=",", na_rep="", float_format=None, columns=None, header=True, index=True, index_label=None, mode="w", …)— one positional destination, then keyword-only options. The destination accepts a filename string, a pathlib path, or an open file object. Omit it entirely and pandas stops writing files: the CSV text comes back as a str instead.
Not every argument gets an example below: the formatting ones — quoting,float_format, compression, lineterminator anddate_format — are worked through in theto_csv() options cookbook.
Two defaults shape every export. index=True writes the DataFrame's index as the first column, which is rarely what a shared CSV wants — seepandas to CSV without index for why and when to override it. header=True writes the column names as the first row, which is almost always what you want.
Saving to a file: filename and folder
A bare filename — "output.csv" — is written into the process's current working directory. Run the same script from a different folder and the file appears in a different place, which is the usual cause of "I saved it but cannot find it". Printos.getcwd() to see where that is, or use a path that does not depend on it:
df.to_csv("/home/me/exports/output.csv", index=False) # absolute
df.to_csv("exports/output.csv", index=False) # relative to the cwdCreating parent directories before saving: pandas creates the target file, but it never creates missing directories. Calling df.to_csv("reports/2026/sales.csv")when reports/2026/ does not exist immediately raises:
FileNotFoundError: [Errno 2] No such file or directory: 'reports/2026/sales.csv'The standard, modern Python pattern uses standard library pathlib.Path to ensure the destination folder exists before writing:
from pathlib import Path
import pandas as pd
df = pd.DataFrame({"product": ["A", "B"], "revenue": [1200, 3400]})
output_path = Path("reports/2026/q4/sales.csv")
# Create parent directory (and all ancestor folders) before saving
output_path.parent.mkdir(parents=True, exist_ok=True)
# to_csv accepts a Path object directly
df.to_csv(output_path, index=False)Three details make this snippet robust:
output_path.parenttargets the directory containing the file (reports/2026/q4), preventing you from accidentally creating a directory named after your CSV file.parents=Truebehaves likemkdir -pin Unix: it creates every missing intermediate folder in the hierarchy, not just the leaf directory.exist_ok=Trueprevents Python from raisingFileExistsErrorif the directory already exists, making the script safe to run repeatedly.
to_csv() natively accepts pathlib.Path instances, so there is no need to convert output_path back to a string.
Try the workflow in the converter
The tool below opens on the Pandas code tab with a small DataFrame literal loaded — the same frame the examples on this page use. It parses in the browser, shows the preview, and generates the df.to_csv() call for the current options. Download the CSV to inspect the file, or copy the code into your script.
Add your data
Advanced: paste a supported pd.DataFrame(...) expression.
Your data stays in your browser. Nothing is uploaded.
CSV options
to_csv() parameter, and the defaults are tuned for clean CSV output.More options— formatting, columns, quoting, line endings
Your CSV is ready
| name | age | city | score |
|---|---|---|---|
| Alice | 25 | Delhi | 91.5 |
| Bob | 30 | Mumbai | 87.25 |
| Charlie | 28 | Chandigarh | 94.75 |
Showing all 3 rows.
Long values are clipped to keep rows readable — hover to see full cell. The downloaded output.csv contains every value in full.
Pandas code
df.to_csv() call that reproduces these settings in Python.df.to_csv("output.csv", index=False)Assumes your DataFrame is named df and that pandas is already imported.
The file is assembled in this tab and the code is generated by the same page — nothing is uploaded.
Overwriting and appending
mode="w" is the default: writing to a path that already exists replaces it, in full, with no prompt and no backup. Nothing is merged. If you meant to add rows to an existing file instead, that is mode="a" — a different problem with its own trap around header rows, covered inhow to append to a CSV file with Pandas.
Encoding and delimiter for the target app
The default encoding is UTF-8, which is correct almost everywhere. The notable exception is Excel on Windows, which needs a byte-order mark to render non-ASCII characters —df.to_csv("output.csv", index=False, encoding="utf-8-sig") writes one. The default separator is a comma; locales that use commas as decimal separators commonly read semicolon-delimited files, written with sep=";" and read back the same way.
Saving only selected columns
columns takes the subset to write, in the order you list it — the DataFrame itself is untouched:
df.to_csv("output.csv", index=False, columns=["name", "city"])Saving only the first N rows
to_csv() has no nrows — that argument belongs toread_csv(), where it caps how much is read in. The writer has no equivalent: it writes whatever DataFrame it is given. To export a prefix, select the rows first and hand the result to to_csv():
df.head(100).to_csv("output.csv", index=False)head(n) takes the first n rows. For a slice that is not a prefix — rows 50 to 150, say — positional slicing does the same job and produces the same file for the same rows:
df.iloc[:100].to_csv("output.csv", index=False) # same as head(100)
df.iloc[50:150].to_csv("output.csv", index=False) # a slice by positionThe row count is just the number inside the selection, so exporting a different amount means changing 100 — there is no separate limit to keep in step. Both forms leave the DataFrame itself untouched; they write a smaller file, not a shorter frame.
Getting the CSV as text instead of a file
Leave the path out and the method returns the CSV text:csv_text = df.to_csv(index=False). Passing a path makes pandas write the file and return None — useful to remember before writingif df.to_csv(...):, which is falsy even when the write succeeded.
Verify the file you wrote
The cheapest check is a round trip through the reader:
df.to_csv("output.csv", index=False)
back = pd.read_csv("output.csv")
assert list(back.columns) == list(df.columns)
assert len(back) == len(df)For a one-off script, opening the file is enough — but the assert above catches the quiet failures: a stale file left behind because the script crashed before writing, a wrong delimiter, or an index column that snuck in because index=False was left off.
Common mistakes
- Letting the index ride along.
index=Trueis the pandas default; most shared files wantindex=False. Seethe without-index guide for the full picture. - Assuming the directory comes with the file. pandas raises
FileNotFoundErrorwhen the parent folder is missing — create it first. - Expecting to be asked before overwriting.
mode="w"truncates silently. Keep intermediate files under different names if you need history. - Saving over a file another program holds open. Windows locks it and the write fails with
PermissionError— close the workbook first. A read-only file or a folder without write access fails the same way; the FAQ above covers telling the causes apart. - Judging success by the return value.
Nonemeans it worked, not that it did not. Check the file or read it back. - Passing your own file handle without
newline="". When you supply an open file rather than a path, open it withnewline=""or line endings get translated twice.
Frequently asked questions
Which arguments does df.to_csv() actually need?
One: the path. df.to_csv("output.csv") writes the file with every other argument at its default. Everything else — index, header, separator, encoding — only appears in the call when you want to change what the defaults produce.
Where does the file get written?
Relative paths resolve against the current working directory of the Python process: the folder you launched the script from, or the notebook's folder. Print os.getcwd() to see it, or pass an absolute path such as "/home/me/exports/output.csv" to remove the ambiguity.
Why do I get FileNotFoundError when the filename is fine?
pandas creates the file, not the directory around it. df.to_csv("exports/output.csv") fails if exports/ does not exist. Create it first: from pathlib import Path; Path("exports").mkdir(parents=True, exist_ok=True), or os.makedirs("exports", exist_ok=True).
Why does writing fail with PermissionError?
PermissionError means the operating system refused the write, and the cause is not always the same. The common one on Windows is a file lock: Excel, an antivirus scanner, or a sync tool such as OneDrive holds output.csv open, so pandas cannot replace it. Close the file in whatever has it, or write to a temporary name and move it into place afterwards. The other frequent cause is filesystem permissions — the account running the script may not have write access to the folder, or the existing file may be marked read-only. There is no single fix: identify which of the two you have before changing code.
Did my file actually get written? How do I check?
Not by the return value: to_csv() returns None on success, so "if df.to_csv(...) is truthy" is never true. Check the file itself — Path("output.csv").exists(), or read it back with pd.read_csv("output.csv") and compare the columns you expect.
Do I need to close the file after to_csv()?
No, when you pass a path. pandas opens and closes the file itself. Only a handle you opened yourself needs closing — and if you pass a file object, open it with newline="" so line endings are not translated twice.
Specialized guides
The two problems that come up next have their own pages, each with a converter already configured for the task:
- Pandas to CSV without index —
index=False,reset_index(),index_label, and the round trip throughread_csv(). - Append to a CSV file with Pandas —
mode="a"versusmode="w", and why the header must be turned off on every append after the first. - The full to_csv() options table — every argument the converter supports, with an example value for each.
Reviewed against the pandas 3.0.6 DataFrame.to_csv() reference — 6 October 2026.