Skip to content
JSON Tidy
Learn JSON / JSON format

JSON format explained: syntax, data types, and how to design it well

JSON is the plain-text format most web APIs use to send data, and it turns up in config files, logs, and data exports too. The grammar is small enough to learn in an afternoon. Most real-world JSON bugs come from the details: a trailing comma, a number with a leading zero, a date with no time zone, or an ID too big for JavaScript.

Every section below uses the same weather forecast response. We’ll read it, break it, look at each type of value it contains, and then look at the design choices behind it.

Reading a JSON document

Here’s what a forecast service might return when you ask about Denver:

{
  "location": {
    "city": "Denver",
    "latitude": 39.7392,
    "longitude": -104.9903
  },
  "updatedAt": "2026-09-29T14:00:00Z",
  "current": {
    "temperatureC": 18.5,
    "condition": "Partly cloudy",
    "windKph": 12,
    "isDaytime": true
  },
  "hourly": [
    {
      "time": "2026-09-29T15:00:00Z",
      "temperatureC": 19.1,
      "chanceOfRain": 0.1
    },
    {
      "time": "2026-09-29T16:00:00Z",
      "temperatureC": 19.4,
      "chanceOfRain": 0.25
    }
  ],
  "alerts": [],
  "stationId": "0457",
  "airQuality": null
}

The whole response is a single object, marked by the outer curly braces. Each member inside has a name in double quotes, a colon, and a value. Some values are simple: updatedAt is a string and airQuality is null. Others are containers. location and current are objects in their own right, hourly is an array with one object per hour, and alerts is an array that happens to be empty.

To point at a value, follow the names and positions down. location.city is "Denver". hourly[1].chanceOfRain is 0.25, the second hour, because array positions start at zero. JavaScript, Python, and command-line tools such as jq all address nested JSON this way. In jq, you’d write .hourly[1].chanceOfRain.

Objects hold things with named properties, and arrays hold lists where order matters. Most APIs combine them the way this one does: an object at the top level so each part has a name, and an array wherever there’s a list. Open this forecast in the JSON Tidy validator to collapse and inspect it.

JSON syntax rules

  • Names are strings in double quotes. {city: "Denver"} and {'city': 'Denver'} are valid JavaScript, but not valid JSON.
  • Colons and commas do the joining. A colon separates a name from its value. A comma separates one member or array item from the next. Nothing follows the last one.
  • Strings use double quotes only. Single quotes and backticks aren’t allowed.
  • The literals are lowercase and unquoted: true, false, and null.
  • A document holds exactly one value. Two objects one after another aren’t a valid document. JSON Lines handles that case.
  • Whitespace between tokens doesn’t matter. {"city":"Denver"} on one line is the same data as the indented version. Pretty-printing and minifying only change how it looks.

Most invalid JSON comes from writing it by hand as if it were JavaScript. This snippet has six problems:

{
  location: {city: 'Denver'},
  "current": {"temperatureC": 18.5, "isDaytime": True,},
  "stationId": 0457,
  "airQuality": undefined // no sensor yet
}

In order: location is missing its quotes, 'Denver' uses single quotes, True is capitalized, a trailing comma follows isDaytime, 0457 has a leading zero, and undefined and the // comment aren’t part of JSON. Here’s the corrected version:

{
  "location": {"city": "Denver"},
  "current": {"temperatureC": 18.5, "isDaytime": true},
  "stationId": "0457",
  "airQuality": null
}

The JSON Tidy validator points to the line of the first error and can repair common mistakes like these. Trailing commas cause so many problems that they have their own guide.

JSON data types

JSON has six kinds of value, and the forecast uses all of them. When a program parses the text, each one becomes that language’s closest equivalent:

TypeIn the forecastJavaScriptPython
String"Partly cloudy"stringstr
Number18.5numberint or float
Booleantruebooleanbool
NullnullnullNone
Objectlocationobjectdict
Arrayhourlyarraylist

