JavaScript Object Notation

Tool

A text format programs use to exchange data, writing values nested inside other values as characters a person can still read.

Why it exists

Inside a program, data sits in layers. A document has a title, and an author, and inside that author there is a name and an email address. To send that to another program, the whole thing has to be flattened into plain characters and unpacked again on the far side. Both ends need the same flattening rule — and for a while every company invented its own.

So integration documents carried sentences like this: skip the first line, split on commas, the third field is the amount, and if the amount is empty the field disappears, so count the fourth field as the third. Change the rule slightly on one side and only that one integration broke, quietly. Every new partner meant writing the reading code again from scratch.

The shared format that came before was XML (Extensible Markup Language), which wraps every value in an opening and a closing tag. The structure was unambiguous, but the same content took roughly twice as many characters and was hard to scan by eye. JSON took over that job with a handful of punctuation marks instead of tags. The rules take about ten minutes to learn, and a received message can be pasted straight onto a screen for a human to find what's wrong with it. That is most of why it spread.

What it solves

It solves the problem of writing layered structure as nothing but characters, and it does so with four devices.

  1. Braces hold pairs of names and values. The name goes in double quotes, the value comes after a colon.
  2. Brackets hold a list of things of the same kind.
  3. A value is text, a number, true or false, or null. Text is quoted, numbers are written bare.
  4. A value can itself be braces or brackets, so layers nest as deep as needed.

One document looks like this.

{
  "id": 1024,
  "title": "September planning notes",
  "done": true,
  "author": { "name": "Mia", "email": "mia@pagedeck.io" },
  "tags": ["meeting", "september"]
}

The second problem falls out of the first: a person can read it. When a payment fails, Theo pulls the message out of the logs and looks at it. Was the amount an empty string, or was the field missing entirely? He can tell right there. With a machine-only format, answering that question means installing a tool first.

Third, it belongs to no particular language. JavaScript is in the name, but the spec itself is independent of it. A Python server and a Swift app both read and write it with facilities that ship in the box.

That is how it became the default for the body carried by an API — the payload. Whether the style is REST or GraphQL, the characters on the wire are usually JSON.

The point is that all three properties come from the same choice. Readability is what makes debugging cheap, and independence from any one language is what lets a team swap out a client without renegotiating the wire format.

Alternatives

Other formats still hold their ground where the job suits them. Four come up most.

  1. XML — every value wrapped in a named tag. It can carry formatting and comments inside the document itself, which is part of why financial and government integrations still run on it. The same content is much longer.
  2. CSV (comma-separated values) — a table with commas between fields. For a few hundred thousand rows of sales data with fixed columns, it's smaller and faster. It cannot express nesting at all.
  3. YAML — structure shown by indentation, almost no punctuation. It suits configuration files that people write and edit by hand. One stray space changes the meaning, which makes it risky for data in transit.
  4. Protocol Buffers — unreadable to a person, but compact and fast to parse. It earns its place between servers trading tens of thousands of messages per second.

JSON has clear weak spots of its own. There are no comments, so a configuration file written in it has nowhere to say what a field means. There is no date type, so dates travel as text and both sides have to agree on a format separately. And because every field repeats its name on every row, the same content travels three or four times larger than it would in Protocol Buffers.

Picking a format is rarely a fresh decision, either. Most teams inherit whatever their framework and their partners already emit, and the argument only opens when a size or speed limit starts to bite.

How it sounds at work

"The amount comes back as text in the response — can you send it as a number?"

Our side can't do arithmetic on it as-is, so please drop the quotes.

"You're getting a 400 because the body isn't JSON."

The format the server expects and the format you sent don't match, so the request is rejected at the door.

"This file is JSON, so we can't put comments in it. I'll write the explanation in the doc."

There's nowhere inside the file to leave notes, so they go somewhere else.

The week Pagedeck integrated a payment provider. Theo gets an approval response back and the amount field arrives wrapped in quotes, though the documentation says it's a number. He copies the raw body into a message to the provider, and the provider finds that same day that one currency has always gone out as text. Because what crossed the wire was readable characters, deciding whose bug it was took under half a day.

To stop that check from repeating on every round trip, teams write the type of each field into a spec document such as OpenAPI. JSON fixes the shape and says nothing about the contents. A generated document like that also gives both sides one place to look when the two implementations disagree.

What people get wrong

The most common mistake is reading it as a JavaScript-only format. The name invites that. In practice JavaScript only lent it the punctuation; the spec was split off on its own long ago, and every mainstream language reads and writes it as a built-in. Teams that believe otherwise pick something else because their server is Java or Python, and then build a conversion layer when a web or mobile client shows up.

The other one is assuming that agreeing on JSON means agreeing on the data. Sharing one format makes it feel that way. But what JSON settles is where the braces and quotes go. Which fields must exist, whether the amount is a number or text, which fields are allowed to be missing — none of that is covered. That's how a perfectly well-formed body arrives and the receiving side still can't compute with it. Valid format and matching contract are two different claims.

Where to go next

  • API

    see the whole channel this format travels through

  • HTTP method

    one of the other two pieces riding along on the same round trip

Last updated 2026-09-16