Library / SDK
kz26/PyExcelerate avatar
kz26/PyExcelerate

PyExcelerate: writing large XLSX files quickly from Python

Accelerated Excel XLSX Writing Library for Python 2/3

561 stars65 forksPythonBSD-2-Clause

At a glance

What is it?
PyExcelerate is a BSD licensed Python library for writing Excel-compatible xlsx files with an emphasis on speed, offering three tiers of API from bulk data writes to individual styled cells, with xlsxwriter and openpyxl as the comparison points. Its own documentation is honest that styling is where the performance goes, and it still supports Python 2.7.
Who is it for?
Adopt PyExcelerate if you are writing large spreadsheets and have measured that cell-by-cell writing is the bottleneck, since the library's whole argument is speed on that path. Do not adopt it for reading spreadsheets, which it does not do, and do not adopt it without reading the styling warning, because styling can multiply execution time many times over.
Can I use it commercially?
Yes. BSD-2-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 10 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 24, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What the library does, and the two-tier speed argument

PyExcelerate is a Python library for writing Excel-compatible XLSX spreadsheet files, with an emphasis on speed. The authors are Kevin Wang and Kevin Zhang, the copyright runs to 2015 with portions copyright Google, Inc., and the licence is the Simplified BSD License, which the project records as BSD-2-Clause.

The interesting design is that the library offers three ways to write, and each is labelled by how fast it is, which is an unusual but honest way to document an API. You can write bulk data directly into a new sheet, you can write into a range, or you can write cell by cell. The further down that list you go, the more control you have and the slower it gets.

Writing bulk data is the fastest path, because the whole two dimensional array is handed over at once:

python
from pyexcelerate import Workbook

data = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] # data is a 2D array

wb = Workbook()
wb.new_sheet("sheet name", data=data)
wb.save("output.xlsx")

Writing to a range is described as faster than writing cell by cell, which is what you would expect, and the API is the same object with the range assigned as a value:

python
from pyexcelerate import Workbook

wb = Workbook()
ws = wb.new_sheet("test")
ws.range("B2", "C3").value = [[1, 2], [3, 4]]
wb.save("output.xlsx")

The cell-by-cell path is the slowest but the most conventional, and there are two spellings of it, one using set_cell_value with row and column numbers and one using direct indexing:

python
from datetime import datetime
from pyexcelerate import Workbook

wb = Workbook()
ws = wb.new_sheet("sheet name")
ws[1][1].value = 15 # a number
ws[1][2].value = 20
ws[1][3].value = "=SUM(A1,B1)" # a formula
ws[1][4].value = datetime.now() # a date
wb.save("output.xlsx")

Formulas and datetimes are both written as ordinary values, which matters because you are producing a spreadsheet rather than a data dump.

The benchmark table, and how much of it is still true

The README includes a benchmark table and is upfront about when it was measured. The benchmark code is in pyexcelerate/tests/benchmark.py, and the environment is given as Ubuntu 12.04 LTS with a Core i5-3450, 8GB of DDR3 and Python 2.7.3.

The table compares 1000 rows by 100 columns across a set of scenarios, and the pattern is consistent. With no styling, pyexcelerate in its fastest mode takes 0.47 seconds against xlsxwriter at 0.84 and openpyxl at 2.74. With styling, pyexcelerate's fastest mode takes 2.40 seconds against xlsxwriter at 4.85 and openpyxl at 6.32.

There is a footnote that explains the strange row names: cheating refers to pregeneration of styles. The pregenerated variants are faster because the style definitions are computed once rather than per cell, and naming them that way is a small piece of honesty about where the speed comes from.

The numbers are from a machine and a Python version that are both about a decade old, so the absolute figures should not be taken as current. What is more durable is the shape of the result, which is that writing values is fast in all three libraries and that styling is what separates them. A reader deciding between libraries today should measure on their own data, but this table at least tells you which dimension to vary.

