What is OpenAPI? OpenAPI Definition & OpenAPI Standards

Discussing the OpenAPI specification standard, what benefits OpenAPI provides to development, and what OpenAPI tools are available.

Discussing the OpenAPI specification standard, what benefits OpenAPI provides to development, and what OpenAPI tools are available.

In recent years, web API development has grown significantly. On average, over 2,000 new public APIs have been released each year since 2015. Similar growth is echoed within organizations, as internal APIs enable more efficient development. However, as companies depend more heavily on their APIs, they need to track and organize the many interfaces they support.

The OpenAPI Specification is a standard format to define structure and syntax REST APIs. OpenAPI documents are both machine and human-readable, which enables anyone to easily determine how each API works. Engineers building APIs can use APIs to plan and design servers, generate code, and implement contract testing. Other internal teams can aggregate these API definitions to determine their API program’s footprint and dependencies.

To adopt the OpenAPI Specification, you create a document that defines your REST API or HTTP API in OpenAPI’s standard format. As an open-source project that is language agnostic and vendor-neutral, the OpenAPI specification has been widely adopted by the industry and is supported by a range of open source and proprietary tools.

πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— What are the Benefits of OpenAPI?

OpenAPI is first meant to be interpreted by machines, but there are many ways it can be used by people. Once you have a complete description of how a REST API works, much of the way engineers work with APIs can be streamlined.

For example:

  • Generate accurate documentation
  • Create stub code for API development
  • Build mock servers to prototype the interface
  • Test that API requests and responses match the intended contract

In addition, you can use the API definition to better collaborate across teams. API architects can review upcoming API changes and offer feedback. Product managers can confirm it fulfills the needs of their software. Frontend teams can even begin work before the backend is complete.

OpenAPI documents enable organizations to adopt design-first APIs. This concept is used across success engineering teams to decrease time to market. You can speed up your development cycle while maintaining confidence in the software you build.

πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— OpenAPI Example Documents

A document written to the OpenAPI specification can use either JSON or YAML to express the API’s capabilities. These formats are interchangeable and include the same elements.

There are three primary areas in every OpenAPI document:

  • Endpoints (i.e. paths appended to the server URL) and the HTTP methods they support. For each method, any parameters that may or must be included in the request and the response formats for the possible HTTP response codes are specified.
  • Reusable components that can be used across multiple endpoints in the API, such as common request parameters and response formats.
  • Meta information, including the title, version, and description of the API, authentication method, and location of the API servers.

It can be helpful to review some OpenAPI examples to see how the documents actually look. The following are small OpenAPI 3.0 examples to show different request types.

πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— GET Request

This is a complete, but very simple, API example with a single endpoint and operation, written as YAML:

openapi: 3.0.0
info:
  title: customer
  version: '1.0'
servers:
  - url: 'https://api.example.com'
paths:
  '/customers/{customer_id}':
    parameters:
      - schema:
          type: integer
        name: customer_id
        in: path
        required: true
    get:
      summary: customer
      tags: []
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  customer_id:
                    type: integer
                  customer_name:
                    type: string
      operationId: get-customers-customer_id
      description: Retrieve a specific customer by ID
components:
  schemas: {}

The /customers/ path expects a specific customer ID and it will return the customer name and ID as a JSON object.

While YAML is more human-readable and easier to write, OpenAPI supports JSON, as well. Here is the same API expressed as JSON:

{
  "openapi": "3.0.0",
  "info": {
    "title": "customer",
    "version": "1.0"
  },
  "servers": [
    {
      "url": "https://api.example.com"
    }
  ],
  "paths": {
    "/customers/{customer_id}": {
      "parameters": [
        {
          "schema": {
            "type": "integer"
          },
          "name": "customer_id",
          "in": "path",
          "required": true
        }
      ],
      "get": {
        "summary": "customer",
        "tags": [],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "customer_id": {
                      "type": "integer"
                    },
                    "customer_name": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "get-customers-customer_id",
        "description": "Retrieve a specific customer by ID"
      }
    }
  },
  "components": {
    "schemas": {}
  }
}

In either YAML or JSON, OpenAPI paths contain an array of endpoints. Within each is one or more HTTP methods.

πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— POST Request

To add a new customer using this example OpenAPI, you need a new path. You can’t reference the ID of a customer that has not yet been created, so OpenAPI views it as a completely separate endpoint.

The complete YAML of the GET request plus the POST request could look like this:

