Stoplight Prism alternative: an OpenAPI mock server with a URL

Sep 11, 2026·15 min read

Prism is Stoplight's open-source OpenAPI mock server, and for years the default answer to "I need to mock this spec". Install it, point it at an OpenAPI document, and every operation in the file answers.

npm install -g @stoplight/prism-cli
prism mock openapi.yml

Mockzilla starts the same way, from the same file.

brew install mockzilla/tap/mockzilla
mockzilla openapi.yml

Two commands either side. Everything below runs those same specs on the same laptop, with the numbers measured rather than asserted.

Mockzilla is also two halves. The CLI above, MIT-licensed, and a hosted half that gives the mock a URL. That second half is usually what sends people looking for a Stoplight Prism alternative in the first place: the Prism CLI has no hosted mode, and Stoplight sells one on their platform from $44 a month.

Mockzilla vs Prism, side by side

MockzillaPrism
ReadsOpenAPI 3.xOpenAPI 2.0, 3.0, 3.1, Postman Collections
Runs on your machineyesyes
APIs per processas many as fit in memoryone
Shared URL your team can callyesno
A URL per pull requestyesno
First-party GitHub Actionyesno
Price for that URLfree plan, then $10 a month$44 a month, on Stoplight's platform
Control over generated valuescontext.yml beside the specx-faker inside the spec
Force a status code per requestnoPrefer: code=404
Enforces the spec's security schemesnoyes
Validates requests and responsesyesyes
Upstream proxyyes, validates it and falls back to the mockprism proxy, validates only
Share the whole setup as one filemockzilla pack, a .mockz anyone runsno, you share the document
Record and replay real responsesyesno
Browser UI for the endpointsyesno
MCP server for coding agentsyesno
LicenseMITApache-2.0

Does Prism have a hosted mock server?

The Prism CLI has no hosted mode. prism mock binds to 127.0.0.1 and that is the whole of it: no account, no shared URL, nothing for a second person to call.

A hosted mock is a Stoplight platform feature instead, and it is priced as one. The free tier there is a single project whose mocking line reads "Local mock servers", which is Prism on your own machine again.

MockzillaStoplight
Mock server you run yourselffree, MITfree, Apache-2.0
Hosted mock on the free tieryesno, local only
Cheapest plan with a hosted mock$0$44 a month billed yearly, $56 monthly
What that plan gives you1 simulation, 1,000 requests a month, 128 MB3 seats, unlimited projects, the design platform
Next step up$10 a month: 100K requests, 2 simulations, 5 PR environments$113 a month billed yearly

Stoplight's $44 buys an API design platform: a visual OpenAPI editor, hosted docs, style guides, governance. Mock servers are one feature inside it. Getting to them is a work email, a confirmation link, a five-step account setup that asks your role and how many APIs your company has, a workspace and a project.

Our free plan has no signup at all. The GitHub Action uses the workflow's own GITHUB_TOKEN to prove the repository is yours, and the account is created behind it on the first push.

Start-up time and memory, measured

The API is Twilio Verify: 29 paths, 53 operations, 173 KB of YAML. Both tools installed natively, three runs each, no container in the way.

Twilio VerifyMockzilla 2.8.16Prism 5.16.0
Start to first response51 to 66 ms509 to 531 ms
Memory, warm46 MiB191 to 199 MiB
What you installone 38 MiB binary210 npm packages, 68 MiB

On a laptop neither of those numbers matters. Half a second is nothing and you have the memory. They are the price of Node, and Prism is a Node program that reads the whole document at start-up and holds it. Where they start to matter is a container that boots and dies on every CI job, and a spec big enough that the reading is the slow part.

The Stripe spec is where it stops

Stripe publishes its API as an OpenAPI document. The copy used here is 6.0 MB, 414 paths and 587 operations, which is large but not unusual: GitHub's is 8.8 MB.

Stripe, 6.0 MBMockzilla 2.8.16Prism 5.16.0
Port open195 msnever opened
First response364 msnone
Memory184 MiB warm216 MiB, still starting
Stopped atserving30 minutes as YAML, 15 more as JSON

Mockzilla reads it and starts:

$ mockzilla stripe.yml
INF Registered service name=stripe mount=/stripe
INF Mockzilla portable mode on http://localhost:2200/

Requests to it average 19 ms once it is warm.

Prism, same file, same machine:

$ prism mock stripe.yml
[CLI] …  awaiting  Starting Prism…

That is the entire log. Thirty minutes on, it was still that one line, the process pinned at 100% of a core with 216 MiB resident and nothing listening on port 4010. That is where I stopped it.

There is no flag for this and nothing is misconfigured. Parsing is not where the time goes: Python's YAML parser loads the same document in 5.3 seconds. The cost is in resolving it. Stripe's document carries 1,286 component schemas with 3,700 $refs pointing into them, and Prism walks all of that before it opens a port.

Handing it JSON instead does not help. The same document as stripe.json, same machine: no listener after fifteen minutes, at 274 MiB.

A document that size is worth trimming whichever mock server you run. Mock the Stripe API in CI cuts it down to the endpoints the tests call, which takes the memory down with it.

