OpenAPI parameters seem straightforward until you're debugging why your generated SDK has confusing method signatures or your API consumers keep passing data in the wrong places. The difference between path, query, and header parameters isn't just about syntax—it's about creating predictable, cacheable APIs that translate into clean, intuitive SDKs.
This guide covers the practical distinctions between OpenAPI parameter types, when to use each one, and how your choices directly impact the developer experience in generated SDKs. You'll learn the resource-filter-metadata rule for parameter placement, how to handle complex data serialization, and why parameter design decisions matter even more when building MCP servers for AI agents.
OpenAPI parameter types at a glance
In OpenAPI, you place parameters in the path to identify a specific resource, like /users/{id}
. You use the query to filter or sort a collection, such as /users?role=admin
, and the header for cross-cutting concerns like authentication, for example Authorization: Bearer ...
. Choosing the right location is crucial for creating a predictable, cacheable, and easy-to-use API and corresponding SDK.
While OpenAPI also supports cookie parameters, we'll focus on the three most common types used in modern REST APIs: path, query, and header. Understanding their distinct roles is the first step toward designing clean, intuitive API contracts that developers will love to use.
Parameter Type | Purpose | Typically Required? | Example |
---|---|---|---|
Path | Uniquely identify a specific resource. | Yes |
|
Query | Filter, sort, or paginate a list of resources. | No |
|
Header | Pass metadata like auth tokens or API versions. | Varies |
|
View one endpoint end-to-end
Let's look at a single API call that uses all three parameter types. A request to fetch a specific user might look like this:
GET /users/usr_123?include_profile=true
With an Authorization
header containing a bearer token.
The OpenAPI definition for this endpoint combines these parameter types to describe the full request structure. When you create OpenAPI specs with clear parameter definitions, the generated SDK can produce methods with distinct arguments for the path, query, and auth, making the developer's job much simpler.
Use path parameters for resource identifiers
Use path parameters to uniquely identify a specific resource or a sub-resource within a collection. Think of them as the primary key for your endpoint. Because they are essential for locating the resource, path parameters are always required.
For example, GET /accounts/{accountId}
uses {accountId}
to pinpoint exactly which account the user wants to retrieve.
Define path parameters in a spec
In your OpenAPI specification, you define a path parameter directly in the path string with curly braces and detail it in the parameters
section. You must set in: path
and required: true
.
Call path parameters from a Stainless SDK
A well-defined path parameter translates directly into a required argument in a generated SDK method. This provides compile-time safety, ensuring developers can't forget to pass the necessary identifier. Remember that your API isn't finished until the SDK ships, and well-designed parameters are crucial for a great SDK experience.
TypeScript
Python
Use query parameters for filtering and pagination
Use query parameters to modify the response for a collection of resources. They are perfect for optional actions like filtering, sorting, and paginating results. Unlike path parameters, query parameters are not part of the core resource path and are typically optional.
For instance, GET /transactions?status=completed
filters a list of transactions to show only the completed ones.
Add simple filters in a spec
To define a query parameter, you specify in: query
in your parameters
section. They are usually not required, allowing clients to request the default resource collection without any filters.
Configure pagination helpers in Stainless
Query parameters are the foundation of API pagination. By defining standard pagination parameters like limit
and after_cursor
in your OpenAPI spec, you can generate SDKs with powerful, built-in helpers that make iterating through large datasets feel effortless.
TypeScript
Python
Use header parameters for cross-cutting metadata
Use header parameters for sending metadata that applies across many different requests, rather than being specific to a single resource. This includes authentication credentials, API versioning, idempotency keys, and content negotiation.
Headers keep the URL clean and focused on the resource itself, while the metadata travels alongside the request.
Define header parameters in a spec
You define header parameters with in: header
. While you can define them per-operation, they are often defined as part of a reusable security scheme for authentication.
Pass header parameters through a Stainless SDK
In a generated SDK, authentication headers are typically handled seamlessly through the client's constructor. The developer provides their API key once, and the SDK automatically adds the Authorization
header to every subsequent request.
TypeScript
Choose the right parameter type
Choosing the correct parameter type makes your API more predictable and easier to use. A good rule of thumb is to think about the data's role: is it identifying, modifying, or contextual?
Follow the resource–filter–metadata rule
Identifier: If it uniquely identifies a resource, use a
path
parameter.Modifier: If it filters, sorts, or paginates a list of resources, use a
query
parameter.Metadata: If it's cross-cutting information like an auth token, use a
header
parameter.
Refactor a confusing endpoint
Consider an endpoint designed to fetch a user's posts: GET /users_posts?user_id=123&status=published
.
This design is confusing. It mixes an identifier (user_id
) with a filter (status
) in the query string. A better, more RESTful design separates these concerns.
Better Design: GET /users/123/posts?status=published
/users/123/posts
clearly identifies the resource collection using path parameters.?status=published
correctly uses a query parameter to filter that collection.
This improved structure is not only clearer for humans but also results in a more intuitive SDK, like client.users.posts.list('123', { query: { status: 'published' } })
.
Serialize arrays and objects safely
Sending complex data like arrays or objects in query parameters requires careful definition in your OpenAPI spec. Using the style
and explode
keywords, you can control how these structures are serialized into the URL string.
Getting this right ensures that your API correctly interprets the data and that generated SDKs can abstract this complexity away from the end user.
Send arrays in query parameters
You can send an array of tags like ['red', 'blue']
in a few different ways. The most common are comma-separated or repeated key-value pairs.
explode: false
(default):GET /items?tags=red,blue
explode: true
:GET /items?tags=red&tags=blue
Your OpenAPI definition determines which format your API expects, and a generated SDK will handle the serialization automatically. You can edit configs and OpenAPI specs with branches to safely test different serialization formats before committing to a design.
Pass objects with deepObject style
For structured filters, like filtering by a price range, you can use objects. The deepObject
style is useful for representing nested data clearly in the URL.
An object like { "price": { "gte": 100 } }
becomes GET /products?filter[price][gte]=100
. This is much more readable than trying to flatten the object into a single string.
Frequently asked questions about OpenAPI parameters
Can a parameter appear in both path and query?
While technically possible in OpenAPI, it is a bad practice. It creates ambiguity and leads to confusing SDKs, as it's unclear which method argument should map to which parameter. Always use unique names for your parameters within an operation.
How do parameter changes influence SDK versions?
Adding an optional query parameter is a non-breaking change (minor version bump). However, making a query parameter required or adding a new path parameter is a breaking change that requires a major version bump, as it changes the method signature in an SDK. If you need to maintain backwards compatibility or add special handling, you can add custom code that persists through SDK regeneration.
Where should sensitive values like passwords go?
Never put sensitive data in path or query parameters, as they can be logged by servers or stored in browser history. Use an Authorization
header for tokens or the request body for credentials, and always communicate over HTTPS.
Does parameter complexity affect MCP servers?
Does parameter complexity affect mcp servers? Yes, significantly. LLMs interacting with your API via an MCP server perform best with simple, clear tools. When you generate an MCP server from an OpenAPI spec, parameter complexity directly impacts how effectively AI agents can understand and use your tools.
Path parameters and simple query filters create clean tool schemas that are easy for an AI to understand and use correctly. Our experience converting complex OpenAPI specs to MCP servers shows that simpler parameter designs lead to more reliable AI tool usage.
Should API versioning use headers or URL paths?
Both GET /v2/users
(path) and Api-Version: 2
(header) are valid strategies. Path-based versioning is more explicit, while header-based versioning keeps the resource URI stable. Choose one and be consistent; a well-designed SDK can be configured to handle either approach seamlessly.
Ready to turn your well-designed OpenAPI spec into a first-class SDK? Get started for free.