mrkeyoor.com_
Sun 20 Sept 02:40 UTC
PyPIUtilsupdated 19 Sept 2026

orjson review

orjson is a compiled JSON encoder and decoder for CPython. dumps returns UTF-8 bytes and natively handles dataclasses, datetime values, UUIDs, enums, and NumPy arrays when the matching option is enabled; loads accepts bytes, bytearray, memoryview, or text. Version 3.12.0 rewrites the serialization implementation, adds Python 3.15 wheels, and stops publishing ppc64le and s390x wheels. It deliberately leaves file I/O and JSON Lines framing to your code.

Verdict

orjson 3.12.0 installed in 0.2 seconds as a 1 MB package with 0 dependencies, typed APIs, and 0 pip-audit findings in our sandbox. Install it after profiling confirms JSON cost and bytes output fits the boundary; avoid it on unsupported runtimes or architectures and where stdlib compatibility matters more than encoder speed.

We installed it

Lab card: what happened when we installed orjsonScreenshot of orjson documentation
Install✓ · 0.2s1 package on disk · 1 MB
Importimport orjson in 0.12s · compiled extensions · py.typed · requires Python >=3.10
Known vulns0(pip-audit)

Answers from our run

Does orjson install cleanly?

Yes. In a fresh container with an empty cache, pip install orjson finished in 0.2s, leaving 1 package and 1 MB on disk. pip-audit reported no known vulnerabilities.

What does orjson need to run?

Python >=3.10, and a platform wheel with compiled extensions. In our run import orjson succeeded in 0.12s, and the package ships py.typed for type checkers.

orjson or ujson: which should you use?

ujson: Choose it when a compiled JSON implementation with a more text-like migration path fits an existing codebase. orjson 3.12.0 installed in 0.2 seconds as a 1 MB package with 0 dependencies, typed APIs, and 0 pip-audit findings in our sandbox.

When should you not use orjson?

The application runs on PyPy, embedded Android or iOS Python, or PEP 554 subinterpreters; the README says these targets are unsupported.

API stability4/5The core stays intentionally small: dumps, loads, JSONEncodeError, JSONDecodeError, Fragment, and documented option flags. The project follows semantic versioning and says serializing a new object type without opt-in counts as a breaking change. Version 3.12.0 rewrote serialization without announcing an API break, but byte-exact consumers should still regression-test float, datetime, key, and whitespace output.
Docs5/5The README is the manual and specifies accepted input types, bytes output, supported native objects, every option, exception behavior, UTF-8 rules, 64-bit and 53-bit integer limits, recursion limits, GIL behavior, wheel targets, build requirements, and reproducible benchmarks. It also shows a subtle default-callback failure that turns an unsupported value into null. Few usage decisions are left implicit.
Maintenance4/5Version 3.12.0 shipped on August 14, 2026, and GitHub recorded a push on August 20. The repository was not archived and had 8,214 stars. GitHub displayed 0 open issues and pull requests because the project intentionally disables that workflow, citing signal-to-noise. Recent releases added Python 3.15 compatibility, raised Rust requirements, and fixed a deep-nesting crash, but public issue discussion is unavailable.
Ecosystem4/5The current dataset records 56,793,807 weekly downloads. orjson plugs into Python web responses, caches, and pipelines, and handles dataclasses, datetime, UUID, enums, and NumPy without an adapter for common cases. The installed wheel ships py.typed and has 0 direct dependencies. Its CPython-only binary model, bytes return value, and missing ppc64le and s390x wheels narrow where it can replace json.

Discussed on

  1. hnJSON extra uses orjson instead of ujson (2019)69 points
  2. hnorjson: Fast, correct Python JSON lib (supports dataclasses, datetimes, numpy)4 points
  3. hnOrjson no more open issue tracker or pull requests due to signal-to-noise ratio3 points
  4. hnorjson v3, fast Python JSON library supporting dataclasses, numpy3 points

Use it if

  • Profiling shows JSON encode or decode time is material in a CPython service or data pipeline.
  • Returning bytes fits your web framework, socket, cache, or file-writing boundary.
  • You need direct serialization of dataclasses, UUIDs, datetime values, or NumPy arrays with explicit options.
  • Strict UTF-8 input and rejection of out-of-range integers are useful correctness checks for your payloads.
Skip it if

Setup reality

