What is JSON?

JSON (JavaScript Object Notation, pronounced “JAY-son”) is a lightweight, text-based format for storing and transporting structured data. It’s the lingua franca of the internet — virtually every API, config file, automation platform, and web service uses JSON to exchange data.

If you’re working with n8n, Home Assistant, APIs, or any modern automation tool, JSON is the single most important data format to understand.

Why JSON Matters for You

Where You’ll See ItHow You’ll Use It
n8nEvery item that flows between nodes is a JSON object. Expressions reference JSON fields.
Home AssistantAPI calls, automation configs, sensor states, WebSocket messages — all JSON.
REST APIsRequest bodies and responses are JSON.
Config filesMany tools use JSON for configuration (package.json, tsconfig.json, etc.).
DatabasesPostgreSQL, MongoDB, and others store/query JSON natively.

JSON vs Other Formats

FormatBest ForProsCons
JSONAPIs, automation, data exchangeUniversal, lightweight, easy to parseNo comments, strict syntax
YAMLConfig files (HA, Docker Compose)Readable, supports commentsIndentation-sensitive, can be ambiguous
CSVTabular data (spreadsheets)Simple, compactNo nesting, no data types
XMLLegacy enterprise systemsSchema validation, attributesVerbose, hard to read
TOMLConfig files (Cargo, Hugo)Clean for configsNot great for deeply nested data

Key takeaway: YAML is what you write (configs, HA automations). JSON is what moves (APIs, n8n data, HA WebSocket messages). You need to be fluent in both, but JSON is the one that carries your data between systems.


JSON Data Types

JSON has exactly 6 data types. That’s it. Everything in JSON is built from these:

TypeWhat It IsExample
StringText, wrapped in double quotes"Hello, world!"
NumberInteger or decimal (no quotes)42 or 3.14159 or -7
BooleanTrue or false (no quotes)true or false
NullAbsence of value (no quotes)null
ArrayOrdered list, wrapped in []["apple", "banana", "cherry"]
ObjectKey-value pairs, wrapped in {}{"name": "John", "age": 35}

Important Rules

  1. Strings MUST use double quotes — single quotes are invalid: "correct" not 'wrong'
  2. Keys MUST be strings with double quotes: "name": "John" not name: "John"
  3. No trailing commas{"a": 1, "b": 2,} is invalid (comma after 2)
  4. No comments// this is a comment is NOT valid JSON
  5. Numbers are just numbers — no quotes, no thousands separators: 4200 not "4,200"

Examples of Each Type

{
  "string_example": "I am text",
  "number_example": 42,
  "decimal_example": 3.14,
  "negative_example": -17,
  "boolean_true": true,
  "boolean_false": false,
  "null_example": null,
  "array_example": ["apple", "banana", "cherry"],
  "object_example": {
    "nested_key": "nested value"
  }
}

Objects: The Building Block

A JSON object is a collection of key-value pairs, wrapped in curly braces {}. This is the most important structure in JSON — almost everything you work with will be an object.

Basic Object

{
  "name": "John Costabile",
  "email": "john@example.com",
  "age": 35,
  "is_active": true
}

Accessing Values

In JavaScript / n8n expressions / most programming languages:

data.name           // "John Costabile"
data["email"]       // "john@example.com"
data.age            // 35
data.is_active      // true

In n8n expressions:

{{ $json.name }}              // "John Costabile"
{{ $json.email }}             // "john@example.com"
{{ $json["is_active"] }}      // true

Keys with Spaces or Special Characters

If a key has spaces or special characters, you must use bracket notation:

{
  "first name": "John",
  "order-total": 150.00
}
data["first name"]        // "John"
data["order-total"]       // 150.00

Best practice: Avoid spaces and hyphens in keys. Use snake_case or camelCase instead: first_name, orderTotal.


Arrays: Lists of Data

A JSON array is an ordered list of values, wrapped in square brackets []. Arrays can contain any type — strings, numbers, objects, or even other arrays.

Array of Strings

["red", "green", "blue"]

Array of Numbers

[10, 20, 30, 40, 50]

Array of Objects (Very Common)

