# clickhouse-driver: a native Python client for ClickHouse

> The clickhouse-driver package speaks the ClickHouse native TCP protocol rather than the HTTP interface, and it offers both a direct Client API and a DB API 2.0 layer. Several hot paths are Cython extensions, and the type coverage follows ClickHouse's own type system closely.

**mymarilyn/clickhouse-driver** — ClickHouse Python Driver with native interface support

- Repository: https://github.com/mymarilyn/clickhouse-driver
- Website: https://clickhouse-driver.readthedocs.io
- Stars: 1,307 · Forks: 248
- Language: Python
- License: NOASSERTION
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/mymarilyn-clickhouse-driver

## Native TCP, and why the choice matters

clickhouse-driver is a ClickHouse Python Driver with native, or TCP, interface support, and the word native is the whole design decision.

ClickHouse can be reached over two interfaces. The HTTP one is simple, works through any HTTP stack, and is what most tooling uses. The native one is a binary protocol that the server speaks directly, and it is faster and more expressive at the type level. A driver that only speaks HTTP is a client for a query interface. A driver that speaks the native protocol is a client for the database.

The package layout shows how seriously the native path is taken. There are four compiled extensions, built from the .pyx sources when Cython is available and from .c sources otherwise. They are bufferedreader and bufferedwriter, plus largeint and varint, which are the ClickHouse integer widths that do not fit naturally in Python's number types. The varint one in particular is the encoding ClickHouse uses for its variable-length integers, and implementing it in C rather than in Python is the difference between a protocol implementation and a protocol binding.

The setup script treats Cython as optional rather than required. If the Cython import fails, USE_CYTHON is set to False and the extensions are built from the pre-generated C, so the package installs on a machine with no compiler for Cython. When Cython is present, the extensions are run through cythonize with language_level 3, and a CYTHON_TRACE environment variable turns on line tracing, which is the kind of debugging aid you only add if you have hit a bug you cannot otherwise locate.

The package is on PyPI as clickhouse-driver, the home page is clickhouse-driver.readthedocs.io, the last push to the repository was on 2026-07-22, and the most recent release is 0.2.11 from 2026-07-17. The repository metadata reports a custom licence that GitHub cannot classify, while the README states the driver is distributed under the MIT license.

## The type list is the feature list

The supported types section is long, and reading it tells you what the driver is for.

There is Float32 and 64. There are the unsigned and signed integers at 8, 16, 32, 64, 128, and 256 bits, which covers ClickHouse's large integer types. Dates come as Date, Date32, DateTime with a timezone, and DateTime64 with a timezone. Strings are String and FixedString(N). There are Enum8 and Enum16, Array(T), Nullable(T), Bool, UUID, Decimal, IPv4 and IPv6, LowCardinality(T), SimpleAggregateFunction(F, T), Tuple with several types, Nested, Map with a key and a value, and JSON.

That last group is the interesting one. LowCardinality is a ClickHouse storage optimisation for columns with few distinct values, SimpleAggregateFunction is a column type that stores pre-aggregated states, and Nested is the column-oriented layout for what other databases call a table of rows. A driver that supports these as distinct types, rather than flattening them into strings or dictionaries, can read them back in a way that round-trips.

The same applies to Decimal and the wide integers. A generic driver maps a Decimal to a float, and a 256-bit integer to a string, and you lose precision or lose the ability to do arithmetic on it. Here the driver keeps the distinction, which is what makes it usable for a warehouse where those columns matter.

The rest of the feature list is about the protocol rather than the data. There is external data for query processing, query settings, compression support, and TLS support. There is query progress information, block by block results streaming, and reading of the query profile. There is receiving of server logs, which matters when you are debugging a server-side failure from the client side. There is multiple hosts support, and optional NumPy arrays support.

## Two ways to talk to the server

There are two ways to communicate with the server, and the README gives an example of each.

The pure Client example is the direct interface. You create a Client, and every operation is a method call that returns its result.

```python
from clickhouse_driver import Client

client = Client('localhost')

client.execute('SHOW TABLES')
# [('test',)]
```

The interesting part is the insert. The execute call takes a query and a list of rows, and a row can be a dictionary, in which case the column names are the keys, or a plain list, in which case the columns are in the order the query names them.

```python
client.execute(
    'INSERT INTO test (x) VALUES',
    [{'x': 100}]
)
# 1
```

The driver also supports parameter substitution with the %(name)s style, which is what the SELECT example does:

```python
client.execute(
    'INSERT INTO test (x) '
    'SELECT * FROM system.numbers LIMIT %(limit)s',
    {'limit': 3}
)
```

The DB API example is the alternative, and it is a genuine DB API 2.0 implementation rather than a thin shim. You call connect with a URL, get a cursor, and the cursor has execute, fetchall, executemany, and rowcount.

```python
from clickhouse_driver import connect

conn = connect('clickhouse://localhost')
cursor = conn.cursor()

cursor.execute('SHOW TABLES')
cursor.fetchall()
# [('test',)]
```

