Use "type": "array" for ordered lists.
Core array keywords
| Keyword | What it does |
|---|
prefixItems | Positional schemas for tuple-style arrays |
items | Schema for each array element |
minItems / maxItems | Bounds array length |
contains, minContains, maxContains, uniqueItems and unevaluatedItems are not supported right now.
If you have a use case that requires them, reach out to us.
Example
{
"type": "object",
"properties": {
"values": {
"type": "array",
"items": {
"type": "number"
},
"minItems": 1,
"maxItems": 5
}
},
"required": ["values"],
"additionalProperties": false
}
from typing import Annotated
from pydantic import BaseModel, ConfigDict, Field
class Payload(BaseModel):
model_config = ConfigDict(extra="forbid")
values: Annotated[list[float], Field(min_length=1, max_length=5)]
import { z } from "zod";
const payloadSchema = z
.object({
values: z.array(z.number()).min(1).max(5),
})
.strict();
For capped payload size, always set maxItems. The default value of minItems is 0.
prefixItems
Use prefixItems for tuple-style arrays where position matters:
{
"type": "object",
"properties": {
"metric": {
"type": "array",
"prefixItems": [
{ "type": "string", "enum": ["latency_ms", "error_count"] },
{ "type": "number", "minimum": 0 }
],
"items": false
}
},
"required": ["metric"],
"additionalProperties": false
}
from typing import Annotated, Literal
from pydantic import BaseModel, ConfigDict, Field
class Payload(BaseModel):
model_config = ConfigDict(extra="forbid")
metric: tuple[Literal["latency_ms", "error_count"], Annotated[float, Field(ge=0)]]
import { z } from "zod";
const payloadSchema = z
.object({
metric: z.tuple([z.enum(["latency_ms", "error_count"]), z.number().min(0)]),
})
.strict();
This example enforces a two-element array: a metric name followed by a numeric value.