Mastering JSON Schema additionalProperties for Flexible Validation

Neel Das avatar
Mastering JSON Schema additionalProperties for Flexible Validation

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

  • additionalProperties controls unknown fields: It defines validation rules for properties not listed in properties or matched by patternProperties.
  • 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 false for strict inputs: Setting additionalProperties: false on 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 additionalProperties is the best approach.

Table of Contents

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.

JSON Schema validator

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 in properties or patternProperties are 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 isAdmin flag.
  • 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. author is an extra property, and its value is a non-empty string.
  • { "theme": "light", "timeout": 1000, "version": 1.2 }Invalid. The value 1.2 is a number, not a string.
  • { "theme": "dark", "timeout": 3000, "note": "" }Invalid. The value for note violates the minLength: 1 constraint.

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:

  1. properties: The validator first checks for specific, named keys like "id". If a property name matches, this rule applies.
  2. patternProperties: If a property wasn’t matched, the validator tries to match its name against regular expressions here. The first pattern that matches wins.
  3. additionalProperties: This is the last resort. Only properties that slipped past the first two checks are handed off to additionalProperties.

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. userId and email are handled by properties. data-team matches the ^data- regex. Nothing is left for additionalProperties.



  • Invalid Data: { "userId": "u-456", "email": "[email protected]", "isAdmin": true }
    This fails. isAdmin doesn’t match anything in properties or patternProperties. It falls down to additionalProperties: false and 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 false for Request Bodies: When building an endpoint that accepts data, additionalProperties: false is a smart security move. It’s a great defense against mass assignment vulnerabilities.
  • Be Cautious with false in Responses: For data you send back, think twice before setting additionalProperties: 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 metadata object is a string.

Infographic about json schema additionalproperties

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 additionalProperties and 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

Discover more from DeepDocs

Subscribe now to keep reading and get access to the full archive.

Continue reading