Quick answer: DataFrame.to_csv() writes tabular data to a CSV path or file-like object. Reliable exports make the index, selected columns, delimiter, encoding, missing-value representation, and line endings deliberate. CSV is a text interchange format, so the writer’s choices must match the program that will read the file.

DataFrame.to_csv() is the pandas method for exporting a DataFrame to a CSV file or to a CSV-formatted string. CSV files are plain text, easy to share, and supported by spreadsheets, databases, notebooks, and many data tools.
The important details are choosing whether to write the row index, where to save the file, how to represent missing values, and whether you need to append to an existing CSV. The examples below were tested with pandas 2.2.3 and follow the current pandas DataFrame.to_csv documentation.
Basic pandas to_csv example
If you call to_csv() without a file path, pandas returns the CSV text as a string. This is useful for learning the output format or sending data to another service without creating a file first.
<div class="pythonpool-code-scroll" style="max-width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;">import pandas as pd
df = pd.DataFrame({
"Name": ["David", "Robert"],
"Age": [20, 30],
"City": ["Seattle", "Chicago"],
})
csv_text = df.to_csv(index=False)
print(csv_text)</div>
Name,Age,City David,20,Seattle Robert,30,Chicago
The index=False argument is usually what beginners want. Without it, pandas writes the DataFrame index as the first CSV column. That index is useful in some data workflows, but it often creates an unwanted blank-looking column when the file is opened in a spreadsheet.
Save a DataFrame to a CSV file
To save a real file, pass a file path as the first argument. A pathlib.Path object works well because it keeps path handling explicit and avoids many filename mistakes.
<div class="pythonpool-code-scroll" style="max-width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;">from pathlib import Path
import pandas as pd
df = pd.DataFrame({
"Name": ["David", "Robert"],
"Age": [20, 30],
"City": ["Seattle", "Chicago"],
})
output_path = Path("people.csv")
df.to_csv(output_path, index=False)</div>
If the file is not created where you expect, print the resolved path or check the current working directory. The same path debugging rules from our Python rename file guide apply here too.
Select columns and handle missing values
You do not always need every DataFrame column in the exported CSV. Use columns to control the output order and na_rep to choose the text written for missing values.
<div class="pythonpool-code-scroll" style="max-width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;">summary = pd.DataFrame({
"Name": ["David", "Robert"],
"Score": [92.5, None],
"Internal ID": [101, 102],
})
summary.to_csv(
"scores.csv",
index=False,
columns=["Name", "Score"],
na_rep="NA",
)</div>
This writes only the Name and Score columns. The missing score becomes NA instead of an empty field, which makes the CSV easier to review and safer for tools that treat empty values differently.

Append rows to an existing CSV
Use mode="a" to append rows to an existing file. When appending, set header=False after the first write so the column names are not repeated in the middle of the CSV.
<div class="pythonpool-code-scroll" style="max-width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;">first = pd.DataFrame({"Name": ["Ana"], "Score": [88]})
second = pd.DataFrame({"Name": ["Bo"], "Score": [91]})
first.to_csv("scores.csv", index=False)
second.to_csv("scores.csv", mode="a", header=False, index=False)</div>
Appending is convenient for small logs and batch exports. For larger pipelines, consider writing separate files per batch and combining them later, because repeated appends can make error recovery harder.
Common to_csv arguments
path_or_buf: file path, buffer, orNoneto return a string.index: writes or skips the DataFrame index.columns: exports only selected columns in the given order.sep: changes the delimiter, such as" "for tab-separated output.na_rep: text to use for missing values.encoding: text encoding, commonly"utf-8"or"utf-8-sig"for some spreadsheet workflows.mode: write mode, such as"w"for overwrite or"a"for append.
Verify the exported CSV
After writing an important CSV, read it back with pandas.read_csv() and check the shape, column names, and a few sample rows. This catches mistakes such as duplicated headers, wrong delimiters, unexpected indexes, and missing-value formatting before the file reaches someone else.
<div class="pythonpool-code-scroll" style="max-width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;">saved = pd.read_csv("people.csv")
print(saved.shape)
print(saved.columns.tolist())</div>