[
  {"name": "John", "role": "admin"},
  {"name": "Dawn", "role": "editor"},
  {"name": "Evie", "role": "viewer"}
]

Accessing Array Elements

Arrays are zero-indexed — the first element is at position 0:

users[0]              // {"name": "John", "role": "admin"}
users[0].name         // "John"
users[1].name         // "Dawn"
users.length          // 3 (number of elements)

In n8n expressions:

{{ $json.users[0].name }}      // "John"
{{ $json.users[1].name }}      // "Dawn"
[42, "hello", true, null, {"key": "value"}, [1, 2, 3]]

Best practice: Keep arrays homogeneous — all elements should be the same type. Mixed-type arrays are valid JSON but make code harder to write and maintain.


Nesting: Objects Within Objects

Real-world JSON is almost always nested — objects inside objects, arrays inside objects, objects inside arrays. This is where JSON gets powerful (and where people get confused).

A Realistic Example: API Response

{
  "status": "success",
  "code": 200,
  "data": {
    "user": {
      "id": 12345,
      "name": "John Costabile",
      "email": "john@example.com",
      "address": {
        "street": "3 Station Rd",
        "suburb": "Red Hill",
        "state": "VIC",
        "postcode": "3937",
        "country": "Australia"
      },
      "devices": [
        {"type": "iphone", "model": "iPhone 15 Pro"},
        {"type": "watch", "model": "Apple Watch Ultra 2"}
      ]
    }
  },
  "metadata": {
    "request_id": "abc-123-def",
    "timestamp": "2026-07-15T10:30:00Z"
  }
}

Accessing Nested Values

response.data.user.name                          // "John Costabile"
response.data.user.address.suburb                // "Red Hill"
response.data.user.address.postcode              // "3937"
response.data.user.devices[0].model              // "iPhone 15 Pro"
response.data.user.devices[1].type               // "watch"
response.metadata.request_id                     // "abc-123-def"

In n8n expressions:

{{ $json.data.user.name }}                       // "John Costabile"
{{ $json.data.user.address.suburb }}              // "Red Hill"
{{ $json.data.user.devices[0].model }}            // "iPhone 15 Pro"

The Mental Model

Think of nested JSON as a folder structure. Each . takes you one level deeper:

response
  └── data
        └── user
              ├── name           → "John Costabile"
              ├── email          → "john@example.com"
              ├── address
              │     ├── street   → "3 Station Rd"
              │     ├── suburb   → "Red Hill"
              │     └── state    → "VIC"
              └── devices
                    ├── [0]
                    │     ├── type   → "iphone"
                    │     └── model  → "iPhone 15 Pro"
                    └── [1]
                          ├── type   → "watch"
                          └── model  → "Apple Watch Ultra 2"

Pro tip: When you’re staring at a complex JSON response and can’t figure out the path to the data you need, paste it into a JSON formatter (see Tools section below) and collapse/expand the tree to navigate visually.


Real-World Examples

Example 1: n8n Workflow Item

This is what a typical item looks like as it flows between n8n nodes:

{
  "id": 9876,
  "name": "Widget Pro",
  "price": 49.99,
  "in_stock": true,
  "tags": ["electronics", "gadget", "popular"],
  "supplier": {
    "name": "Acme Corp",
    "contact": {
      "email": "orders@acme.com",
      "phone": "+61-3-9999-0000"
    }
  },
  "metadata": {
    "created_at": "2026-07-15T08:00:00Z",
    "updated_at": null
  }
}

To get the supplier’s email in n8n:

{{ $json.supplier.contact.email }}    // "orders@acme.com"

Example 2: Home Assistant API Call

When you call the HA REST API to turn on a light, the request body is JSON:

{
  "entity_id": "light.lounge_lamp",
  "brightness": 255,
  "color_name": "blue"
}

And the HA API response is JSON:

{
  "entity_id": "light.lounge_lamp",
  "state": "on",
  "attributes": {
    "friendly_name": "Lounge Lamp",
    "brightness": 255,
    "color_name": "blue",
    "supported_color_modes": ["hs", "rgb"],
    "color_mode": "rgb"
  },
  "last_changed": "2026-07-15T01:30:00.000000+00:00"
}

