Writing valid JSON is easy. Writing good JSON — consistent, predictable, performant, and secure — requires deliberate choices about naming conventions, data modeling, error representation, and serialization strategies. These choices compound across an entire API surface: a well-designed JSON schema makes client code simple and robust, while a poorly designed one creates a constant stream of parsing bugs, type mismatches, and integration headaches.
The best practices in this guide are distilled from analyzing hundreds of production APIs (including Google, GitHub, Stripe, Twilio, and the OpenAPI ecosystem), reviewing common failure patterns in JSON-based systems, and synthesizing the recommendations of RFC 8259 (the JSON specification), RFC 7807 (Problem Details for HTTP APIs), and the JSON:API specification. Each practice is accompanied by concrete code examples showing both the recommended approach and the anti-patterns to avoid.
Whether you're designing a new API, refactoring an existing one, or reviewing code that produces or consumes JSON, these sixteen practices will help you build more reliable, maintainable, and performant systems. Use our JSON Formatter to validate and inspect your JSON as you apply these recommendations.
Validate Your JSON Design
Our free JSON Formatter validates structure, highlights issues, and beautifies output — perfect for reviewing API responses.
Open JSON Formatter →1. Naming Conventions
Consistent key naming is the foundation of a usable API. Inconsistent naming — mixing camelCase and snake_case, or using different names for the same concept across endpoints — forces consumers to maintain mental mappings and increases the likelihood of bugs.
| Convention | Example | Used By | Best When |
|---|---|---|---|
| camelCase | firstName, createdAt |
Google, Twitter | JavaScript/TypeScript ecosystems |
| snake_case | first_name, created_at |
GitHub, Stripe | Python/Ruby ecosystems |
| kebab-case | first-name |
Rare in JSON | Avoid in JSON (not valid unquoted in JS) |
| PascalCase | FirstName |
.NET APIs | C#/.NET ecosystems only |
2. Date and Time Representation
Dates are one of the most common sources of JSON interoperability bugs. Always use ISO 8601 format with explicit timezone information:
// ✅ Good: ISO 8601 with timezone
{
"createdAt": "2026-03-11T16:00:00Z",
"updatedAt": "2026-03-11T21:30:00+05:30",
"expiresAt": "2026-12-31T23:59:59Z"
}
// ❌ Bad: Ambiguous formats
{
"createdAt": "03/11/2026", // US format? EU format?
"updatedAt": 1741708200, // Seconds or milliseconds?
"expiresAt": "Dec 31, 2026" // Locale-specific
}3. Null vs Absent Fields
Distinguish between "this field has no value" (null) and "this field does not apply" (absent). Each has different semantics that consumers need to understand:
// User has no middle name (field present, value is null)
{
"firstName": "Alice",
"middleName": null,
"lastName": "Chen"
}
// User profile without optional bio (field absent)
{
"firstName": "Alice",
"lastName": "Chen"
// bio field not included — not applicable
}For PATCH (partial update) operations, this distinction is critical: sending "middleName": null means "clear this field," while omitting the field means "don't change it."
4. Error Response Structure
Use a consistent, structured error format across your entire API. The RFC 7807 "Problem Details" format is a well-established standard:
// ✅ Good: Structured error with actionable information
{
"type": "https://api.example.com/errors/validation",
"title": "Validation Error",
"status": 422,
"detail": "The request body contains invalid fields.",
"errors": [
{
"field": "email",
"code": "INVALID_FORMAT",
"message": "Must be a valid email address"
},
{
"field": "age",
"code": "OUT_OF_RANGE",
"message": "Must be between 0 and 150"
}
]
}
// ❌ Bad: Vague string error
{
"error": "Something went wrong"
}5. Pagination
For endpoints that return collections, always implement pagination. The two most common approaches are offset-based (simple, supports jumping to any page) and cursor-based (performant, handles real-time data insertion):
| Approach | Pros | Cons | Best For |
|---|---|---|---|
| Offset-based | Simple, supports page jumping | Slow on large datasets, skips/duplicates on inserts | Admin dashboards, static data |
| Cursor-based | Fast, handles real-time inserts | Can't jump to page N | Feeds, timelines, large datasets |
6. Envelope vs Raw Responses
Wrapping responses in an envelope object provides a consistent structure for metadata, pagination, and error handling. This is especially useful when your API needs to communicate information beyond the primary data:
// Envelope pattern — consistent structure across all endpoints
{
"data": {
"id": "user_123",
"name": "Alice",
"email": "alice@example.com"
},
"meta": {
"requestId": "req_abc123",
"timestamp": "2026-03-11T16:00:00Z"
}
}
// Collection with pagination
{
"data": [
{ "id": "user_1", "name": "Alice" },
{ "id": "user_2", "name": "Bob" }
],
"pagination": {
"page": 1,
"perPage": 20,
"total": 150,
"hasMore": true
}
}7. Number Handling and Precision Loss
JSON numbers follow IEEE 754 double-precision floating point. This means integers larger than 2^53 (9,007,199,254,740,992) lose precision, and decimal arithmetic has floating-point rounding issues:
// ❌ Problem: Large IDs lose precision
{ "id": 9007199254740993 } // Parsed as 9007199254740992!
// ✅ Solution: Use string IDs for large numbers or UUIDs
{ "id": "9007199254740993" }
// ❌ Problem: Monetary amounts with floating-point errors
{ "price": 19.99 } // Could become 19.990000000000002
// ✅ Solution: Use integer cents (or string)
{ "priceInCents": 1999 }
{ "price": "19.99" }8. Flattenting Deeply Nested Structures
Deeply nested JSON leads to massive payload sizes, complicated client-side mapping (especially in strictly typed languages like Java or Go), and parser performance degradation. Instead of embedding full child objects within parent objects, practice data normalization using explicit references.
// ❌ Bad: Deep nesting (hard to parse, high duplication)
{
"post_id": "123",
"title": "JSON Practices",
"author": {
"user_id": "99",
"name": "Alice",
"company": {
"company_id": "88",
"name": "Acme Corp"
}
}
}
// ✅ Good: Normalized references (flat, fast to parse)
{
"post_id": "123",
"title": "JSON Practices",
"author_id": "99"
}If the client needs the author or company data, they can either make a separate request or use a GraphQL-style inclusion parameter (?include=author) to return a flattened dictionary of side-loaded entities.
9. Preventing JSON Hijacking
JSON Hijacking is an old but still relevant security vulnerability where a malicious site uses a <script> tag to GET an array-based JSON endpoint, overriding the native Array constructor to steal the data.
Modern browsers have mitigated this, but best practice dictates you should never return a raw JSON array at the root level of an authenticated GET request.
// ❌ Vulnerable to Hijacking (Raw Array)
[
{ "secret": "token_123" },
{ "secret": "token_456" }
]
// ✅ Secure (Wrapped in an Object Envelope)
{
"data": [
{ "secret": "token_123" },
{ "secret": "token_456" }
]
}10. Strict Content-Type Headers
Always transmit JSON with the exact header Content-Type: application/json; charset=utf-8. Never use text/plain or text/html. If you serve JSON with a text/html header, and user input is reflected in the JSON, a browser might attempt to execute the payload as HTML if accessed directly, leading to Cross-Site Scripting (XSS) vulnerabilities.
Furthermore, explicitly instruct browsers not to sniff the content type by sending the X-Content-Type-Options: nosniff header on every API response.
11. Dealing with Enums and State
When an API returns a state or category, never use arbitrary integer codes unless absolutely necessary for micro-optimization. Use explicit, uppercase string enums. They are self-documenting and easier to debug.
// ❌ Bad: Magic Numbers (What does 2 mean?)
{
"orderId": "123",
"status": 2
}
// ✅ Good: String Enums (Self-documenting)
{
"orderId": "123",
"status": "PROCESSING"
}12. Robust Client-Side Parsing
JSON parsing must always be treated as a potentially fatal operation. Never trust that an API (even internal ones) will return valid JSON or respect the expected schema. A network error, proxy timeout, or upstream crash often results in an HTML error page being returned instead of JSON.
Always wrap your `JSON.parse` operations in try-catch blocks, or rely on robust fetching libraries (like Axios) that handle the parsing and error throwing automatically.
13. Performance Optimization
JSON payloads can become significant performance bottlenecks as applications scale. Here are the key techniques for keeping JSON fast:
Minimize payload size: Only include fields that the client actually needs. Use sparse fieldsets (?fields=name,email) or GraphQL to let clients specify required fields. Exclude computed or rarely-used fields from default responses.
Enable compression: Gzip compression reduces JSON payload size by 70-90% for transmission. Most web servers and CDNs support automatic compression. Always set Accept-Encoding: gzip in API requests.
Use streaming for large data: For files over 100 MB, use streaming JSON parsers (like JSONStream in Node.js or ijson in Python) instead of JSON.parse(), which loads the entire document into memory.
14. API Versioning
When your JSON response structure needs to change in ways that break existing clients, use API versioning. The three common approaches:
| Method | Example | Pros | Cons |
|---|---|---|---|
| URL path | /v1/users, /v2/users |
Obvious, easy to implement | URL duplication |
| Header | Accept: application/vnd.api+json;v=2 |
Clean URLs | Harder to test in browser |
| Query param | /users?version=2 |
Easy to test | Can conflict with other params |
Format & Validate Your API JSON
Review your API response quality — paste any JSON into our formatter for instant structure analysis.
Open JSON Formatter →Frequently Asked Questions
What naming convention should I use for JSON keys?
Should I use null or omit missing fields in JSON?
null when a field is known but empty (e.g., no middle name). Omit the field when it doesn't apply. For PATCH operations: null = "clear this field," absent = "don't change it."
How should I represent dates in JSON?
"2026-03-11T16:00:00Z". Never Unix timestamps (seconds vs milliseconds ambiguity), locale-specific formats ("03/11/2026"), or partial formats.
How do I handle errors in JSON API responses?
"VALIDATION_ERROR"), human message, and field-level details array. Follow RFC 7807 for standardized format. Never return 200 with error in body.
How can I improve JSON parsing performance?
Related Resources
- JSON to CSV Converter — Try it free on DominateTools
- JSON Syntax Guide — Complete JSON reference
- JSON vs XML vs YAML — Format comparison
- Debug JSON API Responses — Troubleshooting guide
- JSON Schema Validation — Enforce data rules
- Free JSON Formatter — Validate and beautify JSON