Mockoon alternative: keep your mock API in sync with your OpenAPI spec

Sep 15, 2026·13 min read

You imported Stripe's spec into Mockoon and fixed a few responses. It runs on your laptop.

Then your team needs the same mock, and so does CI. For that, Mockoon sells Mockoon Cloud, starting at $145 a month.

Mockzilla is a Mockoon alternative with a free hosted plan. Mockoon hosts a copy of your spec, converted into its own format on the day you imported it. Mockzilla hosts the spec itself and rebuilds the mock from it on every push, so the mock stays in sync with the spec. On your own machine, both tools are open source under the MIT license.

Mockzilla vs Mockoon, side by side

MockzillaMockoon
On your machineone binary with the API explorer built in, MITa desktop app, and a separate CLI from npm, MIT
Hosted plan fromfree$145 a month, or $100 a month billed yearly
The hosted mock is built fromyour OpenAPI spec, on every deploya Mockoon file, imported once or built by hand
When the spec changespush the new filereimport adds new routes and changes no existing ones
Deploys the hosted mock from CIGitHub Actionno
A URL per pull requeston paid plansno
Fail a set share of requestsyesno
Record real answers and replay themyes, matched on fields you choosein the desktop app, one per path and method
ReadsOpenAPI 3.xOpenAPI 2.0 and 3.x
Remembers data between callsonly in the payment and identity sandboxesyes

Mockoon Cloud pricing

Mockoon Cloud has two plans. Team costs $145 a month, or $100 a month if you pay for a year. Enterprise is priced on request. There is no free plan, only a trial: 14 days if you sign up with a work email, 7 days with a credit card otherwise.

Mockoon's Team plan next to our Starter plan, which has the same number of seats:

Mockzilla StarterMockoon Team
Price$20 a month$145 a month, or $1,200 a year
Team members55
Hosted mocks5 simulations, each with as many APIs as fit in 1,024 MB of memory3 API mocks
Requests a month200,000100,000
Requests a second2010
PR environments10none

Our other plans are Free, Hobby at $10 and Pro at $50, all on our pricing page.

What Mockoon hosts is a copy of your spec

When you import a spec, Mockoon converts it into its own file format. From then on, that file is what you edit and deploy, and the spec is not used anymore.

Stripe's spec is 6.4 MB, and the converted Mockoon file is 30.5 MB. The Team plan allows 30 MB per mock, so the full Stripe API is right at the limit.

Mockoon's own documentation calls import "a good starting point but not a way to share your mock APIs".

The problem shows up when the spec changes. In March 2025 Stripe released an API version called basil. It moved two fields, current_period_start and current_period_end, from the subscription to each subscription item. If you use Stripe subscriptions, your code probably reads one of them.

We imported the Stripe spec from before basil into Mockoon. Mockoon's documentation says reimport only adds what is missing: "No existing routes or responses will be modified or deleted". Reimporting the basil spec adds 2 new routes. The other 559 routes stay as they were, including the subscription route:

$ curl -s localhost:3000/v1/subscriptions/sub_123 \
    | jq '{current_period_end, item_period_end: .items.data[0].current_period_end}'
{
  "current_period_end": 13345,
  "item_period_end": null
}

The mock still returns the old format. If your code is already updated for basil, it reads the item and gets null. Code that was not updated reads the old field, and its tests pass, even though Stripe no longer sends that field on basil. Two endpoints that basil removed still answer with 200.

Mockzilla reads the spec directly, so to update the mock you commit the new spec:

$ curl -s localhost:2200/stripe/v1/subscriptions/sub_123 \
    | jq '{current_period_end, item_period_end: .items.data[0].current_period_end}'
{
  "current_period_end": null,
  "item_period_end": 1166861466
}

The fields are where basil puts them, and the two removed endpoints return 404.

In Mockoon, the only way to get a changed route from a new spec is to delete the route and import it again. That also deletes any changes you made to it.

What comes out of an imported response

Mockoon fills Stripe's text fields with empty strings. Create a customer, the way a test would:

$ curl -s localhost:3000/v1/customers -d email=jenny@example.com -d "name=Jenny Rosen" \
    | jq -c '{id, object, email, name, currency}'
{"id":"","object":"customer","email":"","name":"","currency":""}

