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.
| Feature | JSON | MessagePack |
|---|---|---|
| Encoding | Text (UTF-8) | Binary (format byte + payload) |
| Human readable | Yes – no tools needed | No – requires msgpack library or hex dump |
| Specification | RFC 8259 (IETF), ECMA-404 | Community spec (msgpack.org / github.com/msgpack) |
| Schema required | No – self-describing | No – self-describing format bytes |
| Payload size | Baseline | Smaller 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. |
| Integers | IEEE 754 double – max safe 2^53-1 | Native int8–int64, uint8–uint64 – full 64-bit range |
| Binary data | Base64 required (+33% overhead) | Native bin8/bin16/bin32 – no encoding overhead |
| Float precision | 64-bit IEEE 754 only | float32 and float64 – save 4 bytes with 32-bit where sufficient |
| Extension types | None – use strings by convention (ISO 8601 dates) | ext type: custom types 0–127, timestamp type -1 defined |
| Code generation | Not required | Not required – schema-free like JSON |
| Migration cost | Baseline | Low – swap JSON serializer for msgpack serializer |
| Library support | Native in every language runtime | Good: msgpack-python, @msgpack/msgpack (JS), vmihailenco/msgpack (Go), rmp-serde (Rust) |
| Primary use cases | REST APIs, config, webhooks, browser data | Redis 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.