openapi: 3.0.0
info:
  title: customer
  version: '1.0'
servers:
  - url: 'https://api.example.com'
paths:
  '/customers/{customer_id}':
    parameters:
      - schema:
          type: integer
        name: customer_id
        in: path
        required: true
    get:
      summary: customer
      tags: []
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  customer_id:
                    type: integer
                  customer_name:
                    type: string
      operationId: get-customers-customer_id
      description: Retrieve a specific customer by ID
  /customers:
    post:
      summary: customers
      operationId: post-customers
      responses:
        '201':
          description: Created
      description: Add a new customer record
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
components:
  schemas: {}

Note that /customers is its own separate path. It accepts a single field, the customer name, because the ID is generated by the API.

πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— PUT Request

To update an existing customer record, developers would need to use the ID with the request. Unlike a POST, this PUT request will use the same path as the initial GET request, as shown in this YAML snippet:

paths:
  '/customers/{customer_id}':
    parameters:
      - schema:
          type: integer
        name: customer_id
        in: path
        required: true
    get:
      # Original GET details removed for brevity
    put:
      summary: customer update
      operationId: put-customers-customer_id
      responses:
        '204':
          description: No Content
      description: Update a customer record
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string

These basic examples are merely the Hello World of OpenAPI. Read on for complete API description examples, or see how to reuse models with references.

Most OpenAPI documents are used internally, but we’ve put together some great public examples. Reviewing these documents of live APIs can give you a good idea of how to design your APIs.

SendGrid: YAML and JSON The Twilio-owned email infrastructure API stores its OpenAPI descriptions in a GitHub repo. Whenever it’s updated, its API reference (powered by Stoplight) is updated with the latest documentation for its API.

GitHub: YAML and JSON The code collaboration tool released its description with more than 600 operations. The GitHub API is extensive and mature, which makes this one good to study.

Petstore: YAML and JSON The classic OpenAPI example takes you beyond Hello World. Though the API is live, it’s not exactly real (since it was built as a tutorial).

US Patent and Trademark API: YAML and JSON Maintained by the OpenAPI community, this describes a real government API.

The Open API Initiative (OAI) maintains a handful of other examples in the specification repo covering OpenAPI 2.0, OpenAPI 3.0, and even some OpenAPI 3.1 examples.

πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— OpenAPI Versions and Other API Descriptions

There are several versions of OpenAPI, as well as similar formats. Many of the industry’s biggest players, including Microsoft and Google, have rallied around OpenAPI as a vendor-neutral API specification.

Prior to the creation of the OpenAPI Initiative (OAI, an open governance structure under the Linux Foundation), the OpenAPI format was known as Swagger. SmartBear Software donated Swagger to the OAI. The consortium of member companies and individuals began working on a new specification to replace Swagger.

There are several major OpenAPI Specification (OAS) versions:

  • OpenAPI 2.0, formerly known as Swagger 2.0
  • OpenAPI 3.0, the most recent official version from the community
  • OpenAPI 3.1, a release candidate

There are several other API description formats, including include:

  • RAML
  • API Blueprint
  • Postman Collections

Each of these is built for specific tooling and, in some cases, are no longer maintained. Forward-looking organizations are encouraged to adopt OpenAPI to describe APIs.

πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— Who Uses OpenAPI?

Since OpenAPI was created as an open format to replace the Swagger Specification, many companies have signed on to support OpenAPI, including the authors of those other API description formats.

OpenAPI members include:

  • Atlassian
  • Bloomberg
  • eBay
  • Google
  • IBM
  • Microsoft
  • Oracle
  • Stoplight
  • Vonage

These companies both support and build upon OpenAPI. In addition, thousands of other organizations use OpenAPI internally. Some, like GitHub and Twitter, have even published their OpenAPI descriptions publicly.

πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— What OpenAPI Tools Are Available?

An ecosystem of open source and proprietary software has developed around OpenAPI to leverage the machine-readable format of OpenAPI documents. These include tools for generating API reference documentation, creating mock servers, and defining API contracts for automated integration tests, together with validators and linters to ensure specifications adhere to the OpenAPI structure and syntax.

Some of the useful tools for OpenAPI include:

  • Stoplight Studio, a free OpenAPI editor, to easily write API descriptions without memorizing syntax.
  • Spectral, an automated validation tool, helps maintain API consistency and style with customizable OpenAPI linting.
  • Prism, a mock server generator, which builds upon your OpenAPI definition to prototype or run integration tests.

