Folders & collections
Nest requests in folders like QA / Users. Drag to reorder, duplicate, move, and run a whole folder at once.
pyhttp is a local API client — an alternative to Postman that imports Postman collections and runs Postman scripts — with a built-in collection runner, in its own desktop window. Requests live in a SQLite file, every run writes to plain files, and the backend only listens on 127.0.0.1.
Have pyhttp on a Mac and the in-app update fails? Homebrew 7 must trust the pyhttp tap once: run brew trust pyhttp2/pyhttp in Terminal, then update again. Copy the commands → · Why
Build, organise, run and test requests with the tools you already know.
Nest requests in folders like QA / Users. Drag to reorder, duplicate, move, and run a whole folder at once.
Dev, QA, Staging and Prod, each with its own {{base_url}}. Prod is flagged red so you never hit it by accident.
Mark a key or password secret and it's masked on screen and saved as •••••• in history and run files.
OAuth 2.0 tokens are fetched, cached and refreshed for you. Also Basic, Bearer, API key and Digest, inherited from folders.
Postman collections and environments, OpenAPI 3 / Swagger 2 in JSON or YAML, browser HAR files, or a pasted curl.
Every request you send is kept. Filter by status, send again with one click, or diff two responses, also straight from the response bar after sending several bodies.
Keep valid user, missing email and more on one request. Tick the ones to send, every response shows as its own chip, and tests and scripts can target one body or all of them.
CodeMirror with folding and bracket matching. Ctrl+/ comments a line, Ctrl+B beautifies, and broken JSON is pointed out live.
A collapsible JSON tree. Click a key to copy its path, filter with data.items[*].id, or preview HTML, images and PDF.
{{$guid}}, {{$timestamp}}, {{$randomEmail}} and path params like /users/:id. Unresolved ones are flagged before you send.
Cookies are stored per domain like a browser, sent automatically, and editable in a manager.
Request and response are two panes that scroll on their own, so the URL and the status never leave view. Put the response below or to the right, drag the divider, hide the sidebar with Ctrl+\.
pyhttp checks for new releases at start. The Windows installer, the portable .exe and the Homebrew install download, verify and install the update, then restart.
A per-user Windows installer with no admin rights, a portable .exe, a Homebrew cask or .dmg on macOS, and a .deb for Ubuntu 22.04+ and Debian 12+.
Point a request at the one that fetches its token. The token request runs first, is reused until the expiry you set, and is refreshed with one retry when the API answers 401.
Send fifteen bodies or a whole run at once, bounded by a limit you choose, or keep them in order when one request feeds the next. Pick the mode right on the Send button.
While a run goes, every request it sends shows its responses next to it: one chip per body and iteration, so you compare them without opening the report.
Drive iterations from a CSV or JSON file of variable values, one row per iteration. The run dialog writes a sample with a column for every variable your requests use.
Pick requests and an interval; pyhttp re-sends them while the app is open and flags any error, 4xx/5xx or failed test — in the status bar and, if you set one, a webhook. Sparklines show the trend.
Claude Code, Claude Desktop or Cursor can manage your collection over MCP: create and edit requests, send them, run folders, read history. Setup is one copy-paste from Settings → AI (MCP).
Answer /mock/payments/users/42 from pyhttp itself — no second process, no second port. Routes take :id params, wildcards and a templated body that can read the incoming request.
Server-Sent Events, chunked and AI-style streaming endpoints show their body as it arrives. Cancel stops the stream and keeps what came. One tick in the request's Settings tab.
Deleted a request or body? Undo is right on the toast. Arrows move through the collection, Enter opens, Space ticks for a run, Delete removes — hands on the keyboard.
Give a request a ws:// or wss:// address, written out or as {{events_url}}/orders, and Send becomes Connect. A live log of messages in both directions, a filter, Ping, and a box to send from. The request's headers, auth and {{variables}} are used for the connection.
Every request, folder, environment, mock and monitor as a JSON file in your own Git repository, committed and pushed a few seconds after you change it, and pulled back in when a teammate pushes. Share a collection without a cloud account.
Attach your own HTML page to a request or a whole folder and see the response as a chart, a table or a dashboard, in the View tab or in the browser. A starter view is one click away.
Pin a response to its request. Examples appear in the documentation export, travel with exports and Git sync, and each one can become the answer of a mock route.
Separate workspaces, each with its own requests, environments, history, cookies, mocks and monitors, in its own SQLite file. Switch from the top bar; pyhttp run --project picks one in CI.
One switch moves the values of secret variables out of the project file and into Windows Credential Manager, the macOS Keychain or the Secret Service. The database then holds no secrets.
Export a project or a single folder as Markdown or as one self-contained HTML page: requests, parameters, auth, example bodies, pinned example responses and tests, with credentials masked.
Load the whole body of a multi-megabyte response on demand. A big JSON document is drawn as a window of rows, so 100 000 items scroll, expand, filter by path and search as smoothly as ten.
Sent several bodies, or ran a few iterations? Compare in the response bar diffs any two: body, headers and the request as sent, with JSON keys sorted so only real changes show.
Use it at home or at any company, for any purpose, with no fees, no seats and no registration. The app stays free; paid extras, if ever, are optional add-ons.
Select requests, reorder them and run them with live progress. Each response lands in the run's output folder, so you always have a record of exactly what happened.
{{prev.json.token}} or {{prev.status}}.{{prev}}, or everything at once with a limit you set.A mock server is a name and a list of routes, answered by pyhttp on the port it already runs on. Point your frontend at it and build against endpoints nobody has written yet.
/mock/<name>/… is answered by pyhttp itself. Open /mock to see every server you have./users/:id or /users/{id}, * for one segment, ** for the rest. Match on the query string when two routes overlap.{{req.params.id}}, {{req.query.plan}}, {{req.json.user.name}}, plus {{$guid}} and friends.A response view is an HTML page of your own that pyhttp opens with the response in it. Twenty lines of HTML turn an order list into a revenue dashboard, a metrics endpoint into a chart, a search result into the table your support team asked for.
http://localhost:5173/chart.html. Your own scripts, styles and chart library load next to it.api from its address and fetches it: the body exactly as the API sent it, however large. meta adds status, timing and headers.
On the left the starter view pyhttp writes for you; on the right a dashboard written for this API in sixty lines of HTML. Both draw the same response, and both redraw when you send again.