Example 3: Home Assistant WebSocket Message

When HA sends a state change over WebSocket, it’s JSON:

{
  "type": "event",
  "event": {
    "entity_id": "binary_sensor.8_8_8_8",
    "new_state": {
      "state": "off",
      "attributes": {
        "friendly_name": "8.8.8.8",
        "device_class": "connectivity"
      }
    },
    "old_state": {
      "state": "on",
      "attributes": {
        "friendly_name": "8.8.8.8",
        "device_class": "connectivity"
      }
    }
  }
}

Example 4: Docker Compose / Config File

Docker Compose uses YAML, but many config files use JSON. Here’s a package.json:

{
  "name": "kb-costabile",
  "version": "1.0.0",
  "type": "module",
  "scripts": {
    "dev": "astro dev",
    "build": "astro build",
    "preview": "astro preview"
  },
  "dependencies": {
    "astro": "^5.0.0"
  }
}

Example 5: Weather API Response

{
  "location": {
    "name": "Red Hill",
    "region": "Victoria",
    "country": "Australia",
    "lat": -38.37,
    "lon": 145.22
  },
  "current": {
    "temp_c": 12.5,
    "is_day": 0,
    "condition": {
      "text": "Partly cloudy",
      "icon": "//cdn.weatherapi.com/weather/64x64/night/116.png"
    },
    "humidity": 78,
    "feelslike_c": 10.2
  },
  "forecast": {
    "forecastday": [
      {
        "date": "2026-07-15",
        "day": {
          "maxtemp_c": 14.0,
          "mintemp_c": 7.5,
          "condition": {"text": "Sunny"}
        }
      }
    ]
  }
}

To get today’s max temperature in n8n:

{{ $json.forecast.forecastday[0].day.maxtemp_c }}    // 14.0

Common Mistakes and How to Fix Them

1. Single Quotes Instead of Double Quotes

❌  {'name': 'John', 'age': 35}
✅  {"name": "John", "age": 35}

Why: JSON spec requires double quotes. Single quotes are valid in JavaScript but NOT in JSON.

2. Trailing Comma

❌  {"name": "John", "age": 35,}
✅  {"name": "John", "age": 35}

Why: The last key-value pair must NOT have a trailing comma. This is the #1 cause of JSON parse errors.

3. Unquoted Keys

❌  {name: "John", age: 35}
✅  {"name": "John", "age": 35}

Why: Keys MUST be strings with double quotes. This is valid JavaScript but invalid JSON.

4. Comments

❌  {"name": "John", // this is the user's name
     "age": 35}
✅  {"name": "John", "age": 35}

Why: JSON does not support comments. If you need documentation, use a separate schema file or a key like "_comment": "This is the user profile".

5. Using undefined

❌  {"value": undefined}
✅  {"value": null}

Why: undefined is a JavaScript concept. JSON has null for “no value.”

6. Functions or Dates as Values

❌  {"callback": function() { return 1; }, "date": new Date()}
✅  {"callback": null, "date": "2026-07-15T10:30:00Z"}

Why: JSON only supports the 6 data types. Dates must be represented as ISO 8601 strings. Functions can’t be serialized.

7. Incorrect Number Formats

❌  {"price": "49.99", "quantity": "3"}
✅  {"price": 49.99, "quantity": 3}

Why: Numbers should not be wrapped in quotes. If you quote them, they become strings and numeric operations will fail.

Pro tip: If your JSON won’t parse, paste it into jsonformatter.org — it’ll highlight the exact line and character where the error is.


Working with JSON in Different Contexts

In n8n

n8n is built on JSON. Here are the key patterns:

Accessing data from the current node:

{{ $json.fieldName }}
{{ $json.nested.object.key }}
{{ $json.array[0].field }}

Accessing data from a specific node:

{{ $node["Node Name"].json.field }}

Using expressions in a Set node:

=Hello, my name is {{ $json.name }} and I live in {{ $json.address.suburb }}

Working with arrays (Item Lists):

  • Use the Item Lists node to split an array into individual items
  • Use the Aggregate node to combine multiple items into an array

In Home Assistant

Calling the REST API (curl):