An extensive list of open-source tools that implement the OpenAPI specification is provided on the OpenAPI Github project, and a third-party list of actively maintained modern OpenAPI tools can be found on OpenAPI.Tools.

πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— How OpenAPI Defines APIs

API descriptions can drastically speed up API development. The OpenAPI 3.0 specification was created to define any HTTP API, regardless of response formats, authentication modes, and other details that vary from API to API.

In the following sections, we’ll detail the nitty-gritty of the OpenAPI specification, including:

  • Data types
  • Reusable data models
  • Glossary of OpenAPI specification objects

To write an OpenAPI file by hand, you’ll want to become familiar with these requirements. Alternatively, you can use Stoplight Studio to easily design APIs without memorizing syntax.

πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— OpenAPI Data Types

The specific format used to write OpenAPI documents is either YAML or JSON. You’ll want to get an understanding of the OpenAPI data types that can be contained within those document types.

Primitive Data Types

With an OpenAPI document, you need to specify the schema definition, the types of each field. OpenAPI has specific names for the primitive data types you are most likely familiar with. They are:

  • string
  • number
  • integer
  • boolean
  • array
  • object

You can apply certain restrictions on the data being accepted as well. A common ability is implementing minimum and maximum values for an instance of a type. For example, you can declare a value’s type as an integer, and then specify it must be an integer between 0 and 100. The same can be done for the number of characters in a certain string.

The specific ways you can refine and format data types are numerous, but the documentation provides a thorough explanation of what you can do in this regard.

Formatting the string type is worth touching on, though. OpenAPI accepts specific formats for strings, such as a date and password. You’re not limited to these types, though. You can set any number of arbitrary formats of your own (such as an email format) to later use in your own tools or programs, if supported.

There are a few quirks when it comes to data types that are OpenAPI specific, however. Note that the type keyword only takes a single value, not a list. If you’d like to list multiple types, you do so with specific keywords (oneOf, anyOf, allOf, and not) for clarity.

oneOf:
  - type: string
  - type: integer

Objects in OpenAPI are defined as property/value pairs. Keep this in mind when reading through the upcoming object descriptions.

Media Types

For request and response body data, you can specify the format within the response schema. This is found in the paths object, which we’ll be going over later. Whether it’s JSON, XML, or images, all you need to is specify it as such within the schema.

πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— Add Example Responses and Requests

In addition to the data types, you can help developers understand with example data. Add OpenAPI examples to your document, which can also be used within documentation or even mock servers.

Examples are optional and added for each field in any OpenAPI structure that contains fields, such as responses, requests, paths, and components.

πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— Response Examples

The GET operation for a single customer, used in the sample OpenAPI document earlier, returns two fields: customer_id and name. In this snippet of OpenAPI YAML, you can see the data types and example data are stored alongside each other:

   get:
      summary: customer
      tags: []
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  customer_id:
                    type: integer
                    example: 123456
                  customer_name:
                    type: string
                    example: Roy Fielding
      operationId: get-customers-customer_id
      description: Retrieve a specific customer by ID

The corresponding example response would look like this:

{
  "customer_id": 123456,
  "name": "Roy Fielding"
}

πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— Request Examples

The POST operation to create a new customer record requires a single field, the name as a string. Rather than listed in the responses, these fields (and their examples) are stored in the requestBody object of the OpenAPI description:

  /customers:
    post:
      summary: customers
      operationId: post-customers
      responses:
        '201':
          description: Created
      description: Add a new customer record
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                  example: Roy Fielding

The JSON interpretation of the example data posted to /customers is:

{
  "name": "Roy Fielding"
}

πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— Path Examples

Sometimes data is used within the path itself, such as when retrieving or updating a specific customer record in our sample OpenAPI. Here’s a snippet of the relevant section within a paths child object:

servers:
  - url: 'https://api.example.com'
paths:
  '/customers/{customer_id}':
    parameters:
      - schema:
          type: integer
          example: 123456
        name: customer_id
        in: path
        required: true
        description: Unique customer identifier

In this case, the interpreted example would be the full path to the operation: https://api.example.com/customers/123456

πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— How OpenAPI Re-uses Models With References

As you build your API, you’ll most likely end up needing the same piece of information in multiple places. It’s cases like these where you’ll want to make use of OpenAPI 3.0s ability to create references.

OpenAPI has a dedicated section for declaring schemas to be referenced: components. We discuss what this looks like in the upcoming section on components objects, but essentially schema in this section can be called in other sections of your API document.