Pick the file, or let pyhttp create a starter. Views are saved with the request, so they travel with exports and Git sync. Turn on Show the view first and a response opens straight in its page.
Real screenshots of pyhttp, taken against a local test API.

A ws:// address turns Send into Connect and the response into a live console. Messages in both directions with their time and size, click one to pretty-print its JSON, filter the log, and send the next one with Ctrl+Enter.

Point a request at an HTML page of your own and the response opens in it: a chart, a table, a dashboard. It works for every past call in the history too, and a folder can give its views to every request inside.

Pin the responses worth keeping: they document the request, go out with exports and Git sync, and turn into mock routes. Sent two bodies? Compare shows what differs, line by line.

Pick the request that fetches your token and how long it stays valid. It runs first, its extraction sets {{token}}, and it is reused until it expires. A 401 refreshes it and retries once.

Send ×15 goes out at once, at most eight at a time, with the token fetched only once. Every response is a chip with its status and time; click one to read it. The ▾ next to Send switches between in order and in parallel.

Choose how to send, then load a CSV or JSON of variable values, or let pyhttp write a sample from the variables your requests use. Three rows, three iterations.

Add routes with :id parameters, a status code and a templated body, and pyhttp answers them on the port it already runs on. Hits are counted per route, and every call is logged.

Every few minutes, the requests you picked are sent again. Pass and fail at a glance, duration sparklines, a results log per monitor — and a webhook ping when something breaks.