Across the whole Stripe mock, 223,537 fields are empty, and currency alone is empty in 4,641 places.

The empty id breaks the next call in a test. Fetching the customer by that id requests /v1/customers/, which returns the customer list instead:

$ curl -s localhost:3000/v1/customers/ | jq -c '{object}'
{"object":"list"}

Mockzilla, same spec, nothing configured:

$ curl -s localhost:2200/stripe/v1/customers -d email=jenny@example.com -d "name=Jenny Rosen" \
    | jq -c '{id, object, email, name, currency}'
{"id":"689f8818-8da1-4269-90bd-108a244faaed","object":"customer","email":"anya@gmail.com","name":"individual-report","currency":"classic-configuration"}

The email looks real, but a Stripe customer id is not a UUID, and classic-configuration is not a currency. Neither tool knows what real Stripe data looks like, so you fix it yourself either way.

In Mockoon, you fix it route by route: you open each route and edit its response body, for example with {{body 'email'}} to return the email from the request. Since reimport never updates an existing route, a route you fixed stays on the old spec.

Mockzilla keeps the fixes in a context.yml next to the spec, and the spec stays unchanged:

services/
  stripe/
    openapi.yml
    context.yml
currency: ["usd", "eur", "gbp"]
livemode: false
in-response:
  email: "request:email"
  name: "request:name"
$ curl -s localhost:2200/stripe/v1/customers -d email=jenny@example.com -d "name=Jenny Rosen" \
    | jq -c '{id, object, email, name, currency, livemode}'
{"id":"9a2a6786-ed25-4573-9fb7-c14184663cd3","object":"customer","email":"jenny@example.com","name":"Jenny Rosen","currency":"gbp","livemode":false}

Each line applies to every field with that name, anywhere in Stripe's responses, so one currency line fixes all of them. request: copies the value from the request. When Stripe releases a new version, you replace openapi.yml and keep context.yml as it is. A test can override values for a single request with the X-Mockzilla-Context header.

Latency, errors and replay

Some tests need Stripe to be slow or to fail. Others need the same answer every time. In Mockzilla these are settings in a config.yml next to the spec:

services/
  stripe/
    openapi.yml
    context.yml
    config.yml
latency: 120ms
errors:
  p2: 500
  p5: 429

Every Stripe endpoint now takes 120 ms longer, and some requests fail: 2% with a 500 and the next 3% with a 429. Of 1,000 requests, 956 answered 200, 24 answered 429 and 20 answered 500. The error body is plain text, Simulated error, not the JSON that Stripe sends with its errors.

To slow down one call, send a header. This one took 3,030 ms:

curl -H "X-Mockzilla-Latency: 3s" localhost:2200/stripe/v1/customers/cus_123

Replay records an answer and returns it again for the same request. Point the mock at Stripe's real API and record what comes back:

upstream:
  url: https://api.stripe.com
replay:
  auto-replay: true
  upstream-only: true
  endpoints:
    /v1/payment_intents:
      POST:
        match:
          body:
            - amount
            - currency

The first POST /v1/payment_intents goes to Stripe with your test key, and Mockzilla records the answer. After that, a request with the same amount and currency gets the recorded answer, marked X-Mockzilla-Source: replay, and Stripe is not called. A different amount makes a new recording. upstream-only means only real Stripe answers are recorded.

Mockoon has latency too, for the whole mock or for each response. For errors, you add error responses to a route and pick them by rule or at random, but there is no setting that fails a share of all requests. Its desktop app has a record mode for replay: in proxy mode, it turns calls to the real API into routes, one per path and method. After that, every POST /v1/payment_intents gets that one recorded answer, whatever amount you send.

Start-up time and memory, measured

We ran both tools on Stripe's current spec on one laptop. Each test ran three times, timed from launch to the first response.

Stripe, 6.4 MBMockzilla 2.8.16Mockoon CLI 9.8.0
First response, from the spec210 to 242 ms1,335 to 1,386 ms
Memory, warm194 MiB477 to 487 MiB
First response, from the converted fileno such file457 to 488 ms at 280 MiB
What you installone 40 MiB binary, API explorer includedNode.js and 252 npm packages, 103 MB