curl -X POST http://homeassistant.local:8123/api/services/light/turn_on \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"entity_id": "light.lounge_lamp", "brightness": 255}'

Reading a sensor state (response):

curl -H "Authorization: Bearer YOUR_TOKEN" \
  http://homeassistant.local:8123/api/states/sensor.temperature
{
  "entity_id": "sensor.temperature",
  "state": "22.5",
  "attributes": {
    "unit_of_measurement": "°C",
    "friendly_name": "Living Room Temperature"
  }
}

In JavaScript/Python

JavaScript:

// Parse JSON string to object
const data = JSON.parse('{"name": "John", "age": 35}');
console.log(data.name);  // "John"

// Convert object to JSON string
const json = JSON.stringify(data, null, 2);
console.log(json);

Python:

import json

# Parse JSON string to dict
data = json.loads('{"name": "John", "age": 35}')
print(data["name"])  # "John"

# Convert dict to JSON string
json_str = json.dumps(data, indent=2)
print(json_str)

In the Terminal with jq

jq is a command-line JSON processor. It’s incredibly powerful for filtering and transforming JSON in the terminal:

# Pretty-print JSON from an API response
curl -s https://api.example.com/data | jq .

# Extract a specific field
curl -s https://api.example.com/user | jq '.name'

# Extract nested field
curl -s https://api.example.com/user | jq '.address.suburb'

# Get first element of an array
curl -s https://api.example.com/users | jq '.[0].name'

# Filter array by condition
curl -s https://api.example.com/users | jq '.[] | select(.role == "admin")'

# Extract specific fields from all array elements
curl -s https://api.example.com/users | jq '.[] | {name, email}'

Tools for Working with JSON

ToolTypeBest ForLink
JSON FormatterWebValidating and pretty-printing JSONjsonformatter.org
jqCLIFiltering and transforming JSON in terminaljqlang.org
Postman/InsomniaAppTesting APIs with JSON request bodiespostman.com
JSON SchemaSpecValidating JSON structurejson-schema.org
n8n EditorAppVisual JSON manipulationn8n.io
VS CodeEditorJSON editing with validation built incode.visualstudio.com

VS Code Tips

  • Open any .json file and VS Code will validate it automatically (red squiggles = errors)
  • Shift+Alt+F to format/beautify JSON
  • Install the JSON extension for schema validation
  • Use // in .jsonc files (JSON with Comments) if you need comments

JSON Schema: Validating Structure

When APIs exchange JSON, you need to know the expected structure. JSON Schema is a standard for describing what a JSON object should look like:

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "properties": {
    "name": {"type": "string"},
    "age": {"type": "integer", "minimum": 0},
    "email": {"type": "string", "format": "email"},
    "role": {"type": "string", "enum": ["admin", "editor", "viewer"]},
    "devices": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "type": {"type": "string"},
          "model": {"type": "string"}
        },
        "required": ["type", "model"]
      }
    }
  },
  "required": ["name", "email"]
}

This schema says: “The object must have name and email (required). age must be a non-negative integer. email must be a valid email format. role can only be admin, editor, or viewer. devices is an array of objects with type and model.”

You won’t write schemas often, but understanding them helps you read API documentation that includes them.


Best Practices

PracticeWhyExample
Use consistent namingPredictabilityPick snake_case OR camelCase and stick with it
Use snake_case for keysMost APIs use this conventionfirst_name, created_at, is_active
Use ISO 8601 for datesUniversal, sortable, timezone-aware"2026-07-15T10:30:00Z"
Use null, not empty stringsClear distinction between “no value” and “empty”"middle_name": null not "middle_name": ""
Keep nesting shallowReadability and ease of accessAvoid more than 3-4 levels deep
Use arrays for listsConsistent structure"tags": ["news", "tech"] not "tag1": "news", "tag2": "tech"
Document with a schemaWhen building APIs, provide a JSON SchemaHelps consumers understand the structure
Validate before sendingCatch errors earlyUse JSON.parse() / json.loads() in a try/catch
Pretty-print for readabilityEasier debuggingJSON.stringify(data, null, 2) or json.dumps(data, indent=2)

Your JSON Learning Plan