Types are exact. 18.5 is a number and "18.5" is a string, and true is a boolean but "true" is text. A consumer expecting one will often reject the other, even though both are valid JSON.

Strings and escaping

Inside a string, a backslash starts an escape sequence. You need one for a double quote (\"), a backslash (\\), and control characters such as a newline (\n) or tab (\t). A string can’t contain a raw line break. Any character can also be written as \u plus four hex digits:

{"note": "Station \"Denver-7\"\nReadings in \u00b0C"}

That decodes to Station "Denver-7" on one line and Readings in °C on the next. Characters outside the first 65,536 code points, including most emoji, take two escapes: 🌧 is \ud83c\udf27. RFC 8259 requires JSON exchanged between systems to be UTF-8, so you can usually type ° directly. The \u form mainly helps when a tool along the way can only handle ASCII. To turn raw text into a correctly escaped JSON string, or to reverse it, use the escape and unescape tool.

Numbers

JSON has one number type: an optional minus sign, digits, an optional fraction, and an optional exponent. 18.5, -104.9903, 0.25, and 1.2e3 are all valid. 0457 (leading zero), .5 and 5. (need a digit either side of the point), +3, 0xFF, NaN, and Infinity are not.

The format puts no limit on size or precision, but the program reading it does. JavaScript stores every number as a 64-bit float, which holds whole numbers exactly only up to 9,007,199,254,740,991. Parse {"id": 9007199254740993} in a browser and you get back 9007199254740992, with no error. Python keeps it exact, so the same document can mean different things to different clients. That’s why many APIs send large IDs as strings.

stationId is a string for a related reason. It’s a label, not a quantity, and its leading zero matters. Postal codes, phone numbers, and account numbers belong in strings too. For money, use integer cents or a decimal string, because floating-point arithmetic gives 0.1 + 0.2 = 0.30000000000000004.

Booleans, null, and missing values

isDaytime: true is a plain yes/no. The more interesting distinction is between three kinds of “nothing”. airQuality: null says the service knows about air quality but has no reading right now. alerts: [] says there are currently zero alerts. If airQuality were left out entirely, a reader couldn’t tell whether the service doesn’t support it or simply didn’t send it. Choose what each form means in your data and use it consistently.

What JSON can’t express

JSON deliberately has very few types. For anything else, the sender and receiver have to agree on a convention.

  • Dates and times are usually ISO 8601 strings, like "2026-09-29T14:00:00Z" in the forecast. The Z means UTC, the format sorts correctly as plain text, and every mainstream language can parse it. Avoid "09/29/2026", which reads differently in different countries. If you use Unix timestamps, document whether they’re in seconds or milliseconds.
  • Comments were left out on purpose. Crockford has said he removed them because people were using them to hold parser instructions. Some config formats accept comments anyway: VS Code settings and tsconfig.json use JSONC (JSON with comments), and other tools read JSON5. These files are a superset of JSON, and a strict JSON parser will reject them.
  • undefined, functions, NaN, and Infinity have no JSON form. JavaScript’s JSON.stringify silently drops members whose value is undefined or a function, and writes NaN and Infinity as null. If data seems to disappear when you save it, this is a common cause.
  • Binary data, such as an image, is typically Base64-encoded into a string. That makes it about a third larger, so APIs often send a URL to the file instead.

Designing JSON other people can use

Valid JSON can still be awkward to work with. A few habits make the difference, and the forecast follows each of them:

  • Pick one naming style. The forecast uses camelCase throughout. snake_case works just as well; mixing the two means readers have to check every name.
  • Make units obvious. temperatureC and windKph answer the question a bare "temperature": 18.5 would raise.
  • Keep each field’s type stable. If chanceOfRain is sometimes 0.25 and sometimes "25%", every consumer needs extra code to handle both.
  • Use arrays for lists. Numbered names like hour1 and hour2 can’t be looped over or counted, and you can’t add another hour without a new name.
  • Put an object at the top level. Sending {"hourly": [...]} instead of a bare array leaves room to add updatedAt or paging details later without breaking existing clients.
  • Don’t rely on member order or repeat a name. Objects are unordered, and some storage reorders them: PostgreSQL’s jsonb type does. Parsers also disagree about duplicate names. JavaScript and Python keep the last value, others keep the first or raise an error.
  • Write the shape down. A JSON Schema records which fields are required and what types they hold, so both sides can validate against the same rules.

Where JSON is used

Web APIs are the main place. A server sends the forecast with a Content-Type: application/json header, and the client turns the text into native objects with one call:

// JavaScript
const forecast = await response.json();   // or JSON.parse(text)
forecast.hourly[1].chanceOfRain;           // 0.25

# Python
forecast = json.loads(text)
forecast["hourly"][1]["chanceOfRain"]      # 0.25

Configuration files such as package.json and composer.json use JSON because every language can read it without extra libraries.

Logs and large datasets often use JSON Lines (.jsonl, also called NDJSON). Each line is a complete JSON value, so a program can append records without rewriting the file and process them one at a time.

Databases store it directly. PostgreSQL has json and jsonb column types, and MongoDB stores documents in BSON, a binary format modeled on JSON.

Login tokens are often JWTs. A JWT is three Base64url-encoded parts separated by dots, and the first two decode to JSON objects describing the token and the user. Anyone can decode them. The signature proves who issued the token, but it doesn’t hide the contents. Learn how JWTs work, or inspect one with the JWT decoder.

When JSON needs to fit into a spreadsheet, nested objects have to become columns. The nested JSON to CSV guide shows how that works.

JSON vs XML and YAML

Here are the forecast’s current conditions in the other two common text formats:

XML

<current>
  <temperatureC>18.5</temperatureC>
  <condition>Partly cloudy</condition>
  <isDaytime>true</isDaytime>
</current>

YAML

current:
  temperatureC: 18.5
  condition: Partly cloudy
  isDaytime: true

XML is older and more verbose. Every value is text unless a schema says otherwise, so 18.5 and true aren’t really a number and a boolean. XML has attributes, namespaces, and mixed text-and-markup content, which make it a better fit for documents than for records. SVG, RSS, and the files inside a .docx are XML. Many older enterprise and SOAP APIs still use it.

YAML is built for humans editing configuration: it uses indentation instead of brackets and allows comments. YAML 1.2 is designed so that almost any JSON document is also valid YAML. The trade-off is surprising behavior. Indentation mistakes change the structure, and older YAML 1.1 parsers read the bare value no (for example, Norway’s country code) as false. It’s the usual choice for Kubernetes, Docker Compose, and CI pipelines.

For data passed between programs, JSON is the usual default. It has real types, parsers exist in every language, and browsers include one.

Frequently asked questions

Is JSON case-sensitive?

Yes. "City" and "city" are different names, and the literals must be lowercase: True, NULL, and FALSE are all invalid.

What is the JSON MIME type and file extension?

The registered media type is application/json, and files use the .json extension. APIs send the media type in the Content-Type header so clients know how to read the body.

Does JSON have to start with { or [?

No. Since RFC 7159 in 2014, a JSON document can be any single value, including a string, number, true, false, or null. Some older parsers and many APIs still expect an object or array at the top.

Is there a maximum size for JSON?

The format sets no limit on document size, nesting depth, string length, or number size. Parsers, servers, and databases each impose their own, so check the limits of whatever receives your data.

Is JSON the same as a JavaScript object?

No. JSON is text with a stricter grammar that borrows JavaScript’s literal syntax. A JavaScript object can hold functions, undefined, and single-quoted strings; JSON text cannot. JSON.parse turns the text into an object and JSON.stringify turns an object back into text.

Who created JSON?

Douglas Crockford specified and popularized it in the early 2000s, based on a subset of JavaScript. It is now standardized as ECMA-404 and IETF RFC 8259, which agree on the grammar.