v0.1.11 New: dependent requests with auto token refresh, parallel sends, scripts per body, run responses beside the request →

The API client that stays on your machine.

pyhttp is a Postman-like HTTP client 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.

Free for everyone, including at work Windows · macOS · Linux

The pyhttp desktop app: a Users API folder in the sidebar, a POST request with the named bodies valid user and missing email sent in parallel, a chip per response, the Get token dependency sent first, and a completed run on the right with 8 of 8 tests passed.
—
Downloads so far
0
Accounts, syncs or telemetry
127.0.0.1
The only address it binds to
pm.*
Postman scripts run unchanged
Imports what you already have
  • Postman v2.1
  • OpenAPI 3
  • Swagger 2
  • HAR
  • cURL
  • CSV & JSON data
Features

Everything you use in Postman. Nothing phoning home.

Build, organise, run and test requests with the tools you already know.

Folders & collections

Nest requests in folders like QA / Users. Drag to reorder, duplicate, move, and run a whole folder at once.

Environments & variables

Dev, QA, Staging and Prod, each with its own {{base_url}}. Prod is flagged red so you never hit it by accident.

Secret variables

Mark a key or password secret and it's masked on screen and saved as •••••• in history and run files.

OAuth 2.0 & every auth type

OAuth 2.0 tokens are fetched, cached and refreshed for you. Also Basic, Bearer, API key and Digest, inherited from folders.

Import & export

Postman collections and environments, OpenAPI 3 / Swagger 2 in JSON or YAML, browser HAR files, or a pasted curl.

History & compare

Every request you send is kept. Filter by status, send again with one click, or diff two responses side by side.

Named 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.

Smart body editor

CodeMirror with folding and bracket matching. Ctrl+/ comments a line, Ctrl+B beautifies, and broken JSON is pointed out live.

Response viewer

A collapsible JSON tree. Click a key to copy its path, filter with data.items[*].id, or preview HTML, images and PDF.

Dynamic variables

{{$guid}}, {{$timestamp}}, {{$randomEmail}} and path params like /users/:id. Unresolved ones are flagged before you send.

Cookie jar

Cookies are stored per domain like a browser, sent automatically, and editable in a manager.

Split panes, your layout

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+\.

Updates itself

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.

Windows, macOS, Ubuntu

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+.

New

Dependent requests

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.

New

Parallel sends

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.

New

Run responses beside the request

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.

New

Variables file, with a sample

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.

Free for everyone

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.

Collection runner

Run a whole suite. Keep every response.

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.

  • ChainingRead the last response with {{prev.json.token}} or {{prev.status}}.
  • In order or in parallelOne after another with {{prev}}, or everything at once with a limit you set.
  • Variables fileCSV or JSON, one iteration per row; download a sample made from your own variables.
  • Responses beside the requestEach request in the run shows a chip per body and iteration while it runs.
  • ControlsIterations, delays, stop-on-failure, cancel mid-run, or skip the dialog with ⚡ Run now.
  • Run historyHTML or JSON reports, and every past run one click away.
outputs/2026-10-07_18-30-00_smoke/
summary.json run status
01_login.json request + timing
01_login.body.json raw body
02_create-user__valid-user.json
02_create-user__valid-user.body.json
03_list-users.json
 
✓ POST login 200 · 132 ms
✓ POST create user 201 · 88 ms
✓ GET list users 200 · 141 ms
3 passed0 failed361 ms
See it in action

What's new, in the app itself.

Real screenshots of pyhttp 0.1.10, taken against a local test API.

The Settings tab of Create user: Depends on another request set to Users API › Get token, send it again after 30 minutes, retry on 401 or 403 ticked, and the status line Last sent 2 s ago (200), runs again in 30 min.

Dependent requests: tokens on autopilot

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.

A request with fifteen named bodies sent in parallel: the Send mode menu open with Send in parallel ticked, fifteen response chips all 200, and the badge Get token: reused.

Fifteen bodies, sent in parallel

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.

The Run requests dialog: How to send set to In parallel, at most 8 at once, three requests in order, and the variables file sample-variables.csv with three rows and the variables user_name and page.

Runs: in order or in parallel, driven by a variables file

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.

After a run named smoke: the Create user request shows chips for valid user and missing email in iterations 1 to 3, and the Current run panel lists twelve sends, all 200, with 24 of 24 tests passed.

Every run response, beside its request

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.

Command line & CI

The same collection, headless in your pipeline.

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.

  • Pick what to runFolders by path, requests by name, --tag smoke, or a Postman, OpenAPI or HAR file.
  • Environments--env QA, an exported environment file, or --var key=value.
  • ReportsJUnit XML for your CI, plus HTML and JSON reports.
  • Exit codes0 all passed, 1 something failed, 2 usage error.
terminal.github/workflows/api.yml
$ python -m pyhttp run "API/Users" --env QA \
--junit out/junit.xml --html out/report.html
 
