Skip to main content

JSON vs MessagePack

MessagePack and JSON represent the same data model: integers, floats, strings, booleans, null, arrays, and maps. The difference is encoding. JSON wraps every value in human-readable text – field names are repeated verbatim in every message, numbers are encoded as ASCII digit sequences, and binary data requires base64 encoding. MessagePack encodes the same structure as compact binary where small integers and short strings have single-byte overhead. MessagePack has no RFC – it is maintained as a community specification at github.com/msgpack/msgpack. This is its main disadvantage against CBOR (RFC 8949) in standards-required contexts. In practice, MessagePack is widely deployed in Redis (RESP3 uses it), Fluentd log aggregation, and real-time gaming and financial systems where JSON's parsing overhead is measurable.

JSON is human-readable text with universal tooling support. MessagePack is a schema-free binary format from msgpack.org that serializes the same JSON-compatible data model into fewer bytes by eliminating string key repetition and encoding integers natively – actual size reduction is payload-dependent. MessagePack requires no schema and no code generation, and drops into any codebase that already uses JSON with minimal migration cost.

FeatureJSONMessagePack
EncodingText (UTF-8)Binary (format byte + payload)
Human readableYes – no tools neededNo – requires msgpack library or hex dump
SpecificationRFC 8259 (IETF), ECMA-404Community spec (msgpack.org / github.com/msgpack)
Schema requiredNo – self-describingNo – self-describing format bytes
Payload sizeBaselineSmaller for typical structured data – no string key overhead, native integer encoding. Reduction varies: key-heavy or integer-dense payloads benefit most; string-value-heavy payloads less so.
IntegersIEEE 754 double – max safe 2^53-1Native int8–int64, uint8–uint64 – full 64-bit range
Binary dataBase64 required (+33% overhead)Native bin8/bin16/bin32 – no encoding overhead
Float precision64-bit IEEE 754 onlyfloat32 and float64 – save 4 bytes with 32-bit where sufficient
Extension typesNone – use strings by convention (ISO 8601 dates)ext type: custom types 0–127, timestamp type -1 defined
Code generationNot requiredNot required – schema-free like JSON
Migration costBaselineLow – swap JSON serializer for msgpack serializer
Library supportNative in every language runtimeGood: msgpack-python, @msgpack/msgpack (JS), vmihailenco/msgpack (Go), rmp-serde (Rust)
Primary use casesREST APIs, config, webhooks, browser dataRedis pub/sub, Fluentd, real-time APIs, game state sync

When to use JSON

JSON is correct for: public APIs, any API consumed by browsers or third-party clients, debugging-heavy workflows, and configuration files. If the humans reading and writing the data matter as much as the machines parsing it – use JSON. Also use JSON where RFC-backed standards compliance is required (IANA content negotiation, HTTP Content-Type headers for public APIs).

When to use MessagePack

MessagePack is correct for: internal service-to-service communication where both sides are under your control, high-frequency messaging systems (WebSocket real-time updates, pub/sub event streams), Redis pub/sub channels, Fluentd log pipelines, and any situation where you want the simplicity of JSON's schema-free model with meaningfully better performance. The migration path from JSON is straightforward – the data model is identical.

Common Mistakes

  • Choosing MessagePack over JSON for a public API – the lack of native browser support and universal tooling makes third-party integration painful. Public APIs should almost always use JSON.
  • Assuming MessagePack requires schema files like Protobuf – MessagePack is schema-free. You serialize any map/array/scalar structure and deserialize back to the same structure. No .proto files, no code generation.
  • Not handling the binary/str distinction properly – MessagePack distinguishes bin (raw bytes) from str (UTF-8 text). Some libraries (msgpack-python with raw=False) decode bin as bytes and str as str; others conflate them. Be explicit about which you need.
  • Ignoring the extension type system – if your data has domain-specific types (timestamps, UUIDs, decimals), use MessagePack's ext type instead of encoding them as strings. This preserves type information and avoids runtime parsing.

FAQ

Is MessagePack the same as CBOR?

Both are binary JSON-compatible formats but they are distinct specifications. CBOR (RFC 8949) is IETF-standardized and required by FIDO2/WebAuthn. MessagePack is a community spec (msgpack.org) with no RFC. CBOR has richer semantic tagging (dates, UUIDs, bignums) and a defined canonical form. MessagePack is slightly more compact for small integers and has broader existing deployment in Redis and Fluentd. For new projects without a specific constraint, CBOR is the more future-proof choice; MessagePack is the better choice when migrating from JSON in an existing system due to its identical data model.

How does MessagePack encode small integers?

Integers 0–127 encode as a single byte (positive fixint: 0x00–0x7f). Integers -32 to -1 encode as a single byte (negative fixint: 0xe0–0xff). This means common small values like status codes, counts, and enum-like integers take one byte instead of the 1–3 bytes they would take as a JSON number string.

Can I use MessagePack over HTTP?

Yes. Use Content-Type: application/msgpack (or application/x-msgpack for older clients). The client sends Accept: application/msgpack to signal support. Many HTTP frameworks support content negotiation that transparently serves JSON or MessagePack based on the Accept header, letting you support both without changing your API logic.