Our clean Python 3.12 install of orjson 3.12.0 took 0.2 seconds and left 1 package occupying 1 MB. import orjson completed in 0.12 seconds. The wheel has a compiled .so extension, declares 0 direct dependencies, requires Python 3.10 or newer, and ships py.typed. pip-audit reported 0 known vulnerabilities in our sandbox.

The first migration surprise is the return type: orjson.dumps produces bytes, whereas json.dumps produces str. Many HTTP frameworks accept bytes directly; text-only interfaces need an explicit UTF-8 decode. loads accepts both byte-oriented inputs and str. The package provides no open-file helper and no JSON Lines reader, so your code owns file handles, newlines, and record boundaries.

Serialization choices are bitwise option flags. OPT_NAIVE_UTC changes naive datetime output, OPT_NON_STR_KEYS permits selected key types, and OPT_SERIALIZE_NUMPY handles supported arrays. Non-string keys can collapse into the same JSON key after conversion. A default callback must raise TypeError for an object it cannot handle, otherwise Python's implicit None becomes JSON null and silently changes the value.

The call holds the GIL for its duration, so threads do not turn one large encode into parallel CPU work. Current wheels cover the mainstream CPython platforms listed in the README, but 3.12.0 removed ppc64le and s390x builds. If pip falls back to source, Rust and a C compiler enter the build path. Benchmark your own documents before changing serializers; the measured install proves packaging and import, not an application speedup.

Patterns

Encode to UTF-8 bytes and decode again encode-json

import orjson

payload = orjson.dumps({'status': 'ok'})
assert isinstance(payload, bytes)

dumps returns bytes, not str. Pass the bytes through when the destination accepts them instead of decoding and re-encoding.

Write one JSON record to a file decode-json

value = orjson.loads(b'{"count": 3}')

The library does not manage files. OPT_APPEND_NEWLINE avoids concatenating a second immutable bytes object for JSON Lines output.

Serialize timezone-aware datetimes format-output

data = orjson.dumps(value, option=orjson.OPT_INDENT_2 | orjson.OPT_APPEND_NEWLINE)

OPT_NAIVE_UTC treats a datetime without tzinfo as UTC. Decide whether that assumption is valid before enabling it globally.

Encode supported NumPy arrays encode-datetime

data = orjson.dumps(event, option=orjson.OPT_NAIVE_UTC | orjson.OPT_UTC_Z)

OPT_SERIALIZE_NUMPY handles supported contiguous arrays. Validate dtype and shape expectations at the API boundary.

Convert Decimal through a default callback encode-numpy

data = orjson.dumps(array, option=orjson.OPT_SERIALIZE_NUMPY)

Raise TypeError for every unhandled type. Falling off the function returns None, which orjson encodes as null.

Permit selected non-string dictionary keys encode-custom-type

def default(value):
    if isinstance(value, Decimal):
        return str(value)
    raise TypeError

data = orjson.dumps(record, default=default)

Converted keys can collide, such as 1 and '1'. The later JSON member may overwrite the earlier value when decoded.

Reject integers unsafe for JavaScript allow-non-string-keys

data = orjson.dumps(mapping, option=orjson.OPT_NON_STR_KEYS)

OPT_STRICT_INTEGER caps integers at 53 bits. Without it, orjson accepts signed and unsigned values within its 64-bit limit.

Return readable two-space JSON enforce-safe-integers

data = orjson.dumps(record, option=orjson.OPT_STRICT_INTEGER)

Only a 2-space indentation option is provided. Arbitrary indent widths and ensure_ascii behavior from json.dumps are unavailable.

Embed trusted pre-encoded JSON sort-object-keys

data = orjson.dumps(record, option=orjson.OPT_SORT_KEYS)

Fragment inserts bytes as JSON without validation or escaping. Use it only with content already produced and trusted as valid JSON.

Map decoding failures to client errors embed-json-fragment

cached = orjson.dumps({'id': 42})
body = orjson.dumps({'item': orjson.Fragment(cached)})

JSONDecodeError identifies malformed UTF-8 or JSON. Do not echo an entire untrusted request body in the response or logs.

Alternatives

PackageRegistryPick it when
ujsonPyPIChoose it when a compiled JSON implementation with a more text-like migration path fits an existing codebase.
msgspecPyPIChoose it when typed structural decoding and validation matter alongside fast JSON and MessagePack.
simplejsonPyPIChoose it for a mature stdlib-shaped API with decimal support and fewer binary-platform assumptions.

More utils guides

lru-cache · type-fest · ajv · p-limit · find-up · js-yaml · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.