You opened a pull request, the docs built fine, and three days later a teammate pings you: the staging server rejects every request because the OpenAPI file has a dangling $ref that points at a path you renamed. The spec looked correct in the editor. It rendered in Swagger UI. It just was not actually valid, and nothing in your pipeline caught it before it shipped.
That is the exact job a Swagger command-line tool is built for: catch broken specs before a human does. The phrase “swagger cli” usually points at @apidevtools/swagger-cli, a small npm package whose two commands, validate and bundle, check an OpenAPI document and stitch multi-file specs into one. It is a genuinely useful tool, and this guide shows you how to use it properly. It also shows you where it stops, because validating a spec is only the first half of trusting an API; the second half is sending real requests and asserting on what comes back, and that is where a runner like the Apidog CLI takes over.
So this is really two terminals’ worth of work. First, lint and bundle the spec with swagger-cli (or its successor) so the contract is sound. Then run actual tests against the running API from the command line so you know the implementation matches the contract. We will cover both, be honest about which tool owns which job, and give you copy-paste commands for each.
What “swagger cli” actually refers to
There is no single official binary called “swagger.” The name maps to a few different tools, and knowing which one you mean saves a lot of confusion.
@apidevtools/swagger-cliis the package most people mean. Its binary isswagger-cli, and it does two things: validate an OpenAPI or Swagger 2.0 document, and bundle a multi-file spec into one file. It is the focus of the first half of this article.swagger-codegenis a separate, much larger SmartBear project that generates client SDKs and server stubs from a spec. Different job entirely; we touch on it at the end.- Swagger UI and Swagger Editor are not CLIs at all. They render and edit specs in a browser. If you are still learning the format, the Swagger and OpenAPI primer is the right starting point.
This guide is about the command-line work: validating, bundling, linting, and then testing. If you want a broader survey of design-and-test platforms instead, the Swagger alternatives roundup covers the GUI side.
Installing swagger-cli
The tool ships as an npm package. Install it globally:
npm install -g @apidevtools/swagger-cli
Confirm it resolved:
swagger-cli --version
swagger-cli --help
Node.js is the only system dependency. Any machine or CI image with Node already installed can run it. If you would rather not install it globally, you can call it on demand with npx @apidevtools/swagger-cli ..., which is handy on ephemeral build runners.
One thing to know before you lean on it: the maintainers have marked swagger-cli as deprecated. The README says it plainly, citing the maintenance burden of a large userbase with few contributors. It still works, and plenty of pipelines run it today, but new projects are pointed at Redocly CLI as the actively maintained successor. We cover that migration in its own section below, so you can decide with eyes open.
Validating a spec with swagger-cli
The headline command is validate. Point it at your spec file:
swagger-cli validate openapi.yaml
If the document is sound, you get a one-line confirmation and an exit code of 0. If something is wrong, you get an error describing the problem and a non-zero exit code, which is exactly what you want in a pipeline.
validate runs two checks under the hood, and you can switch either off:
swagger-cli validate --no-schema openapi.yaml
swagger-cli validate --no-spec openapi.yaml
--no-schema skips validation against the OpenAPI JSON Schema, the structural check that confirms your document has the right shape. --no-spec skips validation against the specification rules themselves, the semantic check that catches things like duplicate operation IDs or a $ref that points nowhere. Most of the time you want both on, which is the default. The flags exist for the rare case where one layer is flagging something you have a deliberate reason to allow.
What validate catches well: malformed YAML, missing required fields, broken $ref pointers, and structural mistakes that make a spec unparseable. What it does not do is enforce your team’s style. It will happily pass a spec with no descriptions, inconsistent naming, and no examples, because none of that breaks the OpenAPI rules. For that kind of opinionated check you need a linter, which is the next section.
If validation is the only thing you came for, the deeper walkthrough on how to validate OpenAPI specs compares several tools and edge cases worth knowing.
Bundling a multi-file spec
Real specs rarely live in one file. You split schemas into a components/ directory, reference them with $ref, and keep the root file readable. That is good hygiene, but a lot of downstream tools want a single self-contained document. The bundle command flattens the tree:
swagger-cli bundle openapi.yaml -o dist/openapi.bundled.yaml -t yaml
The flags worth knowing:
-o, --outfile <file>writes the result to a file instead of printing it to stdout.-t, --type <json|yaml>sets the output format. It defaults tojson, so pass-t yamlif you want YAML out.-r, --dereferencefully inlines every$refinstead of just collecting external files into one document. This produces a larger file with no remaining references, which some consumers require.-f, --format <spaces>controls indentation, defaulting to 2 spaces.-w, --wrap <column>sets the line length for wrapping long YAML strings.
The distinction between plain bundle and --dereference matters. A normal bundle keeps internal $ref pointers but pulls all the separate files into one, so the document is portable. --dereference resolves every reference into inline objects, which blows up the file size but guarantees no consumer ever has to resolve a pointer. Use the plain bundle for distribution and the dereferenced form only when a specific tool demands it.
A common pattern is to validate and bundle in one step as part of a build:
swagger-cli validate openapi.yaml && swagger-cli bundle openapi.yaml -o dist/openapi.json
The && means the bundle only runs if validation passes, so a broken spec never produces a build artifact.
Wiring swagger-cli into CI
Validation is most valuable when it runs on every change, not when someone remembers. Drop it into your pipeline as a fast gate. Here is a GitHub Actions step that fails the build on an invalid spec:
name: Validate OpenAPI spec
on:
pull_request:
paths:
- 'openapi.yaml'
- 'components/**'
jobs:
validate-spec:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- name: Validate spec
run: npx @apidevtools/swagger-cli validate openapi.yaml
Because swagger-cli validate exits non-zero on a bad spec, the step turns red and the pull request shows a failing check. No extra configuration. The paths filter keeps the job from running when the spec did not change, which keeps your CI minutes down.
This is the cheapest quality gate you can add to an API repo. It costs seconds and it stops a whole class of “the docs lied” bugs from reaching anyone downstream.
When you need linting, not just validation
Validation answers one question: is this a legal OpenAPI document? Linting answers a harder one: is this a good OpenAPI document by our team’s standards? Those are not the same, and swagger-cli only does the first.
Say your style guide requires every operation to have a summary, every property to have a description, and every path to use kebab-case. A spec can violate all three and still pass swagger-cli validate, because none of those rules are part of the OpenAPI specification. A linter lets you encode and enforce them.
This is the main reason teams move to Redocly CLI, the maintained successor that swagger-cli itself points you toward. It covers the same validation and bundling, then adds a real linting engine on top.
Migrating to Redocly CLI
The migration is small because the command names are close. Install the successor:
npm install -g @redocly/cli
The validate equivalent is lint. To match the old behavior closely, extend the minimal ruleset:
redocly lint --extends=minimal openapi.yaml
Run it with the default ruleset instead and it enforces a stronger set of recommended rules, which is where the linting value shows up. You configure rules in a redocly.yaml file, set each one to error or warn, and even write custom plugins for organization-specific checks.
Bundling maps almost one for one:
redocly bundle openapi.yaml -o dist/openapi.yaml
The flag names shift slightly. swagger-cli’s -o/--outfile becomes --output, -t/--type becomes --ext, and -r/--dereference becomes -d/--dereferenced. If your old scripts used the short flags, a quick find-and-replace gets you there. For a wider comparison of spec-checking tools, the best OpenAPI validator tools breakdown puts Redocly next to the alternatives.
The short version: if you are starting fresh, reach for Redocly CLI. If you have an existing swagger-cli step that works, there is no fire, but the path forward is clear and the rename is mechanical.
The half a spec tool cannot cover
Here is the limit of every tool we have discussed so far. swagger-cli validate confirms your spec is well-formed. redocly lint confirms it follows your style. Neither one sends a single request to your running API. A spec can be perfect on paper while the implementation returns a 500, omits a field it promised, or ignores a query parameter entirely.
Closing that gap means functional testing: send a real request to a real endpoint, then assert on the status code, the response body, and the values inside it. That is a different category of tool, and it is where Apidog fits the workflow.
Apidog is an all-in-one API platform. You import your OpenAPI spec, and from that one definition you get interactive docs, a mock server, and test scenarios you can chain together with assertions, all without leaving the workspace. Importing is direct; the guide on importing Swagger or OpenAPI and generating runnable requests walks through it, and you can generate full test collections straight from the spec instead of hand-building them.
The piece that matters for the terminal is the command-line runner, published as the apidog-cli npm package. You install it and run a saved scenario headlessly:
npm install -g apidog-cli
apidog run --access-token $APIDOG_ACCESS_TOKEN -t 605067 -e 1629989 -r cli
The -t flag is the test scenario ID, -e is the environment ID, and -r chooses report formats such as cli, html, json, or junit. Like swagger-cli, it exits with a clean status code, so a failing assertion turns the pipeline red. Unlike swagger-cli, what it is checking is the live behavior of your API, not just the shape of the file that describes it. For the complete flag reference and CI examples, see the Apidog CLI complete guide, or run apidog run --help for the current options. If you want to set up that first scenario, download Apidog, import your spec, and the runner command is one copy from the scenario’s CI/CD tab.
A complete terminal workflow
Put the two halves together and you get a pipeline that checks the contract and the implementation in sequence:
# 1. Is the spec well-formed and on-style?
redocly lint --extends=minimal openapi.yaml
# 2. Produce a single distributable document.
redocly bundle openapi.yaml -o dist/openapi.yaml
# 3. Does the running API actually match the contract?
apidog run --access-token $APIDOG_ACCESS_TOKEN -t 605067 -e 1629989 -r junit
Step one fails fast on a malformed or off-style spec. Step two produces the artifact your docs and SDK generators consume. Step three sends real traffic and asserts on the responses, so a green build means both the contract and the code behind it are sound. Each step exits non-zero on failure, so the whole chain is a single gate you can drop into any CI runner that has Node.
If your testing today relies on a spec-conformance tool that has gone quiet, the write-up on validating an API against its spec without Dredd covers the same contract-versus-implementation idea from a different angle.
A note on swagger-codegen
People searching for a “swagger cli” sometimes actually want swagger-codegen, which is a different tool with a different job. It reads an OpenAPI spec and generates client SDKs, server stubs, and documentation in dozens of languages. It is genuinely useful for bootstrapping a typed client from a published spec, and it is fair to call it the most capable code-generation option in the Swagger family.
It does not validate, lint, or test in the sense this article covers. Generation assumes you already have a sound spec; if the input is broken, the output is broken in matching ways. So even in a codegen workflow, you still want a validate or lint step in front of it. The tools complement each other rather than replacing one another.