Mockoon handles the full Stripe spec. Prism could not: in our Prism comparison it did not start within 30 minutes. When you give the Mockoon CLI a spec, it converts the spec on every start, so starting takes over a second. From an already converted file, it starts in under half a second.

The Mockoon CLI has no user interface, so if you want one, you install the desktop app separately.

On a laptop these numbers do not matter much. In CI they do, because the mock is installed and started again in every job.

Mock APIs in CI

There are three ways to use a Mockoon mock in CI:

  1. Mockoon's GitHub Action. It installs the Mockoon CLI with npm in every job, which took 8 to 10 seconds here, and runs the mock on the CI machine. The mock is the Mockoon file in your repository, so it does not change when the spec changes.
  2. Download the hosted mock into each job. The command is mockoon-cli start --data cloud://<uuid> --token <token>. This needs the Team plan, and each start counts as one of 1,000 pulls a month.
  3. Call the hosted URL, which looks like https://<subdomain>.<server>.mockoon.app. All branches share one mock and the plan's limit of 10 requests a second. Mockoon's documentation says the URL can change if the mock is stopped and started again.

None of these lets CI update the hosted mock. Mockoon's access tokens can only read (environment:read). To change the hosted mock, someone has to open the Mockoon app.

With Mockzilla, CI is what updates the mock. Add the GitHub Action to the repository with your 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 }}

Put the spec at services/stripe/openapi.yml, with context.yml next to it. On every push, the mock is rebuilt at https://api.mockz.io/gh/<org>/<repo>. The Action posts that URL in the job summary, as its url output, and as a comment on the pull request.

Every branch also gets its own mock at https://api.mockz.io/gh/<org>/<repo>/<branch>, built from that branch's spec. It is deleted when the pull request closes. So a branch that updates your code for basil gets a basil mock, and main keeps the old one until you merge. Use branch names without slashes: feature/basil would deploy, but its URL would answer with the default branch.

Nothing is installed in the test job. There is no account to create first: the GITHUB_TOKEN proves the repository is yours, and the account is created on the first push.

The free plan includes one simulation on the default branch, 1,000 requests a month, 5 requests a second, 128 MB of memory, and replay recordings kept for 2 hours. The full Stripe spec needs 194 MiB, so on the free plan you trim it to the endpoints your tests use, as shown in Mock the Stripe API in CI. PR environments and more memory come with the paid plans on the pricing page.

What Mockoon does better

If you need one of these three things and do not want to write code, stay on Mockoon.

Building a mock by hand. Mockoon is a desktop app where you can create routes without any spec and set each response's body and status code by clicking. Mockzilla's API explorer lists and calls the endpoints from your spec, but you cannot edit them there. In our web app you can add static endpoints by hand, but without a spec nothing else is generated. If your API has no OpenAPI spec, Mockoon is the better fit.

Different responses for different requests. A Mockoon route can have several responses and pick one based on the request. For example, it can return 402 when the request contains pm_card_visa_chargeDeclined, Stripe's test payment method for a declined card. Callbacks can send a follow-up request after a call, which is how you fake a webhook. In Mockzilla, picking a different response or status code based on the request needs Go code in codegen mode. Its error settings only fail a random share of requests.

Saving data between calls. In Mockoon, you can create a record with POST and get it back with GET, both in the free desktop app and in the cloud. Mockzilla's mocks do not save data, so a customer you create is gone when you ask for it. Our payment sandboxes do keep data: a Stripe payment goes through confirm, capture and refund the way it does at Stripe. That is a separate product.

Mockoon also reads Swagger 2.0 files, and Mockzilla does not. Stripe's spec is OpenAPI 3.0, so this only matters for older APIs.

Which Mockoon alternative fits

Stay on Mockoon if you build mocks by hand, or if you need different responses and saved data without code. The desktop app and the CLI are free and good at that, and Mockoon Cloud puts them online.

Switch to Mockzilla if your API has an OpenAPI spec and you want the mock to follow it. When Stripe changes its API, you commit the new spec, and the hosted mock and every PR environment are rebuilt from it. Your context.yml keeps working, and the hosted plan starts free.

We compared Stoplight Prism and WireMock the same way.

Install the CLI and run it on Stripe's spec. Open http://localhost:2200/ to see all 594 endpoints in the API explorer. The hosted version runs the same engine.

Was this page helpful?

More articles

All topics