Day 1: Basics (1-2 hours)

  1. Read this guide start to finish ✅
  2. Go to jsonformatter.org — paste in the examples from this guide and experiment
  3. Try changing values, adding fields, and see what happens
  4. Break the JSON on purpose (remove a quote, add a trailing comma) and see the error messages

Day 2: Hands-on (1-2 hours)

  1. Open your n8n instance (or HA API) and look at the raw JSON in the node outputs
  2. Practice writing expressions to access different fields
  3. Try the jq command line tool — pipe a curl command through jq . to see formatted JSON
  4. Fetch a public API (like https://jsonplaceholder.typicode.com/users) and try to extract specific fields

Day 3: Advanced (1-2 hours)

  1. Build a small n8n workflow that fetches JSON from an API and transforms it
  2. Practice with nested arrays — loop through an array of objects and extract specific fields
  3. Try writing a JSON Schema for a simple object and validate data against it
  4. Read the API documentation for a service you use and understand the JSON structure

Practice Exercises

Exercise 1: Given this JSON, write the path to get the second user’s email:

{
  "users": [
    {"name": "John", "email": "john@test.com"},
    {"name": "Dawn", "email": "dawn@test.com"}
  ]
}
Answer
users[1].email

In n8n: {{ $json.users[1].email }}"dawn@test.com"

Exercise 2: Given this JSON, write the path to get the city:

{
  "data": {
    "profile": {
      "address": {
        "location": {
          "city": "Red Hill"
        }
      }
    }
  }
}
Answer
data.profile.address.location.city

In n8n: {{ $json.data.profile.address.location.city }}"Red Hill"

Exercise 3: Given this JSON, write the path to get the third tag of the first article:

{
  "articles": [
    {
      "title": "Getting Started with n8n",
      "tags": ["automation", "beginner", "tutorial"]
    }
  ]
}
Answer
articles[0].tags[2]

In n8n: {{ $json.articles[0].tags[2] }}"tutorial"


Quick Reference Card

JSON SYNTAX CHEAT SHEET
========================

Object:     {"key": "value", "key2": 42}
Array:      ["item1", "item2", "item3"]
String:     "Hello, world!"
Number:     42 or 3.14 or -7
Boolean:    true or false
Null:       null

ACCESS PATTERNS
===============
Dot notation:     data.name
Bracket notation: data["name"]  (use for keys with spaces/special chars)
Array index:      data.items[0]  (zero-indexed)
Nested:           data.user.address.suburb

COMMON OPERATIONS (JavaScript)
==============================
JSON.parse(string)         → string to object
JSON.stringify(obj)        → object to string
JSON.stringify(obj, null, 2)  → pretty-printed string

COMMON OPERATIONS (Python)
==========================
json.loads(string)         → string to dict
json.dumps(obj)            → dict to string
json.dumps(obj, indent=2)  → pretty-printed string

JQ CHEAT SHEET
==============
.                          → pretty-print entire JSON
.key                       → get value of key
.nested.key                → get nested value
.[0]                       → first array element
.[]                        → all array elements
.[] | select(.field == "x") → filter array
.[] | {name, email}        → extract specific fields
length                     → array/object length
keys                       → array of keys in object

Final Thoughts

JSON is the universal language of data exchange. If you’re working with n8n, Home Assistant, APIs, or any modern automation tool, you’re already swimming in JSON — you just need to learn to read it.

The key insights:

  1. There are only 6 types — string, number, boolean, null, array, object. Everything is built from these.
  2. Objects are key-value pairs ({}), arrays are ordered lists ([]).
  3. Nesting is just going deeperdata.user.address.suburb is like navigating a folder structure.
  4. The syntax rules are strict — double quotes only, no trailing commas, no comments. Get one character wrong and it won’t parse.
  5. Practice beats theory — open n8n, look at the JSON in your node outputs, and write expressions to access different fields. That’s the fastest way to learn.

Once you’re comfortable with JSON, everything else in n8n (and HA, and APIs) becomes dramatically easier.


This guide was compiled from JSON specification (RFC 8259), MDN Web Docs, and practical experience with n8n, Home Assistant, and REST APIs. Last updated: July 2026.