Note that it can’t be called in any section of your document. It’s only allowed where the specification says so, but there are only a few exceptions. You’ll want to note where you can use references before you start placing them throughout your document, as a result.

πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— OpenAPI Objects

openapi

This field is a string that specifies which version of the OpenAPI specification the document is using, and is thus required for every document.

The version is specified with a three-part number (e.g. 3.0.3). This number follows the concepts of semantic versioning The three parts of a semantic versioning number are defined as MAJOR.MINOR.PATCH. Each of these parts has a specific meaning, as defined by the semantic versioning specifications:

  • MAJOR version: when you make incompatible API changes,
  • MINOR version: when you add functionality in a backward-compatible manner, and
  • PATCH version when you make backward-compatible bug fixes.

OpenAPI’s available versions as of writing are 3.0.0, 3.0.1, 3.0.2, and 3.0.3. As such, all these versions have no discernable differences as far as the user is concerned; only bug fixes were added.

info

The Info object is where you put your API’s metadata. This object is accessed by users if they need specific information about your API. Documentation generation tools are often made to pull this data and make it readily available for users.

There are six fields that can be in this object, two of which are required:

  • Title
  • Version

The title is pretty self-explanatory; you input the title of your API as a string.

The version requires a little bit of clarification. As mentioned in the description of the OpenAPI field, you set the OpenAPI Specification version. That is set to whatever OpenAPI spec the document is written with. This field is your own API’s version. It accepts any string, so use whatever version designation you already use.

For example, let’s say you wrote the OpenAPI document for your API’s version 1.0.0. You later make a minor update, and thus you update your API to 1.1.0. In this case, you’ll want to update this string as well.

While our example used semantic versioning, much like OpenAPI does, it doesn’t have to be. Perhaps you appreciated the sweet names Android versions used to have, so your versions are treats such as β€œgelato” and β€œhot-fudge sundae”. Anything works here, as long as it’s a string. Although, it might not be a good idea to rely solely on food-related nicknames.

Besides those fields, there are four others you can use:

  • description
  • termsOfService
  • contact
  • license

The description gives you the ability to explain what your API is. Perhaps your API’s name is an acronym. Here, you can write it out. This field also supports Markdown (specifically CommonMark).

The terms of service field gives you an opportunity to make sure your APIs users can access your terms of service. Don’t worry, you don’t have to copy the text into this field. Instead, this field only accepts strings in the form of a URL. This way, you can just link users to your API’s terms of service.

The contact object contains the name url and email of someone that represents the company or people behind the API. You could set this to your company’s tech support, for example.

Finally, the license object contains the name and url of whatever license you may be using. Both are strings, but the url field only accepts those in the format of a URL. This way your users can easily access the full license you’re using if they wish to read it.

servers

The servers object is where you’ll put your API server information.

There’s only one required field here: url. It being required isn’t a headache though, as it’ll save you time and space with establishing your APIs endpoints (in the following paths section). By noting your API’s base url here, all your endpoints can be written to be relative to that url. No more writing β€œapi.example.com/” at the beginning of every endpoint! Make sure to leave out those query string parameters here, though, it’ll be considered invalid otherwise.

If you’ve got multiple servers you’d like to link to, you can do so here as well. For example, perhaps you’ve got a production server and a development server. By having these listed within this section you’ll be able to specify which one you want to use later on.

OpenAPI 3.0 allows you to set the host name, port, and path within the URL. That being said, you can choose to set any of these as variables within the server object if you’d like. This comes in handy when dealing with multiple protocols, or where a part of the url will be user-specific. This also gives you the ability to set one value as the default value.

As is the case with most OpenAPI objects, you can also fill out an optional description field. This can be helpful in differentiating between those different servers.

paths

The paths section is used to communicate your API’s endpoints. For each endpoint, you can declare the HTTP methods and potential responses, along with other optional parameters.

It’s worth noting what OpenAPI refers to these concepts as:

  • An HTTP method is referred to as an operation
  • An endpoint is referred to as a path

As such, we’ll be following their terminology preference.

The path you specify within this object is not its full URL. Remember that you set the server URL within the servers object. Because of this, you only need to specify the path relative to that server URL.

paths:
  /files:
	...
  /users:
	...

That being said, you can override this mechanism. If there are paths that make use of other servers, you can specify that as such within that path.

paths:
  /files:
	servers:
  	- url: https://files.example.com