Open any request after a run and its responses are right there: one chip per body and iteration, with the run's name and totals above them, next to the full run report.
pyhttp run runs folders, requests or a collection file without the window, prints each result and exits non-zero when anything fails. It works like newman, but for pyhttp.
--tag smoke, or a Postman, OpenAPI or HAR file.--env QA, an exported environment file, or --var key=value.0 all passed, 1 something failed, 2 usage error.The Scripts tab has a Pre-request and a Post-response section, like Postman's. Build checks on status, headers, body, JSON path and response time from simple rows; results show in the response's Tests tab and in run reports.
{{token}} next.pm API in JavaScript.py object for anything the builder can't express.
Request history sits next to Run history in the right-hand panel. Pick any two entries and pyhttp diffs the status, timing, headers and body. JSON keys are sorted first, so only real changes show.
No account to create, nothing synced to a cloud, no telemetry. The only calls pyhttp makes on its own are the update check against GitHub (switch it off in Settings) and a monitor's webhook, if you set one.
The backend listens only on localhost. Window mode picks a free port and prints it on start. Mock servers answer on that same local port — nothing is exposed to the network.
Requests, folders, environments and history live in one SQLite file. Move it with --data-dir.
A native OS webview (WebView2, WKWebView or WebKitGTK) with native file dialogs. Python 3.10+ is all you need.
Use pyhttp run in CI, or start with --server for a REST API documented at /api/docs.
Every common action has a shortcut. On macOS, use Cmd in place of Ctrl.
Packaged builds bundle Python and every dependency. Just double-click.
Installer: per user, no admin rights, updates itself. Or the portable .exe, one file, no install. WebView2 ships with Windows.
Instead of downloading, paste this in Terminal (Apple Silicon). It installs pyhttp through Homebrew, and from then on updates install themselves from inside the app. The last line clears macOS's quarantine flag, which otherwise shows “Apple could not verify pyhttp” on first launch (the build isn't notarized yet):
brew trust pyhttp2/pyhttp
brew install --cask pyhttp2/pyhttp/pyhttp
xattr -dr com.apple.quarantine /Applications/pyhttp.appAlready have pyhttp and “Update with Homebrew” fails? Paste this instead; your requests and settings are kept. The last line stops the “Apple could not verify” popup after a Terminal upgrade. Why this happens
brew trust pyhttp2/pyhttp
brew update && brew upgrade --cask pyhttp2/pyhttp/pyhttp
xattr -dr com.apple.quarantine /Applications/pyhttp.appNo Homebrew? Install it first, or use the .dmg: drag pyhttp to Applications (a .dmg install does not update itself).
Ubuntu 22.04+ / Debian 12+ .deb: sudo apt install ./pyhttp-*.deb. Uses your distro's GTK and WebKitGTK. Other distros run from source.
Prefer pip or Homebrew? pip install pyhttp-app (or pipx install pyhttp-app) installs the same app from PyPI on any OS with Python 3.10+, and brew install pyhttp2/pyhttp/pyhttp does it through Homebrew on macOS (Intel and Apple Silicon) and Linux — or tap once with brew tap pyhttp2/pyhttp and it's just brew install pyhttp from then on. The command is pyhttp either way.
The builds aren't code-signed yet, so the first launch shows a warning you can allow once (see the FAQ). pyhttp checks for new versions at start; on Windows and with Homebrew it installs them itself. Previous versions and checksums (SHA256SUMS.txt) are on the releases page.
Yes, for everyone. pyhttp is free under the pyhttp Free License: use it at home or at work, at a company of any size, for any purpose, with no fees, no seats and no registration. You can share the unmodified installers inside your company or through package managers. The source code is published on PyPI as pyhttp-app, so it can be inspected and installed with pip; modified versions may not be distributed. The app stays free; if paid extras ever appear they will be optional add-ons. pyhttp is provided “as is”, with no warranty of any kind and no liability for any damage or loss from its use; see the license for the full terms.
No. There's no sign-up, no sync and no telemetry. Your collection is a SQLite file on your disk, and the only network traffic is the requests you send. The app checks GitHub for a newer release, and that's all.
Yes. Import Postman v2.1 collections and environments, including folders, auth, variables and scripts. pm.* scripts run unchanged. You can also export back to Postman format.
Yes, OpenAPI 3 and Swagger 2 in JSON or YAML. Each tag becomes a folder, the server URL becomes {{baseUrl}}, and request bodies are filled from the spec's examples and schemas. HAR files from browser devtools work too.
No. Save the request that fetches the token and let it store the value with an extraction or pm.environment.set. In any other request's Settings tab, pick it under Depends on another request and set how long a token stays valid. pyhttp sends it first, reuses the token until then, fetches it only once even for parallel sends, and refreshes it with one retry when the API answers 401 or 403.
Yes. pyhttp mcp serves an MCP server on stdio, so Claude Code, Claude Desktop or Cursor can list, create and edit requests, send them, run a folder or a tag, read history, switch environments and set variables. Settings → AI (MCP) shows the exact command and JSON for your machine, already filled in. It uses the same data folder as the app, so both can be open at once.
Yes. Type a ws:// or wss:// address into a request (or a variable that holds one, such as {{events_url}}/orders) and Send becomes Connect. You get a live log of everything sent and received, Ping, a filter, and a box to send messages from; the request's query parameters, headers, auth and {{variables}} are used for the connection, and a Sec-WebSocket-Protocol header names the subprotocols to offer. Compression (permessage-deflate) and proxies are not supported yet.
Yes, through Git. Git sync (top bar → Git) writes the project's requests, folders, environments, mocks and monitors as JSON files into a repository of yours, commits and pushes a few seconds after every change, and reads your teammates' pushes back in, also on a timer while the app is open. Values of secret variables stay out of the files unless you ask for them. When two people change the same request, pyhttp asks which version to keep.
Yes — add a mock server and point your frontend at http://127.0.0.1:<port>/mock/<name>/…. It is answered by pyhttp itself, so there is no second process or port. Routes take :id parameters and wildcards, the body is a template that can read the incoming request, and you can set the status, headers and a delay per route. You can also seed routes from saved requests so they reply with the response they last gave.
Run python -m pyhttp run with a folder name or an exported collection file. Add --junit out/junit.xml for your CI's test report. The exit code is 1 when any request or test fails.
pyhttp looks up the latest release at start (and on Check now in Settings) and shows an Update button when a newer version exists. The Windows installer, the portable .exe and a Homebrew install download the new version, verify its checksum and install it, then pyhttp restarts. A .dmg or .deb install gets the download and the exact command to run. Your requests and settings are never touched. From 2.0.8 on, pyhttp trusts its own Homebrew tap before upgrading (Homebrew 7 asks this once per third-party tap) and points taps made before the move at the current repository. On 2.0.7 and older the update can fail with an “untrusted tap” error; see the next question.
The .deb installs on Ubuntu 22.04 and later and Debian 12 and later, and uses the distribution's GTK and WebKitGTK packages (they are pulled in as dependencies). Other distributions can run pyhttp from source with Python 3.10+ and the GTK webview bindings.
Homebrew 7 loads nothing from a third-party tap until you have trusted it once. Macs that installed pyhttp before the project moved to github.com/pyhttp2/pyhttp trusted the tap under its old address; when pyhttp points the tap at its new home, that trust no longer applies and brew upgrade stops with Refusing to load cask pyhttp2/pyhttp/pyhttp from untrusted tap. pyhttp 2.0.7 and older report this only as “brew upgrade --cask failed (exit code 1)”. Trust the tap once in a terminal and update again, from the app or with the second line:
brew trust pyhttp2/pyhttp
brew update && brew upgrade --cask pyhttp2/pyhttp/pyhttp
xattr -dr com.apple.quarantine /Applications/pyhttp.appThe last line matters too: an upgrade run from Terminal leaves macOS's quarantine flag on the new app, so the next launch shows “Apple could not verify pyhttp is free of malware” (the build isn't notarized yet). xattr -dr com.apple.quarantine /Applications/pyhttp.app clears it; the in-app updater does this by itself. Your requests, environments and history are kept: they live in ~/Library/Application Support/pyhttp, which no update touches. Not installed with Homebrew? Download the .dmg again and drag pyhttp over the old app. From 2.0.8 on, pyhttp does the trust step itself and shows Homebrew's own error line with the fix when something else goes wrong.
The builds aren't code-signed yet, so the system warns on first launch. On Windows (installer or portable), click More info → Run anyway. On macOS the popup says “Apple could not verify pyhttp is free of malware”: click Done, open System Settings → Privacy & Security, scroll down and click Open Anyway next to pyhttp (right-click → Open no longer works on macOS 15). Or clear the quarantine flag once in Terminal, which also fixes “the app is damaged”:
xattr -dr com.apple.quarantine /Applications/pyhttp.appThe in-app updater clears the flag itself after a Homebrew update; an upgrade run from Terminal does not, hence the third line in the command boxes above.
The .dmg is built for Apple Silicon. On an Intel Mac, run pyhttp from source with Python 3.10+.
In %APPDATA%\pyhttp on Windows, ~/Library/Application Support/pyhttp on macOS and ~/.local/share/pyhttp on Linux. Use --data-dir to keep it somewhere else, such as a synced folder. Updates and uninstalls leave it alone.
Free for everyone, at home or at any company. No sign-up, ever.