When should you keep the index?
Use index=False when the index is just pandas’ default row numbering. Keep the index when it has real meaning, such as a timestamp, product ID, or user ID that identifies each row. If the index should become a named CSV column, call reset_index() first or use index_label so the exported file has a clear header.
This decision affects how the file will be imported later. A CSV with an unnamed index column often looks fine at first, but it can create duplicate row numbers or confusing extra columns when another person opens it in Excel, Google Sheets, or a database import tool.
CSV is not the same as Excel
A CSV file stores text rows and delimiters. It does not preserve formulas, cell colors, filters, merged cells, charts, or multiple worksheets. If you need spreadsheet formatting, export to an Excel file instead. Use CSV when portability matters more than presentation, especially for APIs, command-line tools, version control, and repeatable data processing.

Common pandas to_csv errors
If to_csv() raises a file path error, confirm that the parent folder exists and that the script has permission to write there. If the output contains strange characters, specify an encoding explicitly. If the file has repeated header rows, check whether append mode is being used without header=False. If numbers or dates look different after opening the file in a spreadsheet, inspect the raw CSV text before assuming pandas wrote the wrong values.
Related Python guides
- Python csv DictReader
- Read a file line by line in Python
- Read TSV files in Python
- Read CSV files with NumPy
- Python open() for file handling
- Fix path-like object errors
Official references
- pandas DataFrame.to_csv
- pandas read_csv
- pandas CSV and text files guide
- Python pathlib
- Python csv module
Choose The Destination And Index
Pass a path for a simple export or a file-like object when the surrounding program owns the stream. The index is not automatically a data column in every workflow, so set index=False when it is only pandas bookkeeping. Preserve it when it identifies records and give it a name.
import pandas as pd
frame = pd.DataFrame({"name": ["Ada", "Grace"], "score": [95, 98]})
frame.to_csv("scores.csv", index=False)
frame.to_csv("scores-with-index.csv", index=True, index_label="row_id")
Export Columns And Control The Separator
Use columns to define a stable output contract rather than exporting whatever order happens to be present. A comma is conventional, but tab-separated files and regional spreadsheet settings may require a different separator. Validate requested columns before writing so a renamed column fails clearly.
columns = ["name", "score"]
missing = set(columns) - set(frame.columns)
if missing:
raise KeyError(f"missing columns: {sorted(missing)}")
frame.to_csv("selected.tsv", columns=columns, sep="\t", index=False)Handle Encoding, Missing Values, And Dates
CSV has no native type system. Choose an encoding for the receiving application, decide how missing values should be represented, and format dates intentionally when a downstream system expects a fixed layout. Do not rely on a spreadsheet’s automatic type guesses for identifiers with leading zeroes.
frame = pd.DataFrame({"code": ["001", None], "created": pd.to_datetime(["2026-01-01", "2026-01-02"])})
frame.to_csv(
"export.csv",
index=False,
encoding="utf-8",
na_rep="",
date_format="%Y-%m-%d",
)Export Large DataFrames Carefully
For a large frame, keep the output format stable and consider compression, chunks, or a more typed format when CSV is not a requirement. A CSV export should be tested by reading a sample back with the same separator and encoding, then comparing expected columns and row counts.
frame.to_csv("scores.csv.gz", index=False, compression="gzip")
round_trip = pd.read_csv("scores.csv.gz", compression="gzip")
assert list(round_trip.columns) == list(frame.columns)
print(len(round_trip))The official pandas DataFrame.to_csv() reference documents the complete parameter set. Treat the output options as part of an interface contract, especially when another service or spreadsheet consumes the file.
For related tabular exports, compare Python CSV reading, TSV parsing, and tabulating structured data when defining the format another program will consume.
Frequently Asked Questions
How do I save a pandas DataFrame as CSV?
Call df.to_csv() with a path or file-like object, then choose index, columns, encoding, and separator settings that match the receiving system.
How do I remove the pandas index from a CSV?
Pass index=False when the index is not a meaningful output column; otherwise preserve it deliberately and give it a clear name.
How do I export selected columns with pandas?
Pass a list of column names through the columns argument and validate that each requested column exists before exporting.
Why does a CSV look wrong after to_csv()?
Check delimiter, quoting, encoding, line endings, index output, and the program used to open the file rather than assuming the DataFrame changed.