What comes out of a generated response

Ask the Twilio mock for its services. Prism in its default static mode:

{
  "meta": { "page": 0, "page_size": 0, "url": "http://example.com" },
  "services": [{
    "account_sid": "stringstringstringstringstringstri",
    "friendly_name": "string",
    "code_length": 0,
    "date_created": "2019-08-24T14:15:22Z",
    "url": "http://example.com"
  }]
}

Every string is the word string, padded out to whatever minLength says. Every integer is 0. Every URL is http://example.com. It is a valid document and it is unusable as test data.

The -d flag switches to dynamic generation, which runs the schema through Faker:

{
  "meta": { "page": 38433430, "page_size": 72687063 },
  "services": [{
    "account_sid": "ACaf6cd6EEcddBfAb407381DA4803D7CC6",
    "tts_name": "non deserunt in do",
    "date_created": "1967-11-27T21:39:56.0Z",
    "totp": {
      "description": "Configurations for the TOTP factors (channel) created under this Service.",
      "nullable": true
    }
  }]
}

The account_sid is right, because the schema carries a pattern. The rest is lorem ipsum, a page number of 38 million and a creation date in 1967. And totp, which the Twilio spec declares with a description and no type at all, came back as the schema node itself, so your client receives the spec's own documentation where the object should be.

Mockzilla on the same file, no flags:

{
  "meta": { "key": "incomplete-analytics", "url": "https://www.xru.com/ix-verc" },
  "services": [{
    "account_sid": "ACD7Bd4edfeB0088550DAEe04CD2cb6092",
    "friendly_name": "instant-api",
    "tts_name": "analytical-format",
    "code_length": 2142487848,
    "date_created": "2013-04-29T02:26:32.000Z"
  }]
}

Word-shaped strings and URLs that parse. And code_length of 2.1 billion, which is exactly as wrong as Prism's 6,679,169. The spec says integer and says nothing else, so every generator is guessing. What differs is where you go to fix it.

Prism has two answers and both are inside the spec. example: 6 on the property pins it, in static mode:

code_length:
  type: integer
  example: 6
$ curl localhost:4010/svc
{"code_length":6,"friendly_name":"string"}

x-faker: datatype.number improves it, in dynamic mode. They do not combine. Dynamic generation ignores examples, so the same spec on -d gives this:

$ curl localhost:4010/svc -H "Prefer: dynamic=true"
{"code_length":80674512,"friendly_name":"velit cupidatat occaecat in irure"}

Pick one: pinned values with string everywhere else, or varied data with nothing pinned. Prism's docs also note that x-faker output depends on which Faker version is installed locally, so two developers can get different responses from one document.

Mockzilla keeps it in a file beside the spec, with the spec untouched, and the two do combine:

verify/
  openapi.yml
  context.yml
code_length: 6
friendly_name: "checkout-otp"
page_size: 50
{
  "meta": { "page_size": 50 },
  "services": [{
    "friendly_name": "checkout-otp",
    "code_length": 6,
    "account_sid": "ACdb81c4b9EE69aEAFf7DDcD81F6BD1c31"
  }]
}

A name matches at any depth, so one line covers that field wherever it appears. Everything you did not name stays generated. You buy back the three fields your assertions touch and leave the other fifty alone.

Several OpenAPI specs on one mock server

"A single Prism instance serves one OpenAPI document." That is Prism's own documentation, and what it recommends for a second document is a second process on a second port with a reverse proxy in front of both:

services:
  proxy:
    image: caddy
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
    ports:
      - '8080:80'
    depends_on:
      - prism_1
      - prism_2
  prism_1:
    image: stoplight/prism:4
    command: mock -p 4010 --host 0.0.0.0 petstore-v2.yaml
  prism_2:
    image: stoplight/prism:4
    command: mock -p 4010 --host 0.0.0.0 petstore-v3.yaml
