In JSON Schema, the additionalProperties keyword acts as a gatekeeper for your data structures. In my experience, getting a handle on this one is critical. It’s the key to building APIs that are flexible enough to evolve but strict enough to shut down bad data before it gets in.
TL;DR: Key Takeaways
additionalPropertiescontrols unknown fields: It defines validation rules for properties not listed inpropertiesor matched bypatternProperties.- Three modes of operation: It can be set to
true(allow anything, the default),false(forbid anything extra), or a sub-schema (enforce specific rules on extra fields). - Default is permissive (
true): By default, JSON Schema allows any extra properties. This is a deliberate design choice for forward compatibility, but it often surprises developers. - Use
falsefor strict inputs: SettingadditionalProperties: falseon API request bodies is a best practice for security, preventing mass assignment vulnerabilities. - Use sub-schemas for controlled flexibility: When you need to allow dynamic keys but enforce a consistent value structure (like for metadata or tags), using a schema for
additionalPropertiesis the best approach.
Table of Contents
- What Are additionalProperties in JSON Schema?
- The Default Behavior: Permissive by Design
- Enforcing Strict Contracts with additionalProperties: false
- The Advanced Pattern: Using a Sub-Schema
- How additionalProperties Interacts with Other Keywords
- Common Pitfalls and Best Practices
- Common Questions and Sticking Points
What Are additionalProperties in JSON Schema?
Imagine you’re designing an API for a user profile. You start by defining the essentials, like username and email.
But what happens down the road when a new feature needs to add a lastLogin field? Or, what if a client suddenly starts sending an unexpected isAdmin property?
This is exactly the problem additionalProperties was designed to solve. It sets the rules for any of these unplanned, “additional” properties that aren’t part of your original blueprint.
Think of it as a switch with three settings. The setting you choose directly impacts how strict or forgiving your API is, making it one of the most important keywords you’ll need to master.
The Three Modes of Operation
At a high level, additionalProperties gives you three distinct ways to control your object’s structure:
- Permissive (
true): This is the default. It says, “anything goes,” allowing any number of extra properties. This mode is great for systems where new fields might be added over time without breaking older clients. - Strict (
false): This setting locks the object down completely. Only properties you’ve explicitly defined inpropertiesorpatternPropertiesare allowed. It’s perfect for security-sensitive endpoints where you need absolute certainty about the data’s shape. - Conditional (Schema): This offers a powerful middle ground. You provide another schema that all additional properties must conform to. For example, you could require all extra fields to be strings.
Understanding these three options is the first step toward creating robust and predictable APIs. Choosing the right mode from the start prevents countless validation headaches down the line.
The Default Behavior: Permissive by Design
One of the first things that trips up developers new to JSON Schema is its default behavior. It’s surprisingly… relaxed.
If you don’t explicitly say anything about extra properties, it lets them slide right through. In other words, additionalProperties is implicitly set to true.
This feels wrong at first, doesn’t it? But this permissive default is a deliberate design choice with a critical purpose: forward compatibility.
A Feature, Not a Bug
Let’s say you’re building an API that processes customer orders. Your first version of the schema might only need an orderId and an amount.
{
"type": "object",
"properties": {
"orderId": { "type": "string" },
"amount": { "type": "number" }
},
"required": ["orderId", "amount"]
}
Now, imagine a client sends this JSON object, which includes an extra promoCode field.
{
"orderId": "ORD-12345",
"amount": 99.99,
"promoCode": "SAVE20"
}
Because our schema defaults to additionalProperties: true, the object above is perfectly valid. The validator checks orderId and amount, sees they’re correct, and simply ignores promoCode.
This flexibility is a lifesaver. It means you can evolve your API, adding new, optional fields, without breaking older clients.
This design prevents your system from becoming brittle. If schemas were strict by default, every minor addition would be a breaking change, forcing a cascade of updates. The permissive nature of additionalProperties allows for graceful evolution, which is essential for maintainable systems.
Enforcing Strict Contracts with additionalProperties: false
While the default is great for flexibility, sometimes you need the exact opposite. This is where setting additionalProperties to false becomes your most powerful tool.
It transforms your schema from a guideline into a rigid blueprint. Only the properties you’ve explicitly defined are allowed. Period.
This setting slams the gate shut on unknown fields. If a client sends even one extra property, the entire object will fail validation.