pyhttp 0.1.7 · API/Users · 4 request(s) · environment QA
✓ POST login 200 · 96 ms
✓ GET list users 200 · 121 ms
✓ POST create user 201 · 84 ms
✗ DELETE delete user 403 · 41 ms
✗ status is 204 — expected 204, got 403
3/4 requests passed, 5/6 tests passed · 0.34s
Report: out/junit.xml
Report: out/report.html
$ echo $?
1
JUnitHTMLJSONexit 1
Scripts tab

Assertions without code. Scripts when you need them.

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.

  • No-code testsStatus, header, body, JSON path and timing assertions.
  • Per bodyEach test, and each pre-request or test script, runs for every named body or for one you pick: “missing email” can expect a 400 while “valid user” expects a 201.
  • Tokens that expireMake a request depend on your token request: it runs first, is reused until it expires, and is refreshed when the API says 401.
  • Extract & chainPull a value into a variable, then use {{token}} next.
  • Postman scripts, unchangedThe full pm API in JavaScript.
  • Python when you want itA py object for anything the builder can't express.
The Scripts tab: four no-code tests, two of them scoped to the bodies valid user and missing email, the extract-variables row and a JavaScript test script using the pm API.
History & compare

What changed since the last call? See it line by line.

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.

  • Filter fastSearch by text, or show only 2xx, 4xx, 5xx or failed requests.
  • Send againReplay any past request with today's variables in one click.
  • Open as newTurn a history entry back into an editable request.
CompareGET /users/42 · 10:02 → 10:14
status 200 → 200 time 88 ms → 131 ms
 
{
"email": "ada@example.com",
"id": 42,
- "plan": "free",
+ "plan": "pro",
+ "seats": 5,
"status": "active"
}
+2−1body · headers · request
Local-first

The only place your traffic goes is your API.

No account to create, nothing synced to a cloud, no telemetry.

Bound to 127.0.0.1

The backend listens only on localhost. Window mode picks a free port and prints it on start.

Plain, portable data

Requests, folders, environments and history live in one SQLite file. Move it with --data-dir.

No Electron, no build step

A native OS webview (WebView2, WKWebView or WebKitGTK) with native file dialogs. Python 3.10+ is all you need.

Drive it from scripts

Use pyhttp run in CI, or start with --server for a REST API documented at /api/docs.

Built for the keyboard

Fast by keyboard. Flexible by setting.

Every common action has a shortcut. On macOS, use Cmd in place of Ctrl.

  • AutosaveEvery edit is saved half a second after you stop typing.
  • Code snippetscURL, Python requests and httpx, fetch, axios, PowerShell, raw HTTP and Go, with variables and auth resolved.
  • Body typesJSON, text, XML, HTML, JavaScript, form, form-data with files, binary and GraphQL.
  • SettingsRedirects, proxy with bypass list, per-host client certificates, timeouts, User-Agent, response below or to the right, and update checks.
  • Send request Ctrl↵
  • Save now CtrlS
  • New request CtrlN
  • Duplicate request CtrlD
  • Search the collection CtrlK
  • Show or hide the sidebar Ctrl\
  • Beautify the body CtrlB
  • Search the response CtrlF
  • Code snippet Ctrl⇧C
  • Shortcut help ?
Download

Get pyhttp

Packaged builds bundle Python and every dependency. Just double-click.

Latest versionv0.1.11
Released—
Total downloads—

Windows 10 / 11 Your system

Installer: per user, no admin rights, updates itself. Or the portable .exe, one file, no install. WebView2 ships with Windows.

Download installer

macOS 11+ Your system

Apple Silicon .dmg. Drag pyhttp to Applications, or brew tap pyhttp2/pyhttp https://github.com/pyhttp2/pyhttp-releases && brew install --cask pyhttp2/pyhttp/pyhttp (then updates install themselves).

Download .dmg

Linux Your system

Ubuntu 22.04+ / Debian 12+ .deb: sudo apt install ./pyhttp-*.deb. Uses your distro's GTK and WebKitGTK. Other distros run from source.

Download .deb

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.

FAQ

Questions, answered.

Something missing? Open an issue on GitHub.

Is pyhttp free? Can I use it at work?

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 not published. 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.

Do I need an account, or does it send my data anywhere?

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.

Can I bring my Postman collections?

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.

Can I import an OpenAPI or Swagger spec?

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.

My API needs a token that expires. Do I fetch it by hand?

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.

How do I run my tests in CI?

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.

How do updates work?

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.

Which Linux distributions are supported?

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.

Windows or macOS warns me when I open it. Is that safe?

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, right-click pyhttp in Applications and choose Open, or allow it in System Settings → Privacy & Security. If macOS says the app is damaged, run xattr -dr com.apple.quarantine /Applications/pyhttp.app.

Does the Mac build run on Intel Macs?

The .dmg is built for Apple Silicon. On an Intel Mac, run pyhttp from source with Python 3.10+.

Where is my data stored?

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.

Send your first request.

Free for everyone, at home or at any company. No sign-up, ever.