Swagger and OpenAPI are two names for one specification line. Up to version 2.0 the format was called the Swagger Specification. SmartBear Software donated it in 2015, and it continued as the OpenAPI Specification under the OpenAPI Initiative, a Linux Foundation project. Swagger 2.0 and OpenAPI 2.0 are the same document format; a file that starts with swagger: "2.0" is one. OpenAPI 3.0 and 3.1 are the newer versions, and they start with openapi: instead. Today "Swagger" mostly names SmartBear's tools, such as Swagger UI and Swagger Editor, which read OpenAPI files. What you usually need to know is which version your file is: 2.0, 3.0 or 3.1.
Names and versions at a glance
| What people say | What it is | First line of the file |
|---|---|---|
| Swagger 2.0, OpenAPI 2.0, "a Swagger file" | The last version published under the Swagger name | swagger: "2.0" |
| OpenAPI 3.0 | First version under the OpenAPI name, with a new structure | openapi: 3.0.3 (or another 3.0.x) |
| OpenAPI 3.1 | 3.0 structure with full JSON Schema 2020-12 support | openapi: 3.1.0 (or 3.1.1) |
| Swagger UI, Swagger Editor | SmartBear tools that render and edit OpenAPI documents | Not a spec version |
The file name proves nothing. Many frameworks still serve an OpenAPI 3 document at /swagger.json or behind a Swagger UI page; getting the OpenAPI JSON from a Swagger UI page shows how to find the raw file. Open it and read the version field before choosing a validator, a code generator or a migration plan. The glossary entries for Swagger and OpenAPI cover the terms on their own. If Swagger UI refuses to render a file, Swagger UI unable to render this definition lists the version values it accepts. To write a spec and not only read one, APIForge vs Swagger Editor compares the two tools.
One API in Swagger 2.0 and OpenAPI 3.0
Here is a small Users API with two operations, bearer authentication and a nullable field, written once in each format. Both files pass validation with @apidevtools/swagger-parser, the library APIForge uses to parse specs.
swagger: "2.0"
info:
title: Users API
version: 1.0.0
host: api.example.com
basePath: /v1
schemes:
- https
consumes:
- application/json
produces:
- application/json
securityDefinitions:
bearerAuth:
type: apiKey
in: header
name: Authorization
security:
- bearerAuth: []
paths:
/users:
post:
summary: Create a user
parameters:
- in: body
name: body
required: true
schema:
$ref: "#/definitions/NewUser"
responses:
"201":
description: User created
schema:
$ref: "#/definitions/User"
"400":
description: Invalid input
/users/{id}:
get:
summary: Get a user
parameters:
- name: id
in: path
required: true
type: string
responses:
"200":
description: The user
schema:
$ref: "#/definitions/User"
"404":
description: User not found
definitions:
NewUser:
type: object
required: [email]
properties:
email:
type: string
format: email
nickname:
type: string
x-nullable: true
User:
type: object
required: [id, email]
properties:
id:
type: string
email:
type: string
format: email
nickname:
type: string
x-nullable: trueopenapi: 3.0.3
info:
title: Users API
version: 1.0.0
servers:
- url: https://api.example.com/v1
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
schemas:
NewUser:
type: object
required: [email]
properties:
email:
type: string
format: email
nickname:
type: string
nullable: true
User:
type: object
required: [id, email]
properties:
id:
type: string
email:
type: string
format: email
nickname:
type: string
nullable: true
security:
- bearerAuth: []
paths:
/users:
post:
summary: Create a user
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/NewUser"
responses:
"201":
description: User created
content:
application/json:
schema:
$ref: "#/components/schemas/User"
"400":
description: Invalid input
/users/{id}:
get:
summary: Get a user
parameters:
- name: id
in: path
required: true
schema:
type: string
responses:
"200":
description: The user
content:
application/json:
schema:
$ref: "#/components/schemas/User"
"404":
description: User not foundLine by line, these are the changes. Every row is a direct consequence of the 2.0 and 3.0 specifications:
| Concern | Swagger 2.0 | OpenAPI 3.0 |
|---|---|---|
| Version field | swagger: "2.0" | openapi: 3.0.3 |
| Base URL | host, basePath and schemes, one host only | servers list of URLs, with optional {variables} |
| Request body | A parameter with in: body (at most one per operation) | requestBody with content keyed by media type |
| Form fields and uploads | in: formData parameters, type: file | requestBody with multipart/form-data or application/x-www-form-urlencoded |
| Media types | consumes and produces lists at root or operation level | Media type keys inside each requestBody and response content |
| Reusable schemas | definitions | components/schemas |
| Other reusable parts | parameters, responses at the root | components holds schemas, responses, parameters, examples, requestBodies, headers, securitySchemes, links and callbacks |
| Security schemes | securityDefinitions with basic, apiKey or oauth2 | components/securitySchemes with apiKey, http (basic, bearer and others), oauth2 or openIdConnect |
| Null values | No nullable keyword; tools use the x-nullable extension | nullable: true on the schema |
| Response body | schema directly on the response | schema under content and a media type |
| Callbacks and links | Not available | callbacks for out-of-band requests, links between operations |
Two rows change what the file says about the API. Swagger 2.0 has no bearer scheme, so the 2.0 file describes the token as an apiKey in the Authorization header. That works, but generated clients will not add the Bearer prefix for you; in 3.0, type: http with scheme: bearer says exactly what the server expects. And because 2.0 keeps media types in consumes and produces lists, one operation cannot give JSON and XML responses different schemas. OpenAPI 3.0 puts a schema under each media type.
What changed from OpenAPI 3.0 to 3.1
OpenAPI 3.1 keeps the 3.0 structure and changes the schema language. In 3.0, the Schema Object is an extended subset of JSON Schema: type must be a single string, and nullable: true stands in for null. In 3.1, schemas follow JSON Schema draft 2020-12, so any JSON Schema validator for that draft can read them. The OpenAPI Initiative's upgrade guide lists the edits most files need:
- Replace nullable: true with a type array: type: [string, "null"].
- Change exclusiveMinimum: true next to minimum: 7 into exclusiveMinimum: 7. The keyword now takes the number itself.
- Replace example on a schema with examples, which takes an array.
- Describe binary uploads with contentMediaType or an empty schema instead of format: binary, and base64 data with contentEncoding: base64.
- Change the version field to openapi: 3.1.0 or 3.1.1.
For every change with before and after YAML, see OpenAPI 3.0 vs 3.1. To move a Swagger 2.0 file forward first, follow how to convert Swagger 2.0 to OpenAPI 3.
3.1 also adds fields. webhooks is a new top-level object for requests your API sends to subscribers. paths is no longer required: a document needs openapi and info, plus at least one of paths, components or webhooks, which allows a file that holds only shared schemas. components gains pathItems, info gains summary, license gains an SPDX identifier, and security schemes gain mutualTLS. The User schema below shows the 3.1 versions of the fields from the earlier example.
openapi: 3.1.0
info:
title: Users API
version: 1.0.0
summary: Create and read user accounts.
license:
name: MIT
identifier: MIT
components:
schemas:
User:
type: object
required: [id, email]
properties:
id:
type: string
email:
type: string
format: email
nickname:
type: [string, "null"]
examples: [dana]
age:
type: integer
exclusiveMinimum: 0
webhooks:
userDeleted:
post:
requestBody:
content:
application/json:
schema:
$ref: "#/components/schemas/User"
responses:
"200":
description: Webhook receivedWhat the version does not change
The design of the API is the same in every version. We scored both the Swagger 2.0 and the OpenAPI 3.0 file above with the APIForge quality score, and both got 91 out of 100 with the same single issue: no version segment in the paths. The /v1 is there, but in basePath for 2.0 and in the server URL for 3.0, and the score reads the path keys. Whether that matters is your call; versioning in the base URL is a legitimate choice. It shows that converting a file to a newer format does not fix naming, missing error responses or anything else about the API itself.
Which version should you write?
Choosing a spec version
| Scenario | Recommendation | Alternative |
|---|---|---|
| You are starting a new API | Write OpenAPI 3.1 if your generator, gateway and docs tools read it. | Use 3.0.3 if any tool in your pipeline only reads 3.0. Converting 3.0 to 3.1 later is a short list of edits. |
| You have a working Swagger 2.0 file and no tool needs 3.x | Leave it until you need a 3.x feature such as a bearer scheme, oneOf or several media types. | — |
| You need webhooks, null in enum lists, or plain JSON Schema validators | Use 3.1. These are the cases where 3.0's schema subset gets in the way. | — |
| Your framework generates the spec from code | Use the version it emits and check that setting when you upgrade the framework. | — |
Converting Swagger 2.0 to OpenAPI 3
Converters such as the swagger2openapi npm package handle the mechanical part: host and basePath become servers, definitions move to components/schemas, body parameters become requestBody, and $ref pointers are rewritten. Review these by hand afterwards, because a converter cannot know your intent:
After an automatic conversion
Fix the security scheme
An apiKey on the Authorization header usually means a bearer token. Replace it with type: http and scheme: bearer.
Replace vendor extensions
x-nullable: true becomes nullable: true in 3.0, or a type array in 3.1. Search the file for other x- keys your old tooling relied on.
Check form and file parameters
formData parameters and type: file need a multipart/form-data or application/x-www-form-urlencoded requestBody. Confirm each field's encoding.
Split shared media types
If the 2.0 file listed several produces types, the converter copies one schema to each. Split them if JSON and XML bodies differ.
Validate and compare
Validate the new file, then load both versions and compare operation counts and parameters, so nothing was dropped.
The spec validation guide covers adding that validation step to CI so a broken conversion never merges.
Open either version in APIForge
APIForge reads Swagger 2.0, OpenAPI 3.0 and OpenAPI 3.1. Paste a file or URL into the Swagger 2.0 validator or the OpenAPI validator and it parses the document, resolves references, builds request URLs from host, basePath and schemes for 2.0 files or from servers for 3.x, and treats 2.0 body parameters as the request body. It does not export a converted file, so use a converter for that step and APIForge to check that the result still lists the same operations.