A terminal HTTP client. Send requests, manage collections, inspect responses - keyboard-driven, single binary, no Electron.
# macOS (Intel + Apple Silicon)
brew install d0mkaaa/tap/gopull
# if macOS blocks the binary: xattr -d com.apple.quarantine "$(which gopull)"
# Windows
scoop bucket add d0mkaaa https://github.com/d0mkaaa/scoop-gopull
scoop install gopull
# Go toolchain
go install github.com/d0mkaaa/gopull@latestOr grab a binary from releases - Linux, macOS, Windows all covered.
Build from source:
git clone https://github.com/d0mkaaa/gopull
cd gopull
go build -o gopull .- Collections - save and organise requests, run them in sequence with pass/fail reporting
- Environments - create, edit, select, and delete named variable sets with
{{VAR}}substitution - History - browse the last 500 requests, replay them, save them, or diff responses
- Streaming - SSE responses arrive live line by line
- Themes - dark, light, nord, gruvbox - or create your own in JSON
- Curl import - paste a curl command into the URL field and press tab
- Import/export - Postman collections,
.httpfiles (VS Code REST Client), OpenAPI v2/v3 - CLI runner - run local collections in scripts or CI with text, JSON, or JUnit reports
- Git-friendly files - export collections as plain
.gopullrequest files - Search -
/to search the response body,n/Nto jump between matches - JSON tree -
tto collapse/expand JSON responses interactively - Assertions - write tests that run after every response; extract values into env vars with
set TOKEN = $.data.token - Multipart and files - send multipart forms or a file as the request body
- Cookies and certs - opt-in cookie jar, CA bundle, and client certificate paths
- Proxy - per-request proxy URL or
HTTP_PROXY/HTTPS_PROXYenv vars - Custom methods - type any method (PROPFIND, REPORT, etc.) in the method field
gopull
gopull ./api-requestsRun a collection from the shell:
gopull init ./my-api --env
gopull lint ./my-api
gopull run ./my-api --env dev --report json
gopull run collection.json --bail
gopull run ./my-api --tags smoke --exclude-tags slow --report junit
gopull run ./my-api --offlineUseful CLI flags:
--env dev use a saved environment by name or id
--env-file .env merge variables from dotenv or JSON
--env-var TOKEN=value override a variable, repeatable
--tags smoke include only matching request tags
--exclude-tags slow skip matching request tags
--iteration-count 3 run selected requests multiple times
--report text|json|junit choose output format
--offline print built requests without sending
Three panels: sidebar (collections), editor (request builder), response. tab / shift+tab moves focus.
[ and ] cycle tabs: body, params, headers, auth, tests, opts.
- Method:
space/up/downcycle standard methods; press any letter to type a custom one - Params: query params append to the URL; path params fill
:idplaceholders - Headers:
Key: Value, one per line. Prefix with#to disable a header - Body: raw by default,
alt+mtoggles raw / form / GraphQL / multipart / file mode - Auth: bearer token or basic auth under the auth tab
- Completion:
alt+/completes{{env}}variables, header names, content types, and missing path params from the URL ctrl+rsends
Multipart body lines use:
name=value
file avatar=/path/to/avatar.png
File body mode reads the body from the path in the body field.
ctrl+s saves the current request. Auto-names from method + URL path if no name is set.
From the sidebar: up / down navigate, enter open, r run all requests in sequence, n rename, ctrl+d duplicate a request, ctrl+j / ctrl+k move a request, d twice to delete.
ctrl+e opens the environment manager. enter selects, n creates, e edits, and d deletes.
Variables use a compact line format:
BASE_URL=https://api.example.com
secret TOKEN=value
# DISABLED=value
secret values are masked in the picker. If an environment has a dotenv path, file variables load first and inline variables override them.
alt+h opens the history browser. Filter by typing, enter loads a request into the editor, ctrl+r replays it, s saves it to the active collection, and D diffs its response body against another history entry.
Responses open on a preview tab with status, timing, size, header count, cookies, and a content-aware body summary. JSON responses show top-level keys and shapes, HTML responses show title/headings/text, XML responses show root/children, and plain text shows the first readable lines.
[ / ] switches response tabs: preview, body, headers, and tests when assertions ran.
j/k scroll, / search, t JSON tree view from the body tab, y copy body, w save to file, D diff against history.
In tree view: space collapse/expand, c collapse all, e expand all, {/} jump between siblings.
Write assertions in the Tests tab:
assert status == 200
assert header Content-Type contains json
assert body contains "token"
assert jsonpath $.data.id > 0
assert response_time < 500
set TOKEN = $.data.access_token
set extracts a JSONPath value and stores it in the active environment for use in the next request.
ctrl+i accepts:
- Postman collection JSON
.http/.restfiles (VS Code REST Client format)- OpenAPI v2 (Swagger) or v3 JSON files
- OpenAPI spec URLs (
https://...) - gopull plain-text collection directories
Plain-text collections look like:
my-api/
gopull.collection
list-users.gopull
create-user.gopull
Each .gopull request file uses simple sections like [query], [path], [headers], [body raw], and [tests]. Add tags: smoke, auth near the top of a request file to make CLI filters work.
Open a plain collection directly with gopull ./my-api. In workspace mode, saves write back to the .gopull files instead of copying the collection into the config store.
Scaffold a new plain collection:
gopull init ./my-api --envValidate a collection before committing:
gopull lint ./my-api
gopull lint ./my-api --format jsonalt+p - fuzzy search over all actions and saved requests.
| Key | Action |
|---|---|
ctrl+r |
Send request |
ctrl+s |
Save request |
alt+n |
New request |
alt+p |
Command palette |
alt+b |
Toggle sidebar |
alt+o |
Settings |
ctrl+e |
Environment manager |
alt+h |
History browser |
alt+l |
Plugin manager |
ctrl+i |
Import collection |
ctrl+x |
Export collection (Postman JSON) |
alt+j |
Format JSON body |
alt+m |
Toggle body mode |
alt+/ |
Complete current editor field |
alt+c |
Copy request as curl |
alt+e |
Open body in external editor |
[ / ] |
Switch editor tabs |
tab / shift+tab |
Move focus between panels |
/ |
Search response body |
t |
Toggle JSON tree view |
y |
Copy response to clipboard |
w |
Save response to file |
D |
Diff response against history |
alt+q |
Quit |
Custom bindings go in ~/.config/gopull/keybindings.json.
~/.config/gopull/
collections/ one JSON file per collection
environments/ named variable sets
history.json last 500 requests
config.json theme, timeout, max display bytes
keybindings.json key overrides
themes/ custom theme files
plugins/ experimental local hooks
disabled.json local plugin enable/disable state
config.json options:
{
"timeout_secs": 30,
"theme": "dark",
"max_display_bytes": 5242880
}Built-in: dark (catppuccin mocha), light (catppuccin latte), nord, gruvbox. Settings (alt+o) lets you switch themes, change the default timeout, set the max response body display size, and confirm whether you are using the config store or a plain .gopull workspace.
Drop a JSON file in ~/.config/gopull/themes/ to add your own - a starter template is written there on first run.
Executables in ~/.config/gopull/plugins/ can register local pre_request and post_response hooks. Hooks are local executables that speak JSON over stdin/stdout; there is no marketplace, remote install, account, or sync layer.
Plugins return a manifest from --manifest:
{
"name": "aws-sigv4",
"version": "0.1.0",
"api_version": "v1",
"hooks": ["pre_request"],
"permissions": ["read_env", "read_body", "read_secrets", "write_headers"]
}pre_request hooks receive request JSON and may return changed fields. post_response hooks receive the request plus a response snapshot and may return env updates.
v1 permissions:
read_env- receive non-secret environment variablesread_body- receive the request bodyread_secrets- receive secret environment variables and auth secretswrite_request- change method or URLwrite_headers- replace request headerswrite_body- replace the request bodywrite_env- write environment variables frompost_response
Manifests without api_version run in legacy mode for compatibility. New plugins should use api_version: "v1" and declare the narrowest permissions they need.
Open the plugin manager with alt+l to inspect local plugins, review hooks and permissions, reload manifests, or enable/disable a plugin.
MIT