The difference in practice is the return value. The pure Client's execute returns the rows directly, while the DB API cursor exposes the row count as rowcount and you fetch separately. The rowcount semantics are the DB API ones, so an INSERT reports one row and the SELECT with the system.numbers insert reports zero, which is the count of affected rows rather than of returned rows.

The URL form for connect, clickhouse://localhost, is worth noting, because it means the same connection configuration can be expressed as a string, which is what you want when the settings come from an environment variable.

## The database API escape hatch, and the async sibling

Two things are worth pointing out that are easy to miss.

The first is that an asynchronous wrapper is a separate project. The README says the asynchronous wrapper is available at the aioch repository, under the same mymarilyn organisation. That is a deliberate split, since an async driver has a different architecture, a different connection model, and a different set of trade-offs, and folding it into the synchronous package would have complicated both.

The second is that the driver supports the DB API 2.0 specification, and that this is a real implementation rather than a naming convention. The documentation mentions the specification support as a feature in its own right, alongside multiple hosts support, which suggests the cursor and connection objects follow the interface closely enough to be usable with tooling that expects it.

The practical value of the DB API layer is that anything written against DB API 2.0 can talk to ClickHouse without modification. That includes report generators, ORM-style query builders that target DB API, and internal tools that abstract the database behind a standard interface. For a team that already has a house style of writing against DB API, this driver slots in.

The connection configuration is also worth a note. The pure Client takes a host string, while connect takes a URL, so there are two configuration styles in the same package. The URL style carries the scheme, host, and credentials in one string, which is convenient when the connection details come from configuration rather than from code.

## The repository layout, and what a driver project has to carry

The top level entries show the standard furniture of a mature Python driver, and each piece has a reason to exist.

There is the clickhouse_driver/ package itself, tests/ and a separate testsrequire.py for the test requirements, a docs/ directory with the readthedocs configuration in .readthedocs.yaml, and a perf/ directory for benchmarks. There is a .coveragerc and a coverage_excludes.py, which is the shape of a project that measures its own coverage and needs to exclude the parts that are not meaningfully testable. The flake8 configuration and the setup.cfg sit alongside them.

The valgrind.supp file is the one that tells you most about how the project is developed. A suppression file for Valgrind is a list of known false positives, and keeping one is a sign that the native extension code is run under memory checking regularly, which is the only way to be confident about hand-written C in a database protocol implementation.

The setup.py handles the Cython-optional build described earlier, and the CHANGELOG.md and CONTRIBUTING.rst are the ordinary release hygiene. The .github/ directory holds the CI workflow whose badge appears at the top of the README.

The performance directory deserves a mention because a driver that speaks a native binary protocol has a throughput ceiling, and a perf directory is how you find out where that ceiling is. It is also how you notice that a change to the buffered reader has made inserts slower, which is the kind of regression that never shows up in a correctness test.

## Conclusion

Adopt clickhouse-driver if you want to insert into ClickHouse at native protocol speed rather than through the HTTP interface, and if you need ClickHouse's own type semantics rather than a generic SQL dialect. Do not adopt it if your workload is a small number of large analytical queries, where the HTTP interface is simpler to operate. Verify first that the type list covers the columns you plan to use, since the driver implements ClickHouse types directly rather than mapping them to generic Python ones.

## FAQ

### What is the difference between the pure Client and the DB API in clickhouse-driver?

The pure Client returns results directly from execute, while the DB API layer, reached through connect, uses a cursor where rowcount reports the affected rows and you call fetchall or fetchone. The DB API path follows the Python DB API 2.0 specification, so code written against that interface can talk to ClickHouse unchanged.

### Which ClickHouse types does clickhouse-driver support?

Float32 and 64, signed and unsigned integers from 8 to 256 bits, Date, Date32, DateTime and DateTime64 with timezones, String and FixedString(N), Enum8 and Enum16, Array(T), Nullable(T), Bool, UUID, Decimal, IPv4 and IPv6, LowCardinality(T), SimpleAggregateFunction(F, T), Tuple, Nested, Map, and JSON.

### Does clickhouse-driver need Cython to install?

No. The setup script tries to import Cython and falls back to building the extensions from the pre-generated C sources if the import fails, so the package installs without a Cython compiler. When Cython is present, the extensions are cythonized, and a CYTHON_TRACE environment variable enables line tracing.

### Is there an asynchronous version of clickhouse-driver?

Yes, but it is a separate project. The README points to the asynchronous wrapper in the aioch repository under the same organisation, rather than shipping an async API in the same package.

### How are rows passed to an INSERT in clickhouse-driver?

A row can be a dictionary, where the keys are the column names, or a plain list, where the columns are in the order the query names them. The execute call returns the number of rows inserted, and the driver also supports %(name)s parameter substitution for queries.

## Sources

- [Issues](https://github.com/mymarilyn/clickhouse-driver/issues)
- [mymarilyn/clickhouse-driver on GitHub](https://github.com/mymarilyn/clickhouse-driver)
- [Project website](https://clickhouse-driver.readthedocs.io)
- [README](https://github.com/mymarilyn/clickhouse-driver/blob/master/README.md)
- [Releases](https://github.com/mymarilyn/clickhouse-driver/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/mymarilyn-clickhouse-driver