The order of magnitude that matters for planning is between the value-only and styled runs. For pyexcelerate, going from the fastest value mode to the fastest style mode multiplies the time by about five, which is a large enough factor that it changes how you would structure a job that has to write a million styled rows.

What it does not do, and how the project is packaged

Two boundaries are worth stating before you pick this over openpyxl or xlsxwriter.

First, PyExcelerate writes and does not read. The description, the features and every example in the README are about producing a file. If you need to read an existing workbook back into Python, this is not the library for it, and the fact that its name sits next to openpyxl in the benchmark table is about output speed rather than feature parity.

Second, it targets the xlsx format only, and it targets the file format rather than a spreadsheet model. There is no concept of a workbook with formulas evaluated, no calculation engine, and no support for the parts of the format that a rich document would use. The value proposition is a fast, small, dependable way to get a table of numbers into a file that Excel opens correctly.

The packaging tells you about the project's maturity honestly. The version in setup.py is 0.13.0 while the released versions are 0.13.1 on 2026-08-05, 0.12.0 on 2024-06-10 and 0.11.0 on 2024-04-15, so the release line moves slowly and the last two releases are close together while the one before them is two years earlier. The default branch is dev, and the homepage is the PyPI project page, which is where most people will find it.

The repository itself is a small, conventional Python package. There is a pyexcelerate/ directory with the library, a setup.py, a requirements.txt listing jinja2 and six, a MANIFEST.in, a Pipfile and Pipfile.lock, a .travis.yml for continuous integration, a .coveragerc, a CHANGELOG.md and a hook-pyexcelerate.Writer.py.

That hook file is the most interesting artefact in the root. A writer hook is a registration point, and its presence suggests the library is meant to be plugged into a larger document generation pipeline that lets you swap the writer used for spreadsheet output. The README also links to a note on compatibility for Pandas DataFrames, which tells you the intended data entry point is a DataFrame even though the examples pass plain lists.

So the shape is: a small, fast, write-only xlsx writer with a Jinja2 template engine underneath, a Python 2 and 3 compatibility shim, and a benchmark history that goes back to a 2013 machine.

Styling, and the warning that comes with it

The styling section opens with the caveat that matters more than anything else in the README. Styling cells causes non-negligible overhead, it will increase your execution time, up to 10 times longer if done improperly, and you should only style cells if absolutely necessary.

The styling API has three tiers too, and they differ in more than syntax. The fastest builds a Style object and passes it to set_cell_style:

python
from pyexcelerate import Workbook, Color, Style, Font, Fill, Format
from datetime import datetime

wb = Workbook()
ws = wb.new_sheet("sheet name")
ws.set_cell_value(1, 1, 1)
ws.set_cell_style(1, 1, Style(font=Font(bold=True)))
ws.set_cell_style(1, 1, Style(fill=Fill(background=Color(255,0,0,0))))
ws.set_cell_value(1, 2, datetime.now())
ws.set_cell_style(1, 1, Style(format=Format('mm/dd/yy')))
wb.save("output.xlsx")

The middle tier fetches the style object and mutates it, and the slowest assigns attributes on the cell's style directly. The performance difference between them is not large.

The gotcha is documented right after the first example: subsequent calls to set_cell_style override the previous styles and they are not merged in. To have a combined style you create a single Style object with multiple properties, for example a Style with both a font and a format in one call. Anyone who reads only the fast example and then adds a second call will find their bold heading has lost its fill, so it is worth knowing before you write the code rather than after.

There is a similar trap with ranges and rows. Styling a range is one call over the whole area, which is what makes it cheap, and styling a row is described as the simpler and faster way to style an entire row, with a set_row_style method that takes a row number and a Style.

One small oddity is flagged too: the repeated .style.format.format is due to planned support for conditional formatting and other related features, and the formatting syntax may be improved in the future.

Naming cells, merging, and the library's shape underneath

Beyond the write paths, the API covers the operations you need once you are producing a real file.

