Converting Between Postman Collections and OpenAPI Specs
Postman collections and OpenAPI specs describe the same thing — API requests — but for different purposes. A collection is built for running requests interactively; a spec is built for documenting a contract (and generating docs, client SDKs, mock servers from it). Converting between them is common when a team documents in one and tests in the other. Here’s what actually happens in each direction.
Postman → OpenAPI
{
"info": { "name": "Sample API" },
"item": [
{
"name": "Get user",
"request": {
"method": "GET",
"url": { "raw": "{{baseUrl}}/users/:id", "path": ["users", ":id"] }
}
}
]
}
becomes:
openapi: 3.0.3
info:
title: Sample API
version: 1.0.0
paths:
/users/{id}:
get:
summary: Get user
parameters:
- name: id
in: path
required: true
schema:
type: string
responses:
'200':
description: OK
Two conversions happening here worth naming explicitly:
- Path variables: Postman’s
:idcolon syntax becomes OpenAPI’s{id}curly-brace syntax, with a matchingin: pathparameter added automatically. {{baseUrl}}disappears: Postman environment variables in the host aren’t carried into the path — OpenAPI represents the base URL separately via a top-levelserverslist, which isn’t something a single request can express, so it’s dropped rather than baked incorrectly into every path.
A request body gets its schema inferred from the raw JSON sample — each field’s type becomes the OpenAPI schema type for that field. This is inference from one example, not a formal contract: if your actual API sometimes omits a field or returns a different shape, the generated schema won’t know that. Treat it as a starting point to tighten up (required fields, enums, string formats), not a final contract.
OpenAPI → Postman
Going the other way, the same information flows back:
paths:
/users:
post:
requestBody:
content:
application/json:
schema:
type: object
properties:
name: { type: string, example: Alice }
becomes a Postman request with the body pre-filled from the schema’s example values:
{
"mode": "raw",
"raw": "{\n \"name\": \"Alice\"\n}",
"options": { "raw": { "language": "json" } }
}
If a property has no example in the schema, a placeholder is generated instead — "string" for strings, 0 for numbers, true for booleans — so the request body is always valid, editable JSON even when the spec itself is sparse. {id} path parameters become :id again, and every request uses {{baseUrl}} as an environment variable placeholder — set it once in a Postman environment after importing rather than hardcoding a host per request.
What neither direction fully automates
Both conversions handle the mechanical parts — paths, methods, parameters, a body schema — but two things are deliberately left as a starting point rather than a finished artifact:
- Response schemas. Both tools generate a bare
'200': { description: 'OK' }— Postman collections typically don’t carry saved example responses in a form that maps cleanly to OpenAPI’s response schema structure, so this is left for you to fill in with your API’s actual response shapes. - Auth schemes. Postman’s collection-level or folder-level auth configuration (Bearer tokens, OAuth2 flows, API keys) isn’t OpenAPI’s
securitySchemes— they’re different enough models that a header likeAuthorization: Bearer {{token}}on an individual request converts as a literal example header, not a proper OpenAPI security scheme definition.
Both tools handle nested folders (flattening every request regardless of how deep it’s organized) and both accept the format you’d realistically have on hand — OpenAPI to Postman reads either YAML or JSON, matching however your spec is actually written. Everything runs client-side; nothing you paste is uploaded anywhere.