route /app_1/* {
	uri strip_prefix /app_1
	reverse_proxy prism_1:4010
}

route /app_2/* {
	uri strip_prefix /app_2
	reverse_proxy prism_2:4010
}

Most test suites talk to more than one vendor. Three of them is three Node processes on three ports, a Caddyfile, and something to supervise the lot, on every developer machine and in every CI job. Each process holds its own copy of its own document, so the 191 to 199 MiB measured earlier is per API rather than per machine.

Mockzilla takes the folder:

services/
  openai.yml
  slack.yml
  verify.yml
mockzilla ./
INF Registered service name=openai mount=/openai
INF Registered service name=slack  mount=/slack
INF Registered service name=verify mount=/verify
INF Mockzilla portable mode on http://localhost:2200/

OpenAI, the Slack Web API and Twilio Verify: 255 endpoints, one port, 90 ms to listening and 70 MiB. The file name is the prefix.

curl localhost:2200/openai/models
curl localhost:2200/slack/api.test
curl localhost:2200/verify/v2/Services

There is no proxy in front of that, because there is nothing to proxy between. One base URL goes in your test config and the paths sort themselves out.

Sharing it without a server

Putting the APIs on one port fixes your machine. The next person still has to rebuild the folder, and the specs on their own are not the setup: the config, the context overrides and the static responses are what make two developers see the same answers.

pack puts the lot in one file.

$ mockzilla pack --output vendors.mockz ./
Packed ./ -> vendors.mockz

1.0 MB of specs comes out as an 89 KiB archive carrying a manifest of every service, its mount and its mode. Commit it, attach it to a release, drop it in a bucket. Whoever picks it up runs one command:

mockzilla vendors.mockz

Or skips the download and points at where it lives:

mockzilla https://example.com/vendors.mockz

Both bring up the same three services on the same mounts, and context.yml travels with them, so their code_length comes back as 6 like yours rather than 2.1 billion. Nothing is hosted and nothing is paid for. pack is in the open-source CLI.

Prism has no packaging step. What you hand over is the document, and everything around it is each person's own.

The URL your team calls

Add the GitHub Action to the repository that already holds the spec:

name: mockzilla

on:
  push:
    branches: [main]
  pull_request:
    types: [opened, synchronize, reopened, closed]

jobs:
  publish:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      pull-requests: write
    steps:
      - uses: actions/checkout@v4
      - uses: mockzilla/actions@v1
        with:
          token: ${{ secrets.GITHUB_TOKEN }}

There is no form, no confirmation email and no key to paste. When the job finishes, the simulation is at https://api.mockz.io/gh/<org>/<repo> and the step writes the URL to the job summary, to its url output for later steps, and to a comment on the pull request.

Every branch gets its own copy at https://api.mockz.io/gh/<org>/<repo>/<branch>, built from that branch's spec and torn down when the pull request closes. Keep the branch names flat: the router reads the ref as one path segment, so feature/checkout deploys and then answers from the default branch instead.

Nobody runs anything. There is no npm install in the test job, no port to pick, no process to wait for and no container to keep alive across steps.

The free plan is one simulation on its default branch, 1,000 requests a month, 5 requests a second and 128 MB of memory, which is why the whole Stripe document at 184 MiB needs more than free. Per-branch copies and more memory are on the paid plans at pricing, and the six limits that cost money move independently in the plan builder.

A payment provider needs more than its spec

Mocking Stripe from Stripe's document gets you response shapes. It does not get you a payment.

A charge is authorized, then captured, then refunded, and every step has to remember the one before it. Stripe refuses some of those orders and a stateless mock will wave them through. The decline you want to test has a code specific to the provider, and a vendor's own sandbox will not produce half of them on demand.

So for payments we ship sandboxes rather than generated mocks. Adyen, Stripe, Klarna, PayPal and the rest run the real lifecycle: authorize, capture, partial capture, refund, cancel, adjust an authorization. Idempotency keys behave the way the vendor's do, so sending the same key twice returns the first action instead of charging again. 3D Secure runs end to end without a person, challenge page included. Name the cardholder after the case you want and that refusal comes back in the provider's own code.

The same is there for identity: KYC and KYB providers, with the verification outcomes their sandboxes will not hand you.

Payment provider sandboxes · Identity provider sandboxes

What Prism does better

Three things, and if you need the first one this comparison ends here.

Forcing a status code, per request. Prefer: code=404 on any call, or ?__code=404 if a header is awkward, and Prism serves that response out of the document. Ask for one the document does not declare and you still get the status, with a Prism error body saying so. Mockzilla has no per-request equivalent: a static file answers 200, other codes come from the spec, and the error rules in config.yml are percentages (p5: 500 sends five percent of calls to a 500). If your test needs this call, right now, to come back 409, Prism has it and we do not.

Enforcing the spec's security. Twilio Verify declares HTTP basic auth, so Prism answers 401 until you send credentials, with a WWW-Authenticate header. Mockzilla answers 200 and leaves auth to you. Prism's behaviour is the contract-accurate one, and it will catch a client that forgot to send a token.

Older and stranger documents. Prism reads OpenAPI 2.0, the version still called Swagger, and Postman Collections. Mockzilla reads OpenAPI 3.x. Hand it a swagger: "2.0" file and it does not recognise it as a spec at all. If your document has not been converted yet, that decides it.

Which OpenAPI mock server to pick

On your own machine the two do the same job and both are free to run, so the numbers above are the whole difference. 51 ms against 509 ms to start. 46 MiB against 191. One port for every spec you own instead of one process each, and a 6 MB document that serves instead of never opening a port.

Prism keeps three things we do not have, and if you need one of them that settles it: forcing a status code per request, enforcing the document's security schemes, and reading Swagger 2.0 or a Postman Collection.

The hosted half is the part Prism itself does not do, and the part Stoplight charges $44 a month for with a design suite attached. A URL your whole team calls, a fresh one on every pull request, put up by the pipeline you already run, with nothing to install in the test job. Ours starts on the free plan.

If Prism is not the only thing you are weighing, the same measurements against WireMock are in WireMock alternative: mock an OpenAPI spec without writing stubs.

Install the CLI and point it at a spec you have lying around. The hosted side runs the same engine, so nothing you learn locally is thrown away.

Was this page helpful?

More articles

All topics