# python-rapidjson wraps a C++ parser but refuses to be a drop-in for the json module

> python-rapidjson is a Python 3 extension module around the RapidJSON C++ library, adding JSON Schema validation, relaxed JSONC parsing and chunked dumping. It is fast by design and deliberately not API-identical to the standard library.

**python-rapidjson/python-rapidjson** — Python wrapper around rapidjson

- Repository: https://github.com/python-rapidjson/python-rapidjson
- Stars: 533 · Forks: 57
- Language: C++
- License: NOASSERTION
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/python-rapidjson-python-rapidjson

## A C++ parser with a Python surface, and one deliberate refusal

python-rapidjson exists to put RapidJSON, described as an extremely fast C++ JSON parser and serialization library, behind a Python 3 extension module. The wrapper exposes serialization and deserialization to and from bytes, str or file-like instances, and it adds JSON Schema validation on top.

The interesting part of the design is the refusal. The documentation states that although the maintainers tried to implement an API similar to the standard library json module, being a strict drop-in replacement is not their goal, and that they decided to depart from it in some aspects. There is a dedicated section in the online documentation for those incompatibilities, and the project points readers there rather than trying to hide the differences.

That framing matters for anyone choosing a JSON library. The stdlib json module is the default answer for most Python code, and a wrapper only earns its place if it buys something. Here the trade is explicit: native speed and a schema validator in exchange for behaviour that does not always match the standard library. The README also notes that most functionality is exposed both as functions and as classes, so you can pick the calling style that suits the call site.

The package is a compiled extension rather than a pure Python library, and the repository is laid out accordingly: a rapidjson.cpp binding source, a rapidjson directory holding the vendored library, a setup.py, a tests directory, a benchmarks directory and a typings directory for type stubs.

## Why the setup script refuses to build on anything older than Python 3.10

Before any of the API questions, there is a hard floor. setup.py starts with a version check that raises NotImplementedError with the message "Only Python 3.10+ is supported." The same file pins the package version to 1.25, with a comment saying the value is automatically updated by bump-my-version at release time.

The build also depends on the vendored RapidJSON sources. The extension options in setup.py list the sources as ./rapidjson.cpp, set the include directory to ./rapidjson/include, and define a macro carrying the package version. If that include directory is missing, setup.py raises a RuntimeError explaining that the RapidJSON sources were not found and that you should initialize the git submodule.

That is the whole build story, and it is a two-layer one. RapidJSON arrives as a git submodule inside the rapidjson directory, and the Python binding compiles on top of it. A second layer of detail appears in the Makefile, which has a target that regenerates rapidjson_exact_version.txt from the submodule's git describe output, and setup.py folds that file's contents into the build as an extra macro when it exists. The intent is that the built module can report the exact upstream version it was compiled against.

So installing from a source checkout is not the same as installing from a wheel. The wheel carries a prebuilt extension; the checkout needs the submodule populated first.

## Installing from PyPI, from conda-forge, or from a recursive clone

The quickest path is a wheel from PyPI, and the documentation gives it as a one-liner:

```bash
    $ pip install python-rapidjson
```

There is a conda-forge channel route as well, for environments where pip is not how you manage dependencies:

```bash
    $ conda install -c conda-forge python-rapidjson
```

For a development checkout, the clone must be recursive, because the RapidJSON sources come in as a submodule:

```bash
    $ git clone --recursive https://github.com/python-rapidjson/python-rapidjson.git
```

The documentation calls the recursive option necessary, and offers a plain clone followed by git submodule update --init as the alternative. There is a third path for people who already have compatible RapidJSON headers on the system, in which case the module can be compiled against that location through a setup.py option, for example python3 setup.py build with the --rj-include-dir flag pointing at that directory.

For routine work the project leans on make targets. A set of makefiles implements the common operations such as build, check and release, and the documentation says to see the make help output for the list of available targets. The build target compiles the extension in place, the check target runs the test suite and then the documentation doctests, and the benchmarks target runs the benchmark directory.

## Function and class APIs, plus a file-like writer that chunks as it goes

The documented basic usage is short enough to read in one screen, and it shows the two output modes that matter. The module can serialize a plain dict to a string and parse a string back into a dict:

```python
    >>> import rapidjson
    >>> data = {'foo': 100, 'bar': 'baz'}
    >>> rapidjson.dumps(data)
    '{"foo":100,"bar":"baz"}'
    >>> rapidjson.loads('{"bar":"baz","foo":100}')
    {'bar': 'baz', 'foo': 100}
```

The second example is the more interesting one, because it dumps to a file-like object in fixed size chunks. The example defines a Stream class whose write method prints what it receives, then calls rapidjson.dump with chunk_size=5:

```python
    >>> class Stream:
    ...   def write(self, data):
    ...      print("Chunk:", data)
    ...
    >>> rapidjson.dump(data, Stream(), chunk_size=5)
    Chunk: b'{"foo'
    Chunk: b'":100'
    Chunk: b',"bar'
    Chunk: b'":"ba'
    Chunk: b'z"}'
```

The point of that example is the last parameter. A caller that hands the module a file-like object and a chunk size gets the serialized bytes handed over in pieces rather than as one assembled string, which matters when the payload is large or the destination is a socket or a stream that should not hold the whole document in memory. Note that the chunks arrive as bytes, so the caller is dealing with binary output even though the object being serialized is a Python dict of str and int values.

## The relaxed Decoder accepts JSONC comments and trailing commas

