JSON Arrays vs Objects: When to Use Each
Every JSON document is built from exactly two container types: arrays and objects. The syntax difference takes one sentence to explain. The modelling difference — knowing which one a given piece of data should be — is what separates an API that clients enjoy from one that generates support tickets.
This post is about the choice, not the syntax. If you need the underlying rules first, The Complete Guide to JSON covers the grammar and the six data types.
The mechanical difference
An array is an ordered list in square brackets. Elements are accessed by zero-based numeric index. Order is part of the data — ["a", "b"] and ["b", "a"] are different documents. Duplicates are fine.
An object is an unordered set of key-value pairs in curly braces. Values are accessed by key name. Keys must be unique in practice and must be double-quoted strings. Order is explicitly not part of the data, even though most parsers happen to preserve insertion order.
{
"orderId": "A-8842",
"status": "shipped",
"items": [
{ "sku": "TSH-01", "qty": 2, "price": 19.99 },
{ "sku": "MUG-07", "qty": 1, "price": 8.50 }
],
"shippedAt": "2026-08-07T09:14:00Z"
}
The order itself is an object: it has a fixed set of named fields that mean different things. items is an array: an arbitrary number of things of the same kind, where the sequence might matter for display. Each item is an object again. That pattern — objects for records, arrays for collections of records — covers most real payloads.
The decision rule
Ask two questions.
Are these things the same kind of thing? If yes, and there can be zero, one, or many of them, use an array. Products, log entries, users, tags, coordinates. If the fields mean different things — name, email, createdAt — use an object.
Does the order carry information? Arrays preserve it, objects do not. Search results ranked by relevance, steps in a workflow, timeseries points, a playlist — all arrays, because reordering them destroys meaning. A user's profile fields have no meaningful order, so an object is correct.
The failure mode you want to avoid is the numeric-keyed object:
{ "0": "apple", "1": "banana", "2": "cherry" }
This is a list wearing an object costume. It usually appears when a language serialises a sparse array or a PHP array, and it forces every consumer to sort string keys numerically to recover the order. If it is a list, make it an array.
When an object beats an array for a collection
There is one legitimate exception, and it is a good one: keying a collection by ID.
{
"users": {
"usr_8Kq2": { "name": "Ada", "role": "admin" },
"usr_31Bd": { "name": "Grace", "role": "editor" }
}
}
Compare with the array form, where each object carries its own id field. The keyed version wins when consumers mostly do lookups: users["usr_8Kq2"] is a direct hash access, whereas the array requires scanning or building an index client-side. It also makes duplicate IDs structurally impossible, and it produces cleaner diffs — adding a user changes one line rather than shifting indices.
The array wins when order matters, when you paginate or stream, when the collection is large enough that you want to process it incrementally, or when the same entity can legitimately appear twice. Normalised client-side stores (Redux, Apollo's cache) use keyed objects internally for exactly the lookup reason, and it is common to send an array over the wire and normalise on arrival.
Top-level shape: always an object
Returning a bare array from an API endpoint is a decision you cannot undo.
["a", "b", "c"]
That is valid JSON and it works fine — until you need to add a total count, a next-page cursor, a deprecation warning, or a partial-failure list. Any of those requires changing the top-level type, which breaks every existing client at once.
Wrap it instead:
{
"data": [ { "id": 1 }, { "id": 2 } ],
"page": { "next": "cur_9f2a", "total": 4821 }
}
Now metadata is additive. New optional fields at the top level are backwards compatible, because a client reading data ignores everything it does not know about. There was also a historical security argument — a top-level array was exploitable via JSON hijacking in older browsers — which no longer applies to modern engines but did shape the convention.
The same instinct applies to single values. An endpoint that returns 42 or "ok" is technically valid JSON under RFC 8259, and it is still worth wrapping in an object so the response has somewhere to grow.
Consistency beats cleverness
Two rules save more client-side code than any structural choice.
Never let a field change type. A field that is an object when there is one result and an array when there are several is the most user-hostile shape in API design. Consumers have to type-check on every access. Always return an array, even when it has zero or one element — an empty array is a perfectly good "nothing here".
Prefer explicit null to a missing key. If deletedAt is absent when the record is live and present when it is deleted, a consumer cannot distinguish "not deleted" from "the API did not send this field this time". Sending "deletedAt": null makes the contract self-describing. Omit keys only when absence is genuinely meaningful, such as a sparse patch payload.
Keep arrays homogeneous. [1, "two", null, {"three": 3}] is legal JSON and terrible data. Consumers should be able to write one loop body, not a type switch.
Nesting: how deep is too deep
The spec puts no limit on nesting. Parsers do, usually a few hundred to a few thousand levels, to avoid blowing the stack — and deeply nested untrusted input is a real denial-of-service vector, which is why you should cap depth when parsing anything from outside your system.
The practical limit arrives much earlier. Past three or four levels, a payload becomes hard to reason about, hard to type, and expensive to traverse defensively — data.user.profile.settings.notifications.email needs a null check at every step. When you hit that, the usual fixes are to flatten related fields into the parent, split the response into separate endpoints, or normalise into ID-keyed collections and let the client join.
Reading deep structures in raw text does not scale. A JSON Tree Viewer renders the document as a collapsible tree so you can fold branches you do not care about, and it makes array-versus-object mistakes immediately obvious — a numeric-keyed object looks visibly different from a real array. Pair it with a JSON Formatter when you just need indentation, and both run entirely in your browser, so you can inspect a production response containing real customer data without uploading it anywhere.
Structure decides what you can do next
The array-versus-object choice has downstream consequences that are easy to miss when you are designing a payload.
Querying. Extracting data with JSONPath is straightforward over arrays of uniform objects — $.items[*].sku gets you every SKU in one expression. Over an ID-keyed object you need a wildcard on the keys instead, and filters get more awkward. If a collection is going to be queried and filtered a lot, an array of uniform objects is the friendlier shape; JSONPath queries covers the syntax, and a JSONPath Query Tester lets you check an expression against your own document before you write code around it.
Tabular export. An array of flat objects converts to CSV or a spreadsheet almost mechanically — one row per element, one column per key. Nested objects and arrays inside those elements are where conversion gets lossy and you have to choose a flattening strategy, which is the whole subject of converting JSON to CSV without losing nested data.
If you know a payload will end up in a spreadsheet or a data warehouse, an array of flat, uniform objects at the top of the data field is the shape that makes everything downstream easy. Design for the consumer, not for whatever your ORM happened to serialise.
Frequently asked questions
What is the difference between a JSON array and a JSON object?
An array uses square brackets and holds an ordered list of values accessed by numeric index. An object uses curly braces and holds unordered key-value pairs accessed by name. Use arrays for collections of similar things, objects for a set of named fields.
Is the order of keys in a JSON object guaranteed?
No. The specification defines objects as unordered, so a conforming parser may return keys in any order. Most implementations preserve insertion order in practice, but never write code that depends on it. Use an array if order is meaningful.
Should an API return an array or an object at the top level?
Return an object. A top-level object lets you add pagination, metadata, or error fields later without breaking clients, whereas a top-level array locks the shape permanently. Wrap the collection in a data or items field.
Can a JSON array hold different types?
Yes, the grammar allows [1, "two", null, {}] and it is valid JSON. It is still a bad idea for API payloads, because consumers must type-check every element instead of processing the list uniformly.
When should I use an object keyed by ID instead of an array?
Use an ID-keyed object when consumers mostly look items up by identifier and order does not matter — it turns an O(n) scan into a direct lookup. Use an array when order matters, duplicates are possible, or the collection is streamed or paginated.
Can JSON objects have duplicate keys?
The grammar does not forbid them, but behaviour is undefined. Most parsers keep the last occurrence, some keep the first, and some raise an error. Never produce duplicate keys — the document is not portable if you do.
How deeply can JSON be nested?
The specification sets no limit, but every parser imposes a practical depth cap to prevent stack exhaustion, often a few hundred to a few thousand levels. Past three or four levels, deep nesting is usually a modelling problem rather than a technical one.
Try the related tools
JSON Formatter & Beautifier
Format, indent, validate, and beautify JSON with custom spacing.
JSON Tree Viewer
Visualize JSON hierarchy with collapsible outline tree view.
JSONPath Query Tester
Query and extract elements from complex JSON data using JSONPath syntax.
Related articles
The Complete Guide to JSON
A complete JSON guide for developers: syntax rules, data types, escape characters, common errors, minifying vs beautifying, and how JSON works in APIs.
How to Convert JSON to CSV Without Losing Nested Data
Convert JSON to CSV without losing nested data: flattening strategies for nested objects and arrays, CSV escaping rules, and clean output in Excel and Sheets.
JSONPath Queries: Finding Data in Big JSON Files
A practical JSONPath guide: selectors, wildcards, slices and filter expression syntax, with worked examples for pulling data out of large JSON API responses.