Performance · Load testing · Testing
API load testing in the browser: virtual users, profiles and percentiles
Run a load test against your API from the browser with virtual users and fixed, ramp-up, spike or peak profiles, and learn how to read throughput, p50/p95/p99 and error rate.
You can load test an API from the browser in Swastik Codes without installing anything: pick a collection, choose how many virtual users to run and for how long, pick a load profile (fixed, ramp-up, spike or peak) and press Run. The load is generated by the swastik.codes server, and the results stream back live as charts of throughput, response-time percentiles and error rate, with a per-request breakdown at the end.
This guide explains how the performance runner works, walks through a first test, and spends most of its time on the part that matters: reading the numbers correctly and knowing when a browser-based load test is enough and when it is not.
How the performance runner works
The model is simple and matches what most API tools call a "performance runner":
- A virtual user (VU) runs the selected requests in collection order, then starts again from the top, until the test ends or it is asked to stop.
- Each VU has its own variable scope, so a token saved by a login request in one VU's loop stays available for that VU's next requests. Pre-request and test scripts run for every request, exactly as they do in a normal send.
- If you attach a data file (CSV or JSON), VU number n uses row n modulo the number of rows. Ten VUs and five rows means each row is used by two VUs.
- The load profile decides how many VUs are active at each moment. The controller re-checks it every 100 ms, starting new VUs or asking the newest ones to stop after their current request.
- A sample counts as an error when there is no response, a transport error (DNS, TLS, timeout, connection reset), or an HTTP status of 400 or higher.
- When the duration is over, requests still in flight are abandoned and not counted, so the test never overruns.
- All VUs share one connection pool and one cookie jar.
The four load profiles
| Profile | Shape | Good for |
|---|---|---|
| Fixed | The full number of VUs for the whole duration | A steady baseline you can compare between releases |
| Ramp up | From start users up to the maximum over ramp-up seconds, then hold | Finding the point where latency starts to climb |
| Spike | Base users all the time, the maximum for spike length seconds starting at spike at | Checking how the API copes with, and recovers from, a sudden burst |
| Peak | Ramp from base users to the maximum over ramp, hold for peak hold, ramp back down | A rehearsal of a busy period, such as a launch or a sale |
The setup panel draws the profile as a small chart before you run, so you can see exactly how many VUs will be active at each second.
Limits on the hosted service
The web app runs load tests on the swastik.codes server, so there are caps to keep the service fair for everyone. Currently a run can use up to 50 virtual users and last up to 300 seconds, each user can start up to 10 runs a minute, and one run per user can be active at a time. These limits may change. TLS certificate checks cannot be switched off for server-side load tests.
As with every request sent from the web app, targets must be publicly reachable: private, loopback and reserved addresses are refused. The load comes from our server's IP address, not yours, which matters if your API has an IP allowlist or a WAF that rate-limits by address.
Step by step: your first load test
1. Prepare a collection
Open the app and create a free account (sending requests from the web app requires one). Put the requests you want to test in a collection, or in a folder of it. A realistic test usually has a small flow rather than a single endpoint, for example:
POST {{baseUrl}}/auth/login, with a test script that saves the token;GET {{baseUrl}}/products?page=1;GET {{baseUrl}}/products/{{productId}}.
The login test script might be:
swc.test("login succeeded", () => swc.response.to.have.status(200));
const { token } = swc.response.json();
swc.environment.set("token", token);and the other requests use Bearer {{token}} in their auth. Every VU works on its own copy of the variables, so each VU logs in and keeps its own token; nothing is written back to your saved environment.
Send each request once by hand first. A load test of a request that fails with 401 only tells you that it fails very quickly.
2. Open a performance test
Click New in the top bar and choose New performance test (it is also in the command palette, Ctrl+K / Cmd+K). In the Requests section choose the collection, optionally a folder ("Entire collection" by default), tick the requests to include, and pick an environment.
3. Set the load
In the Load section set Virtual users and Duration, then pick a profile and fill in its fields. For a first run, try something modest:
- profile Ramp up, start users 1, ramp-up 30 s;
- 20 virtual users;
- duration 120 s.
Optionally attach a data file if each VU should use different input, such as different user accounts.
4. Run and watch
Press Run. While it runs you see live totals (total requests, throughput, average response, P50, P90, P95, P99, error rate and current virtual users) and three charts: throughput with VUs, response time (average and percentiles), and error rate. When it finishes, a table lists each request with its count, requests per second, average, minimum, maximum, P50, P90, P95, P99 and errors. Distinct error messages are listed underneath.
5. Compare and keep the results
Finished runs are saved under Previous runs in your browser (the most recent 30 per workspace). Open an earlier run and choose it as a baseline: the charts overlay the baseline as dashed lines, and a comparison table shows whether throughput, latency and errors got better or worse. Export JSON downloads the full result if you want to keep it with a release.
Reading the numbers
Most mistakes in load testing come from reading the results wrongly, not from running the test wrongly.
Throughput is an output, not an input
You set virtual users, not requests per second. Each VU sends its next request as soon as the previous one finishes, so throughput is roughly VUs / average response time. Twenty VUs against an endpoint that answers in 100 ms give at most about 200 requests per second. If the API slows to 400 ms, the same twenty VUs produce only 50 requests per second. A drop in throughput at constant VUs is therefore a sign of slower responses, not of fewer users.
Use percentiles, not the average
The average hides the experience of the slowest users. Percentiles do not:
- P50 (the median): half the requests were faster than this. It describes the typical request.
- P95: 1 request in 20 was slower than this. This is where queueing and lock contention usually show up first.
- P99: 1 request in 100 was slower. If one page makes ten API calls, a large share of page views include at least one call this slow.
A healthy result has P95 and P99 reasonably close to P50 and stable over the run. A P99 several times higher than P50, or percentiles that climb steadily while VUs are flat, points at a saturated resource: a connection pool, a database, a thread pool or a downstream service.
Watch the error rate alongside latency
A fast run with a 30% error rate is not a fast API, it is an API that is rejecting work quickly. Check the error list: 429 means a rate limiter is doing its job, 502/503/504 usually mean an upstream or a load balancer gave up, and timeouts mean the API never answered. Remember that errors here are transport failures and HTTP status codes of 400 and above. A failing swc.test assertion on a 200 response is not counted as an error, so check your functional behaviour with the collection runner as well.
Look at the shape over time
The charts matter more than the totals. Three patterns are worth recognising:
- Latency rises with VUs, then flattens while throughput stops growing. You found the capacity limit. The VU count where the curve bends is a useful number to write down.
- Latency spikes during a spike profile, then recovers. Good: the system absorbs bursts. If it does not recover after the spike ends, something is stuck, such as a queue that never drains.
- Errors start late in a fixed-load run. Something accumulates: memory, open connections, or a cache that fills up. Run longer to confirm.
Be careful with conclusions
The load comes from one region and one server, over the public internet. Measured latency includes the network path between our server and your API. Compare runs with each other, from the same setup, rather than treating one number as the truth about production. And keep the test environment as close to production as you can: a staging database with a hundred rows will not behave like one with ten million.
When to graduate to a dedicated load-testing tool
A browser-based performance runner is the right tool for quick checks: "did this release make the search endpoint slower?", "does the API hold up with 50 concurrent users?", "what happens during a burst?". It reuses the collection, variables and scripts you already have, so there is nothing to write.
For larger or more formal tests, dedicated tools such as k6, Gatling, Apache JMeter and Locust are a better fit. They can generate far more load, run from several machines or regions, model arrival rates (requests per second) rather than only concurrent users, run inside CI with pass/fail thresholds, and export to monitoring systems. They take more setup, and the tests are written as code or in their own formats, but they are built for exactly that job. A common approach is to use Swastik Codes to explore and to catch regressions early, and a dedicated tool for capacity planning before a big launch.
Frequently asked questions
Is load testing in Swastik Codes free?
Yes. Swastik Codes is free to use today. Runs are limited (currently up to 50 virtual users and 300 seconds on the hosted service) to keep it fair for everyone.
Can I load test an API running on localhost?
Not from the web app. Tests run on the swastik.codes server, which refuses private and loopback addresses. Use a publicly reachable staging URL, or run the load test in the desktop app, where virtual users run on your own computer.
Do my test scripts run during a load test?
Yes. Pre-request and test scripts run for every request in every VU, so you can log in, save tokens and chain requests. Script-heavy collections add some overhead on the client side of the test.
What counts as an error?
No response, a transport error, or an HTTP status of 400 or above. Failing test assertions are not counted as load-test errors.
Can I compare two runs?
Yes. Choose an earlier run as the baseline to overlay it on the charts and see the differences in a table.
Run your first test
Open the app, create a free account, and start with a small ramp-up test against an endpoint you own. The load testing feature page summarises the options, Writing API tests with swc.* scripts shows how to prepare tokens and assertions, and data-driven API testing explains data files in more detail.