Beyond the strict mode, the module offers a relaxed parsing mode exposed through a Decoder class. The documentation demonstrates it with a parse mode built from two flags, PM_COMMENTS and PM_TRAILING_COMMAS:

```python
    >>> from rapidjson import Decoder
    >>> from rapidjson import PM_COMMENTS, PM_TRAILING_COMMAS
    >>> decoder = Decoder(parse_mode=PM_COMMENTS | PM_TRAILING_COMMAS)
    >>> decoder('''
    ... {
    ...     "bar": /* Block comment */ "baz",
    ...     "foo":100, // Trailing comma and comment
    ... }
    ... ''')
    {'bar': 'baz', 'foo': 100}
```

This is the mode to reach for when the input is machine-generated configuration that happens to carry human annotations, or when you are reading files that a developer has been allowed to comment. The flags combine with a bitwise or, so the granularity is per-flag, and a strict Decoder is simply the default without them.

The other capability the wrapper adds over a plain parser is JSON Schema validation, linked to the JSON Schema specification. The README mentions it in the same breath as serialization, and the repository has a typings directory, so the schema and type interfaces are part of the public surface rather than an afterthought.

The documentation is hosted on Read the Docs and the latest version documentation is rendered automatically, which matters for a library where the API is deliberately not the standard library's.

## The licence is a question the repository metadata cannot answer for you

One detail deserves a separate flag, because it is the kind of thing that surprises a legal reviewer. The README header states the project is under the MIT License and points at the LICENSE file in the repository, and the README file itself carries the MIT notice with copyright lines for Ken Robbins and, from 2016 onward, Lele Gaifax. setup.py repeats the same MIT header.

However, the licence reported for the repository is not a standard SPDX identifier. It is recorded as a custom licence that GitHub cannot classify, which points to the LICENSE file rather than to the MIT grant described in the headers.

That is a documentation question, not a legal opinion, and it is worth resolving before you ship a dependency on it. The header comments and the LICENSE file disagree in form even if they agree in substance, and the vendored RapidJSON sources inside the rapidjson submodule carry their own licence file, which the Makefile regenerates as rapidjson/license.txt during setup. A rapidjson project on the default branch is not archived, and the last push to the repository was on 2026-10-12, but the repository has no GitHub releases, so release history lives in the CHANGES.rst file rather than in tagged releases.

The practical check before adopting is to read the LICENSE file in the repository, confirm it is the MIT text the headers describe, and confirm how you want to treat the vendored RapidJSON sources it includes.

## Where python-rapidjson is the wrong tool

The clearest wrong-tool case is code that depends on the standard library json behaviour in detail. Anything relying on the exact exception types, the exact hook points, the default separators of the stdlib encoder, or the stdlib's own object types coming back out of a parse will find different behaviour here. The project does not pretend otherwise, and the incompatibility section of the documentation is the place to read the specifics before committing to a swap.

The second wrong-tool case is operational rather than API-level. A compiled extension means a wheel that matches your interpreter, architecture and platform, and it means a build step in environments where wheels are not available. If your deployment pipeline is restricted to a pure-Python dependency set, or if you need to run on an interpreter older than 3.10, this module is out.

For the alternative question, the standard library json module is the honest comparison. It is pure Python, always available, and identical everywhere, and for most payloads it is not the bottleneck. The difference in approach is native compilation, a chunked writer for streams, relaxed parse modes and JSON Schema validation against a pure-Python parser with no schema support. The development dependency list in requirements.txt is a useful hint about how the project itself positions the field: it pulls in orjson, ujson, simplejson and pysimdjson for benchmark comparisons, and there is a make target for running benchmarks against other engines, with the benchmark suite living in the benchmarks directory. The stdlib json module is the one competitor absent from that dependency list, which suggests it is treated as the baseline rather than a rival.

## Conclusion

python-rapidjson suits a service that parses or emits large JSON payloads on CPython 3.10 or newer and wants a native parser, especially when JSON Schema validation is part of the job. It does not suit a library that must behave exactly like the standard json module, since the project states that a strict drop-in replacement is not its goal. Before adopting, check that your target interpreter is 3.10 or above because setup.py refuses anything older, and read the incompatibility section of the documentation rather than assuming the json API carries over unchanged.

## FAQ

### How do I install python-rapidjson?

Run `pip install python-rapidjson` for a wheel from PyPI, or `conda install -c conda-forge python-rapidjson` from conda-forge. From source, clone with `--recursive` so the RapidJSON submodule is populated.

### Which Python versions does python-rapidjson support?

Python 3.10 and newer. setup.py raises NotImplementedError with the message "Only Python 3.10+ is supported." on anything older.

### Is python-rapidjson a drop-in replacement for the standard json module?

No, and the project says so directly: being a strict drop-in replacement is not the goal, and the documentation has a dedicated section listing the incompatibilities.

### Can python-rapidjson parse comments and trailing commas?

Yes. A Decoder instance can be built with the PM_COMMENTS and PM_TRAILING_COMMAS flags combined through a bitwise or, giving a relaxed syntax that handles JSONC style input.

### Does python-rapidjson support JSON Schema validation?

Yes. The module exposes JSON Schema validation alongside its serialization and deserialization to bytes, str and file-like instances.

## Sources

- [Issues](https://github.com/python-rapidjson/python-rapidjson/issues)
- [python-rapidjson/python-rapidjson on GitHub](https://github.com/python-rapidjson/python-rapidjson)
- [README](https://github.com/python-rapidjson/python-rapidjson/blob/master/README.md)

---

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