Cells can be selected by name rather than by row and column number, which is how you address a spreadsheet:

python
from pyexcelerate import Workbook

wb = Workbook()
ws = wb.new_sheet("sheet name")
ws.cell("A1").value = 12
wb.save("output.xlsx")

And cells can be merged, using the same range object:

python
from pyexcelerate import Workbook

wb = Workbook()
ws = wb.new_sheet("sheet name")
ws[1][1].value = 15
ws.range("A1", "B1").merge()
wb.save("output.xlsx")

The setup.py explains how the file is produced, and it is a design point rather than an implementation detail. The install requirements are Jinja2 and six, and the package data lists XML templates under templates/ for the xlsx parts: the package relationships file, the docProps XML, the workbook XML, the sheet XML and the worksheet XML directories. The library is essentially a template renderer that fills in those XML parts, and Jinja2 is doing the templating. That is why the package data matters: the templates are part of the distribution, not generated at install time.

The six dependency is the giveaway about the project's era. It is the compatibility shim for writing code that runs on both Python 2 and Python 3, and it is why the library supports Python 2.7. The README states support for Python 2.7, 3.4, 3.5, 3.6, 3.7 and 3.8, and the setup.py classifiers list exactly the same set.

The repository root matches. There is a Pipfile and Pipfile.lock alongside a requirements.txt listing jinja2 and six, a setup.py, a MANIFEST.in, a .travis.yml for continuous integration, a .coveragerc with the coverage badge in the README, and a hook-pyexcelerate.Writer.py file. That last one is worth noting, because a writer hook is how a library like this would be integrated with a document generation pipeline that wants a different writer for spreadsheets.

Editorial conclusion

Adopt PyExcelerate if you are writing large spreadsheets and have measured that cell-by-cell writing is the bottleneck, since the library's whole argument is speed on that path. Do not adopt it for reading spreadsheets, which it does not do, and do not adopt it without reading the styling warning, because styling can multiply execution time many times over. Before you upgrade, check the Python version support in the setup metadata, since the classifiers still list 2.7 through 3.8 while the project is published as version 0.13, and note that the benchmark figures in the README come from a 2013-era machine and Python 2.7.

Frequently asked questions

What does PyExcelerate do?

It is a Python library for writing Excel-compatible XLSX spreadsheet files with an emphasis on speed. It offers three ways to write, bulk data into a new sheet, writing to a range, and writing cell by cell, with styling, merging and selecting cells by name supported on top.

How do I install PyExcelerate?

With pip install pyexcelerate. The README states support for Python 2.7, 3.4, 3.5, 3.6, 3.7 and 3.8, and the setup classifiers list the same set of versions.

How does PyExcelerate compare with xlsxwriter and openpyxl?

In the benchmark in the README, on 1000 rows by 100 columns with a Core i5-3450 running Ubuntu 12.04 and Python 2.7.3, the fastest pyexcelerate value mode took 0.47 seconds against 0.84 for xlsxwriter and 2.74 for openpyxl, and with styling the figures were 2.40, 4.85 and 6.32 seconds respectively.

What is the performance warning when styling cells with PyExcelerate?

Styling causes non-negligible overhead and will increase execution time, up to 10 times longer if done improperly, so cells should only be styled if absolutely necessary. There are also three styling APIs, from building a Style object and passing it to set_cell_style through to assigning attributes on the cell style directly.

Why do repeated set_cell_style calls in PyExcelerate lose earlier styles?

Because subsequent calls to set_cell_style override the previous styles and they are not merged in. To get a combined style you create a single Style object with multiple properties, such as Style(font=Font(bold=True), format=Format('mm/dd/yy')).

Official sources

  1. kz26/PyExcelerate on GitHub
  2. License: BSD-2-Clause
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/kz26-pyexcelerate.svg)](https://hysenlabs.com/projects/kz26-pyexcelerate)

Was this useful?