You can test an API without Postman using tools you already have. For a single call, curl in a terminal or fetch() in the browser console is enough. If the API has an OpenAPI or Swagger spec, a browser-based tester that reads the spec is faster still, because it builds each request from the documented paths, parameters and auth for you. Browser testers have one catch: CORS can block the call, so the good ones route the request through a server-side proxy when that happens. This guide compares the options, explains the CORS problem and how proxies work around it, and walks through testing one endpoint from a spec.
When Postman is more than you need
Postman is built for teams that keep shared collections, environments, mock servers and monitors in one workspace. If that describes your week, keep using it. Many API checks are smaller than that:
- You want to see what one endpoint returns before you write the frontend code that calls it.
- A teammate sent a Swagger UI link and you need to confirm the staging API answers with the fields the docs promise.
- You are on a locked-down work laptop where installing a desktop app means a ticket to IT.
- You are reproducing a bug report and need the exact status code and headers, once.
- You would rather not sign in to a cloud account to send a GET request.
For these jobs the cost of Postman is setup: installing the app, signing in, creating a workspace, and then typing in a URL, headers and body by hand. Recent Postman versions push you toward an account, and the features available while signed out have changed more than once, so check the current terms if that matters to you.
The options, compared
Each tool below can send an HTTP request and show you the response. They differ in where they run, whether they ask for an account, and whether they can read an OpenAPI spec. Pricing and login rules change often, so treat the account column as a starting point and confirm on each project's site.
| Tool | Runs in | Account to start | Imports OpenAPI | Good fit |
|---|---|---|---|---|
| Browser DevTools fetch() | Any browser console | None | No | A quick call to an API on the same origin as the open page |
| curl | Terminal (ships with macOS, most Linux distros and Windows 10+) | None | No | One-off calls, scripts, sharing an exact repro |
| HTTPie | Terminal (open-source CLI); also desktop and web apps | None for the CLI | Varies by app | Readable command-line requests with JSON shortcuts |
| Bruno | Desktop app (open source) | None for the core app | Yes | Collections stored as plain files in your Git repo |
| Hoppscotch | Browser (hosted or self-hosted), also desktop | None for basic use | Yes | A Postman-style interface in a browser tab |
| Insomnia | Desktop app | Has varied by version; local-only storage is available | Yes | Teams already on Kong tooling |
| VS Code REST Client | VS Code extension | None | No | Requests saved as .http files next to your code |
| Thunder Client | VS Code extension | Some features are on paid plans | Yes | A GUI client without leaving the editor |
| APIForge | Browser | None | Yes, it starts from the spec | Testing endpoints straight from an OpenAPI or Swagger URL, with response validation |
If you live in a terminal, curl and HTTPie are hard to beat for single calls. If you want saved collections under version control, Bruno and the VS Code REST Client keep requests as files you can commit. Hoppscotch is the closest thing to Postman in a browser tab. APIForge covers a narrower job: you have a spec, and you want to call its endpoints and check the responses against it without building each request yourself.
The same request in three tools
# curl
curl -i https://api.example.com/v1/users/42 \
-H "Authorization: Bearer $TOKEN" \
-H "Accept: application/json"
# HTTPie
http GET https://api.example.com/v1/users/42 \
"Authorization:Bearer $TOKEN" Accept:application/json@token = paste-a-test-token-here
GET https://api.example.com/v1/users/42
Authorization: Bearer {{token}}
Accept: application/jsonAll three are fine for one endpoint you already know. The work grows with the API: for a spec with 60 operations you still have to read the docs for each path, its parameters and its body shape, then type them in.
Why browser-based testers hit CORS
A request sent from JavaScript on a web page follows the browser's same-origin policy. When a page on one origin calls an API on another, the browser sends the request (or first an OPTIONS preflight) and only hands the response to the page if the API replies with matching Access-Control-Allow-Origin and related headers. curl, HTTPie, Bruno and the VS Code extensions never run into this, because they are not web pages.
Many APIs send no CORS headers at all, since their real clients are servers or mobile apps. Others allow only their own frontend's origin. Either way, an online API tester running in your browser gets the same opaque "Failed to fetch" error that Swagger UI shows. A request with an Authorization header or a JSON body makes it more likely, because it triggers a preflight that the server must also answer.
Using the console without a CORS error
// Open DevTools on a page served from api.example.com itself,
// for example its /docs or /health page, then run:
const res = await fetch("/v1/users/42", {
headers: { Authorization: "Bearer TEST_TOKEN" },
});
console.log(res.status, await res.json());A request from a page to its own origin is same-origin, so CORS does not apply. Running fetch() from a tab on a different site, such as a blank tab or another app, will be blocked unless the API allows that origin.
There are three common ways around it. You can fix the API's CORS configuration, which is the right answer for your own frontend but needs server access. You can install a browser extension that sends requests outside the page's sandbox; Hoppscotch offers this as one of its options. Or the tester can send the request from its own server, where the browser's rules do not apply.
How a proxy gets around CORS, and what it costs
Direct: browser page ---> api.example.com
<--- response without CORS headers
browser blocks the response ("Failed to fetch")
Proxy: browser page ---> tester's server ---> api.example.com
<--- response
browser page <--- tester's server (same origin, allowed)Browsers enforce CORS, and a server calling an API skips the check entirely. The page sends the request description (method, URL, headers, body) to the tester's own backend, which is the same origin as the page. That backend calls the API, reads the response and passes it back.
In APIForge, the request panel has three modes. Browser sends the request with fetch() straight from your tab. Proxy always sends it from APIForge's server through the /api/test-request endpoint. Auto, the default, tries the browser first and retries through the proxy only when the browser reports a CORS or network error. A timeout does not trigger the fallback.
The trade-offs are real, and they apply to any proxy-based tester:
- Your headers, including any token, pass through the tester's server. Use a test or staging token, and avoid production credentials.
- A hosted proxy cannot reach an API that only exists on your machine, such as http://localhost:3000. For that, use Browser mode with CORS enabled locally, or a terminal tool.
- Proxies have limits. APIForge's caps each request at a 30-second timeout, a 2 MB request body, a 5 MB response and 3 redirects, with 60 requests per minute per client. It also checks each destination and redirect target before connecting and drops credential headers when a redirect leaves the original origin.
- A proxy only tells you the API works. Your own frontend will still be blocked until the API sends the right CORS headers.
If Swagger UI shows Failed to fetch and you want to know which header is missing, the Swagger UI Failed to fetch guide covers each cause and its server fix.
Testing from an OpenAPI spec vs building requests by hand
A spec already describes every operation: the path, the method, which parameters are required, the request body schema, the auth scheme and the response schemas for each status code. A tool that reads it can fill in the request form and then check the response you get back.
Hand-built requests vs spec-driven requests
| Task | Hand-built (curl, generic client) | From the spec |
|---|---|---|
| Finding the endpoint | Read the docs, copy the path | Pick it from a list grouped by tag |
| Path and query parameters | Typed from memory or docs | Form fields generated from the spec, required ones marked |
| Request body | Written by hand | Starts from the schema or the documented example |
| Auth | You look up the scheme and header name | The security scheme tells you bearer, basic or API key |
| Checking the response | You eyeball the JSON | Compared against the documented schema for that status code |
| When the API changes | Your saved request goes stale | Reload the spec and the form follows it |
Hand-built requests still win when there is no spec, when you need to send something the spec does not allow (a malformed body, a missing header) to test error handling, or when you want a command to paste into a bug report. For everyday checks against a documented API, starting from the spec removes most of the typing and gives you schema validation in the same step. The guide to validating API responses against an OpenAPI schema explains what that check catches.
Sending auth headers
Most failed first requests are auth problems. OpenAPI specs declare the scheme under components.securitySchemes, and the three you will meet most are below.
| Scheme in the spec | What gets sent | curl example |
|---|---|---|
| http, scheme: bearer | Authorization: Bearer <token> | -H "Authorization: Bearer $TOKEN" |
| http, scheme: basic | Authorization: Basic base64(user:password) | -u user:password |
| apiKey, in: header | A named header, for example X-API-Key: <key> | -H "X-API-Key: $KEY" |
| apiKey, in: query | A query parameter, for example ?api_key=<key> | "https://api.example.com/v1/items?api_key=$KEY" |
APIForge has fields for all four. It builds the Basic header from the username and password you enter, and it puts an API key in the header or query string based on the location you choose. If you add your own Authorization header, the auth fields will not overwrite it.
A request that adds an Authorization header is no longer a CORS "simple" request, so the browser sends a preflight first. This is why a public GET often works in a browser tester while the same endpoint with a token fails. The Swagger UI bearer token guide covers the other common token mistakes.
Step by step: test an endpoint from a spec in the browser
Testing one endpoint with APIForge
Find the spec URL
Look for /openapi.json, /swagger.json, /v3/api-docs or /swagger/v1/swagger.json on the API's host. If you only have a Swagger UI page, APIForge can usually discover the spec from it, and the guide to getting the OpenAPI JSON from Swagger UI lists the manual routes. A local .json or .yaml file works too.
Load it
Paste the URL or upload the file on the APIForge home page. The endpoints appear grouped by tag, with the method and path for each operation.
Pick the server
Specs often list several servers (production, staging, local). Choose staging or a sandbox for testing, so test writes do not touch real data.
Fill in parameters and body
Required path and query parameters are marked. For POST, PUT and PATCH, start from the generated body and change only the fields you are testing.
Add auth
Choose bearer, basic or API key to match the security scheme in the spec, and paste a test credential.
Send in Auto mode
The request goes from your browser first. If CORS or the network blocks it, APIForge retries through its proxy and shows which mode returned the response.
Read the result
Check the status code, headers, timing and body, then the schema validation result. A missing required field or a wrong type in the response points to drift between the API and its docs.
Take it further
Generate TypeScript types or an AI IDE prompt for the endpoint, or check the spec's quality score before you build against it.
Which tool should you reach for?
| Scenario | Recommendation | Alternative |
|---|---|---|
| You need one quick call and you have a terminal open | curl or HTTPie | fetch() in the console if you are already on the API's own origin |
| The API has an OpenAPI or Swagger spec and you want to try several endpoints | A spec-driven browser tester such as APIForge | Import the spec into Bruno or Hoppscotch if you want a saved collection |
| You want requests versioned in Git alongside the code | Bruno or VS Code REST Client .http files | — |
| The API runs only on localhost | A terminal or desktop tool, or a browser tester in direct mode with CORS enabled on your dev server | — |
| Your team shares collections, environments and monitors every day | Postman or Insomnia are built for that, and the setup pays off | — |