A Practical Example of Strict Validation
Let’s go back to our user profile schema, but this time, we’ll lock it down.
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Strict User Profile",
"type": "object",
"properties": {
"username": { "type": "string" },
"email": { "type": "string", "format": "email" }
},
"required": ["username", "email"],
"additionalProperties": false
}
Now, imagine this JSON payload comes in:
{
"username": "testuser",
"email": "[email protected]",
"isAdmin": true
}
With additionalProperties: false in place, this object is invalid. The isAdmin property isn’t defined, so the validator rejects the entire payload.
When to Enforce Strictness
Deciding to set additionalProperties to false is a strategic choice. It’s particularly useful in these scenarios:
- Securing API Endpoints: By rejecting unknown properties, you can help shut down mass assignment vulnerabilities. You prevent a malicious user from injecting fields they shouldn’t have access to, like that sneaky
isAdminflag. - Defining Precise Configurations: For config files, strict validation ensures there are no typos or misunderstood options. An unknown property is an error, preventing silent failures.
- High-Stakes Data Exchange: Think financial transactions or healthcare records. A strict schema ensures you process exactly what you expect, and nothing more.
By default, JSON Schema is permissive, but a huge number of developers rely on additionalProperties: false to enforce data integrity. To see how these principles apply in the real world, check out our guide on making a great API.
Of course, this approach has trade-offs. It makes your API less adaptable. The key is to find the right balance, applying strictness where security and precision are non-negotiable.
The Advanced Pattern: Using a Sub-Schema
So far, we’ve treated additionalProperties as a simple on/off switch. But its real power comes from using another schema as its value.
This pattern lets you define rules that all extra properties must follow. It’s like telling a bouncer, “Anyone not on the main list is welcome, but they must all be wearing a blue shirt.”
Defining Rules for Extra Properties
Let’s look at a practical example. Imagine a config file for a web app. We have core settings like theme and timeout, but we also want to let developers add custom metadata. We need to ensure all the metadata values are non-empty strings.
Here’s how we’d write that schema:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "App Configuration with Metadata",
"type": "object",
"properties": {
"theme": { "type": "string", "enum": ["dark", "light"] },
"timeout": { "type": "integer", "minimum": 0 }
},
"required": ["theme", "timeout"],
"additionalProperties": {
"type": "string",
"minLength": 1
}
}
In this schema, any property that isn’t theme or timeout will be checked against the sub-schema: { "type": "string", "minLength": 1 }.
Let’s test a few objects:
{ "theme": "dark", "timeout": 5000, "author": "dev-team" }– Valid.authoris an extra property, and its value is a non-empty string.{ "theme": "light", "timeout": 1000, "version": 1.2 }– Invalid. The value1.2is a number, not a string.{ "theme": "dark", "timeout": 3000, "note": "" }– Invalid. The value fornoteviolates theminLength: 1constraint.
This technique gives you fine-grained control, ensuring even unplanned data follows a predictable structure.
Common Use Cases for Sub-Schemas
This pattern is a lifesaver in many real-world scenarios. We’ve found it shines brightest when handling dynamic data without throwing validation out the window.
- User-Defined Metadata: Allowing users to attach custom key-value tags to resources (like AWS tags) is a classic example. You can enforce that all metadata values must be strings or numbers.
- Plugin Architectures: For config files that support third-party plugins, you can define a schema for what a valid plugin configuration looks like and apply it to all non-core properties.
- Key-Value Stores: If you’re building an API that functions like a key-value store, a sub-schema ensures every value conforms to a predefined structure, even though keys are dynamic.
How additionalProperties Interacts with Other Keywords
When you mix additionalProperties with properties and patternProperties, its behavior gets more interesting.
The key takeaway is this: additionalProperties only kicks in for properties that haven’t already been matched by properties or patternProperties. This creates a clean hierarchy for precise validation.
Understanding the Validation Order
Picture your schema applying rules in three layers:
properties: The validator first checks for specific, named keys like"id". If a property name matches, this rule applies.patternProperties: If a property wasn’t matched, the validator tries to match its name against regular expressions here. The first pattern that matches wins.additionalProperties: This is the last resort. Only properties that slipped past the first two checks are handed off toadditionalProperties.
This behavior was clarified in Draft 7 of JSON Schema to avoid ambiguity. You can dive into the history in the official JSON Schema discussions.
A Layered Validation Example
Let’s define a user object. We need a couple of required fields, we want to allow custom fields that start with data-, but we want to forbid everything else.
Here’s the schema:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "User with Custom Data",
"type": "object",
"properties": {
"userId": { "type": "string" },
"email": { "type": "string", "format": "email" }
},
"patternProperties": {
"^data-": { "type": "string" }
},
"required": ["userId", "email"],
"additionalProperties": false
}
Now, let’s test it:
Valid Data:
{ "userId": "u-123", "email": "[email protected]", "data-team": "alpha" }
This object is valid.userIdandemailare handled byproperties.data-teammatches the^data-regex. Nothing is left foradditionalProperties.Invalid Data:
{ "userId": "u-456", "email": "[email protected]", "isAdmin": true }
This fails.isAdmindoesn’t match anything inpropertiesorpatternProperties. It falls down toadditionalProperties: falseand is rejected.
Getting a handle on this hierarchy separates a basic schema from a robust one. For more hands-on designs, check out our OpenAPI examples.
Common Pitfalls and Best Practices
Like any powerful tool, additionalProperties can cause headaches if you’re not careful. I’ve seen developers fall into a few common traps, like making APIs brittle or misunderstanding the default behavior.
One frequent mistake is jumping to additionalProperties: false on API responses. Sure, it feels strict, but it paints you into a corner. The moment you add a minor field in the future, you risk breaking every client that relies on that rigid schema.
Avoiding Common Mistakes
To build schemas that last, it pays to follow a few principles.
- Default to
falsefor Request Bodies: When building an endpoint that accepts data,additionalProperties: falseis a smart security move. It’s a great defense against mass assignment vulnerabilities. - Be Cautious with
falsein Responses: For data you send back, think twice before settingadditionalProperties: false. Leaving it open means you can add new properties later without breaking older clients. - Use a Sub-Schema for Controlled Extensibility: A sub-schema is perfect for the middle ground. It lets you allow dynamic properties but still enforce rules, like making sure every value in a
metadataobject is a string.