After establishing the path, you can implement operations (endpoints) to access that path. OpenAPI 3.0 supports the following operations:

  • GET
  • PUT
  • POST
  • DELETE
  • OPTIONS
  • HEAD
  • PATCH
  • TRACE

An OpenAPI path can have one of each type of operation, but only one. For example, you can have an operation such as GET /files as well as POST /files, but you can’t have two definitions of GET /files even if they contain different parameters within.

Within an operation, you can set parameters. This is how a user will provide information about their specific API request using that path. You can specify how the operation accepts these parameters and what form those parameters shall take.

You can let your users pass parameters in four ways:

  • path (denoted with curly braces)
  • query
  • header
  • cookies

For sending information to the server, a user will do so using the request body. You can specify what that looks like as well.

You’ll also need to define what the response for an API request will look like. This will consist of a response status, and a response body when necessary. The response status will most likely be your typical status codes such as 200 or 404. When a response body is possible, you can define its schema.

There are also some additional elements that OpenAPI supports. These are mainly used to aid in providing organization and documentation:

  • summary
  • description
  • externalDocs
  • tags

If you want a brief comment providing detail on what an operation does, you can do so with a summary. If you’d like to be more thorough, you can take advantage of the description element which has multi-line and Markdown support. If you’d just rather send a user to an external resource for documentation, you can do so with the externalDocs element. And finally, for organization purposes, you can group operations with the tags element.

While none of these are documentation-oriented elements are required, it is certainly in the spirit of the OpenAPI specification to be organized and well-documented.

components

The components object gives you the opportunity to define object schemas so you can repeatedly use them throughout your API. There are a variety of objects types you can use to hold data structures:

  • schema
  • responses
  • parameters
  • examples
  • requestBodies
  • headers
  • securitySchemes
  • links
  • callback

Schemas within have their properties’ data types explicitly stated. You can also state which properties are required.

For example, you can create a schema within the components object for accessing files within your API. Then, within the relevant endpoints’ responses, you can reference this schema via $ref: #/components/schemas/{your_schema} instead of repeatedly writing out the data structure. You can reference them in other places, such as parameters and request bodies.

Referencing the schema this way is the only way to integrate them into your API. Think of the components object as merely a place to hold data structures on standby until you need them.

security

One of the available schemas listed under components was securitySchemes. If you define security schemes within the components section, you’ll use the security requirement object to apply them.

To set up a security scheme under components, you specify the type of authentication used. OpenAPI supports the following authentication methods:

There are specific nuances for setting up each method, so you’ll want to peruse their docs when doing so. You can get pretty particular with your security, depending on what authentication methods you’re using. You can implement scopes if desired. You can also control whether multiple authentication methods can be accepted and whether all are required or only one of them is. Make sure your REST API supports this type of authentication, however.

Once you’ve defined the scheme within the components object, you can apply it using the security requirement object. When you’re doing so, you have the choice between applying it to your entire API or to specific operations within paths.

If you want to apply the security scheme to the entire API, you’d so outside any other objects in the OpenAPI document. You can override this security scheme for individual operations by specifying as such within the paths object.

tags

We mentioned that you can associate tags with OpenAPI operations. While you don’t have to declare tags within the tags object, doing so gives you the opportunity to have them organized how you prefer. If they aren’t declared, it’ll be up to the tool you’re using to decide how they’re organized. So if you’re concerned with tag structure, you’ll want to make use of this object.

A tag object consists of a name, a description, and an externalDocs field. The name is what you’ll reference when assigning tags within the operation object. The description is an additional benefit of explicitly defining tags with this object type; you’ll be able to add a description with markdown syntax if you’d like.

If you’d rather just link to external documentation for a specific tag, just make use of the externalDocs field.

externalDocs

On that note, let’s look at what exactly that externalDocs field is. We’ve mentioned it a few times, as it can be implemented within multiple other objects. Specifically, you can make use of the external documentation object for:

  • Tags
  • Operations
  • Schemas

For any of these items, you can provide a description and a url for where a user can find the relevant documentation. This can be especially helpful as you update your docs; as long as the links stay the same, those using your API will always get to the most updated version.

πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— πŸ”— Quickly Create OpenAPI Design Documents

Now you’re an expert on OpenAPI. See how easy it is to create your own OpenAPI descriptions with Stoplight Studio.

Stoplight API Editor

Design APIs visually, create instant mock servers, and generate documentation automatically. You can use Stoplight Studio now for free.

Design & Document OpenAPIs with Stoplight