# zapreq
> A fast, friendly HTTP client for the terminal — HTTPie reimagined in Rust.
[](https://crates.io/crates/zapreq)
[](https://github.com/MFAIZAN20/zapreq/actions/workflows/ci.yml)
[](#license)
[](https://www.rust-lang.org)
`zapreq` installs an `http` binary. If you know HTTPie, you already know zapreq — same
syntax, faster startup, lower memory, and a handful of features HTTPie never shipped.
---
## Table of contents
- [Why zapreq](#why-zapreq)
- [Install](#install)
- [Quick start](#quick-start)
- [Request items](#request-items)
- [Output control](#output-control)
- [Authentication](#authentication)
- [Sessions](#sessions)
- [Environment profiles](#environment-profiles)
- [Request collections](#request-collections)
- [AI assistant](#ai-assistant)
- [Response diffing](#response-diffing)
- [Download mode](#download-mode)
- [Configuration](#configuration)
- [Plugins](#plugins)
- [Comparison with HTTPie](#comparison-with-httpie)
- [Contributing](#contributing)
- [License](#license)
---
## Why zapreq
- **~3 MB binary, ~5 ms startup.** HTTPie ships ~15 MB and takes ~200 ms. ZapReq is
compiled Rust — no interpreter, no import overhead.
- **HTTPie-compatible syntax.** Every `key=value`, `key:=value`, `key==value` item works
exactly as you expect.
- **Full auth coverage.** Basic, Bearer, and Digest (RFC 7616, MD5 + SHA-256) with an
automatic 401 retry cycle for Digest.
- **Session persistence.** Cookies, headers, and auth are saved to
`~/.config/zapreq/sessions/` and restored on the next request.
- **Environment profiles.** Switch between `dev`, `staging`, and `prod` with one flag.
- **Request collections.** Save a request by name, replay it later — Postman-style,
entirely in the terminal.
- **AI assistant.** Describe a request in plain English; zapreq generates and runs it.
- **Response diffing.** Compare two endpoints side by side, key by key.
- **Zero Python dependency.** One binary, no virtualenv, no pip.
---
## Install
```bash
cargo install zapreq
```
Pre-built binaries for Linux, macOS (x86 + ARM), and Windows are attached to every
[GitHub release](https://github.com/MFAIZAN20/zapreq/releases). Download and place
the binary on your `PATH` if you do not have a Rust toolchain.
**Package managers**
```bash
# Arch Linux (AUR)
yay -S zapreq
# macOS / Linux (Homebrew tap)
brew tap MFAIZAN20/zapreq
brew install zapreq
```
---
## Quick start
```bash
# GET request
http GET https://httpbin.org/get
# POST with JSON body (inferred from data items)
http POST https://httpbin.org/post name=faizan age:=22
# Form-encoded body
http --form POST https://httpbin.org/post field=value
# Custom headers
http GET https://httpbin.org/headers Accept:application/json X-Token:abc123
# Query parameters
http GET https://httpbin.org/get page==2 limit==10
# Basic auth
http --auth user:pass https://httpbin.org/basic-auth/user/pass
# Bearer token
http --auth-type bearer --auth "$TOKEN" https://api.example.com/me
# Named session (saves cookies + auth for next time)
http --session myapi POST https://api.example.com/login username=faizan password=secret
http --session myapi GET https://api.example.com/me
# Download a file with a progress bar
http --download https://example.com/archive.zip
# Compare two API versions
http diff https://api.example.com/v1/user/1 https://api.example.com/v2/user/1
```
---
## Request items
Items are positional arguments after the URL. The operator between key and value
determines what the item does.
| `key:value` | Request header | `Accept:application/json` |
| `key=value` | String body field | `name=faizan` |
| `key:=value` | Raw JSON body field | `active:=true` or `count:=42` |
| `key==value` | Query string parameter | `page==2` |
| `key@/path` | File upload (multipart) | `avatar@/tmp/photo.png` |
| `key@/path;type=mime` | File upload with explicit MIME | `blob@/tmp/data.bin;type=application/octet-stream` |
| `key=@/path` | String field read from file | `payload=@/tmp/body.txt` |
| `key:=@/path` | JSON field read from file | `config:=@/tmp/opts.json` |
Operator precedence (strict, evaluated left to right): `:=` and `:=@` → `==` → `:` → `=`
and `=@` → `@`.
**JSON body** is the default when data items are present. Pass `--form` for
`application/x-www-form-urlencoded` or `--multipart` for `multipart/form-data`.
---
## Output control
```bash
# Print only response headers
http --print=h GET https://httpbin.org/get
# Print request + response headers (no body)
http --print=Hh GET https://httpbin.org/get
# Print everything
http --verbose GET https://httpbin.org/get
# Disable all formatting and colour (good for piping)
# Change syntax theme
http --style=dracula GET https://httpbin.org/json
```
**`--print` flags** — combine freely:
| `H` | Request headers |
| `B` | Request body |
| `h` | Response headers |
| `b` | Response body |
Default is `hb` (response headers + body). `--verbose` is shorthand for `HBhb`.
**`--pretty` modes:** `all` (default when TTY), `colors`, `format`, `none`.
**`--style` themes:** `monokai` (default), `solarized`, `dracula`, `autumn`.
---
## Authentication
```bash
# HTTP Basic
http --auth user:pass https://api.example.com/resource
# Bearer token
http --auth-type bearer --auth "$TOKEN" https://api.example.com/resource
# HTTP Digest (RFC 7616 — MD5 and SHA-256 supported, automatic 401 retry)
http --auth-type digest --auth user:pass https://api.example.com/resource
```
The `--auth-type` flag defaults to `basic`. Credentials passed via `--auth` are masked
(`user:****`) in `--verbose` output.
---
## Sessions
Named sessions persist headers, cookies, and auth credentials between requests. Session
files live in `~/.config/zapreq/sessions/{hostname}/{name}.json`.
```bash
# Log in — session is created and cookies are saved
http --session prod POST https://api.example.com/login username=faizan password=secret
# Subsequent requests reuse the saved session
http --session prod GET https://api.example.com/me
http --session prod GET https://api.example.com/orders
# Load a session but do not update it after the response
http --session-read-only prod GET https://api.example.com/me
```
Session file format:
```json
{
"headers": { "X-Client": "zapreq" },
"auth": { "type": "basic", "username": "faizan", "password": "secret" },
"cookies": [{ "name": "sid", "value": "abc", "domain": "api.example.com", "path": "/" }],
"meta": { "created": "2026-02-01T10:00:00Z", "last_used": "2026-05-01T14:22:00Z" }
}
```
---
## Environment profiles
Profiles let you switch base URL, headers, and variable values with a single flag.
Profile files live in `~/.config/zapreq/envs/{name}.json`.
```json
{
"base_url": "https://api.example.com",
"headers": { "X-API-Version": "2" },
"variables": { "USER_ID": "42", "TOKEN": "prod-secret" }
}
```
```bash
# Use a named profile
http --env-profile prod GET /users/{USER_ID}
# Combine with a collection
http run get-user --env-profile staging
```
Variable tokens `{KEY}` are substituted in the URL and in all request item values before
the request is built.
---
## Request collections
Collections let you save a request by name and replay it later, optionally with a
different environment profile.
```bash
# Save a request
http save login -- POST https://api.example.com/login username=faizan password={PASSWORD}
# List saved requests
http list
# Run a saved request
http run login
# Run with a profile (profile variables fill {PASSWORD})
http run login --env-profile prod
# Delete a saved request
http delete login
```
Collection files live in `~/.config/zapreq/collections/{alias}.json`.
---
## AI assistant
Set an OpenAI-compatible API key:
```bash
export ZAPREQ_AI_KEY=sk-...
```
Describe your request in plain English:
```bash
http ai "POST to https://api.example.com/users with name Faizan and role admin"
```
ZapReq prints the generated command before executing it so you can see exactly what
it built. The assistant uses a structured JSON prompt — it never guesses; it constructs
a concrete request from your description.
---
## Response diffing
Compare two API endpoints key by key. ZapReq flattens both JSON responses into
dot-notation paths and shows what changed.
```bash
http diff https://api.example.com/v1/user/42 https://api.example.com/v2/user/42
```
Output:
```
A: GET https://api.example.com/v1/user/42 → 200 OK
B: GET https://api.example.com/v2/user/42 → 200 OK
user.id 42
user.name "faizan"
- user.role "admin" (only in A)
+ user.role "viewer" (only in B)
~ user.updated_at "2025-01-01" → "2026-05-01"
```
Green lines are additions, red are removals, yellow are value changes.
---
## Download mode
```bash
# Download to current directory (filename from Content-Disposition or URL)
http --download https://example.com/archive.zip
# Save to a specific path
http --download --output ~/Downloads/archive.zip https://example.com/archive.zip
# Resume a partial download
http --download --continue --output archive.zip https://example.com/archive.zip
```
A progress bar is shown during the download:
```
[████████████░░░░░░░░] 4.2 MB / 10.0 MB · 1.2 MB/s · ETA 5s
✔ Downloaded: archive.zip (10.0 MB in 8.3s · avg 1.2 MB/s)
```
---
## Configuration
Config file: `~/.config/zapreq/config.json`
```json
{
"default_options": ["--style=monokai"],
"default_scheme": "https",
"plugins_dir": "~/.config/zapreq/plugins",
"output_theme": "monokai",
"pretty": "all",
"verify": true
}
```
| `default_options` | `string[]` | `[]` | Flags prepended before every invocation |
| `default_scheme` | `string` | `https` | Scheme used when URL has no scheme |
| `plugins_dir` | `string` | `~/.config/zapreq/plugins` | Directory scanned for plugin manifests |
| `output_theme` | `string` | `monokai` | Default syntax theme |
| `pretty` | `string` | `all` | Default pretty mode |
| `verify` | `bool` | `true` | TLS certificate verification |
Precedence order (highest to lowest):
```
Explicit CLI flags
↓
ZAPREQ_DEFAULT_OPTIONS environment variable
↓
config.json default_options
↓
Built-in defaults
```
---
## Plugins
Built-in plugins (`basic`, `bearer`, `digest`) are always available. Third-party plugins
are discovered from `plugins_dir` via `.toml` manifest files.
```bash
# List all registered plugins
http plugins list
# Install instructions for a community plugin
http plugins install zapreq-plugin-aws
```
Manifest format (`~/.config/zapreq/plugins/my-plugin.toml`):
```toml
[plugin]
name = "my-auth"
version = "1.0.0"
description = "Custom HMAC authentication"
auth_types = ["hmac"]
```
See the [plugin authoring guide](https://github.com/MFAIZAN20/zapreq/wiki/plugins) for
how to build and distribute a zapreq plugin.
---
## Comparison with HTTPie
| JSON / form / multipart | ✅ | ✅ |
| Sessions | ✅ | ✅ |
| Basic + Bearer auth | ✅ | ✅ |
| Digest auth (RFC 7616) | ✅ | ✅ |
| Syntax-highlighted output | ✅ | ✅ |
| Environment profiles | ❌ | ✅ |
| Request collections | ❌ | ✅ |
| AI request assistant | ❌ | ✅ |
| Response diffing | ❌ | ✅ |
| Resume downloads | ❌ | ✅ |
| Native binary (no Python) | ❌ | ✅ |
| Binary size | ~15 MB | ~3 MB |
| Startup time | ~200 ms | ~5 ms |
---
## Contributing
```bash
# Clone
git clone https://github.com/MFAIZAN20/zapreq
cd zapreq
# Run tests
cargo test --all
# Lint
cargo clippy --all-targets --all-features -- -D warnings
# Format
cargo fmt --all
# Release build
cargo build --release
```
All pull requests must pass the CI matrix (Ubuntu, macOS, Windows × stable, beta) before
merge. Please open an issue before starting work on a large feature.
---
## License
Licensed under either of the following, at your option:
- [MIT License](LICENSE-MIT)
- [Apache License, Version 2.0](LICENSE-APACHE)
This is the standard dual license used across the Rust ecosystem (Rust itself, Cargo,
tokio, serde, clap, reqwest). You may choose whichever license suits your project.
Copyright © 2026 Muhammad Faizan