Caption: This diagram shows additionalProperties as the final check after properties and patternProperties.
This visual drives home a key point: additionalProperties only applies to properties not covered by more specific rules.
Document Your Decisions
Finally, don’t forget that clear communication is everything. The way you handle additionalProperties has real-world consequences, as seen in the tricky data transitions discussed in the JSON Schema community.
Always document your choice for
additionalPropertiesand the reasoning behind it. Whether you’re aiming for strictness or flexibility, your API consumers need to know what to expect.
Good documentation elevates your schema from a simple validation file to a reliable contract. If you want to dig deeper, check out our guide on API documentation best practices.
Common Questions and Sticking Points
Even after you get the hang of additionalProperties, a few questions tend to pop up. Let’s walk through the most frequent ones.
How Can I Use additionalProperties to Ban All Properties?
On its own, additionalProperties: false only applies to undefined properties. To block everything, combine it with empty properties and patternProperties objects.
{
"type": "object",
"properties": {},
"patternProperties": {},
"additionalProperties": false
}
This setup tells the validator that there are no allowed named properties and no allowed patterns, so any property is considered “additional” and will be rejected. This is the go-to method for enforcing a truly empty object.
How Do I Make a Property Optional?
It’s easy to mix up additionalProperties with property optionality. additionalProperties handles unknown properties.
To make a known property optional, you use the required array. Define the property in your properties object but leave its name out of the required list.
For instance, to make email optional:
{
"type": "object",
"properties": {
"username": { "type": "string" },
"email": { "type": "string" }
},
"required": ["username"]
}
Here, email is valid, but an object will pass validation whether email is present or not.
What Is the Default Value of additionalProperties?
This is the single most important detail to remember: the default value for additionalProperties is true.
If you don’t explicitly add additionalProperties: false, JSON Schema will allow any extra properties. This was a deliberate choice to support forward compatibility, but it means you must be intentional about locking down your schemas when you need strict validation.
Tired of your documentation drifting from your code? DeepDocs is a GitHub-native AI app that automatically detects outdated docs and keeps them in sync with every commit. It intelligently updates only what’s needed, preserving your style and formatting. Stop manual updates and ensure your READMEs, API references, and tutorials are always accurate. Get started for free at deepdocs.dev.

Leave a Reply