Ain is a terminal HTTP API client. It's an alternative to postman, paw or insomnia.



Ain is a terminal HTTP API client. It's an alternative to postman, paw or insomnia.

Show and tell

  • Flexible organization of API:s using files and folders.
  • Use shell-scripts and executables for common tasks.
  • Put things that change in environment variables or .env-files.
  • Share the resulting curl or httpie command.
  • Pipe the API output for further processing.
  • Tries hard to be helpful when there are errors.

Ain was built to enable scripting of input and further processing of output via pipes. It targets users who work with many API:s using a simple file format. It uses curl or httpie to make the actual calls.


You need curl or httpie installed and available on your $PATH. The easiest way to test this is to open up a shell and type curl or http. If there's any output you're good to go.

Go (version 1.13 or higher) if you want to use go get or build it yourself.


If you have go installed

Using go get:

go install[email protected]

Via homebrew

Using the package-manager homebrew

brew tap jonaslu/tools
brew install ain

Via scoop

Using the windows package-manager scoop

scoop bucket add jonaslu_tools
scoop install ain

Download binaries yourself

Install it so it's available on your $PATH:

Quick start

Ain comes with a built in basic template that you can use as a starting point.


ain -e basic_template.ain

The basic template calls GET on localhost using curl. Run the generated template by specifying a PORT environment variable:

PORT=8080 ain basic_template.ain

Longer start

Ain uses sections in square brackets to specify what API to call.

Start by putting things common to a service in a file (let's call it base.ain):

$> cat base.ain

Content-Type: application/json



Then add another file for a specific URL:

$> cat create-blog-post.ain


  "title": "Million dollar idea",
  "text": "A dating service. With music."

Run ain to combine them into a single API call and print the result:

$> ain base.ain create-blog-post.ain
  "status": "ok"

See all options: ain -h

Important concepts

  • Templates: Files containing what, how and where to make the API call. By convention has the file-ending .ain.
  • Sections: Headings in a template file.
  • Environment variables: Enables variables in a template file.
  • Executables: Enables using the results of another command in a template file.
  • Backends: The thing that makes the API call (curl or httpie).
  • Fatals: Error in parsing the template files (it's your fault).


Ain reads sections from template-files. Here's a full example:


Authorization: Bearer $(./
Content-Type: application/json


  "title": "Reaping death",
  "content": "There is a place beyond the dreamworlds past the womb of night."



-sS # Comments are ignored.
# This too.

The template files can be named anything but some unique ending-convention such as .ain is recommended so you can find them easily.

Ain understands seven [Sections] (the things in square brackets). Each of the sections are described in details below.

Sections either combine or overwrite across all the template files given to ain.

Anything after a pound sign (#) is a comment and will be ignored.

Running ain

ain [options] [!]

Ain accepts one or more template-files as a mandatory parameter. As sections combine or overwrite where it makes sense you can better organize API-calls into hierarchical structures with increasing specificity. An example would be setting the [Headers], [Backend] and [BackendOptions] in a base template file and then specifying the specific [Host], [Method] and [Body] in several template files, one for each API-endpoint. You can even use an alias for things you will always set.

Adding an exclamation-mark (!) at the end of the template file name makes ain open the file in your $EDITOR (or vim if not set) so you can edit the file. The edit is not stored back into the template file and used only this invocation.

Note that the $EDITOR cannot fork (as vscode does) because ain waits for the $EDITOR command to finish. Any terminal editor such as vim, emacs, nano etc will be fine.

If ain is connected to a pipe it will try to read template file names off that pipe. This enables you to use find and a selector such as fzf to keep track of the template-files: find . -name *.ain | fzf -m | ain

Template file names specified on the command line are read before any names from a pipe. This means that echo create-blog-post.ain | ain base.ain is the same as ain base.ain create-blog-post.ain.

Supported sections

Sections are case-insensitive and whitespace ignored but by convention uses CamelCase and are left indented. A section cannot be defined twice in a file. A section ends where the next begins or the file ends.

In the unlikely event that the contents of a section must contain the exact same text as a valid section (one of the seven below) on one line you can escape that text with a \ and it will be passed as text.

E g:

All of this will be passed as the body!
Including the text above.


Contains the URL to the API. This section appends the lines from one template file to the next. This neat little feature allows you to specify a base-url in one file (e g base.ain) as such: http://localhost:3000 and in the next template file specify the endpoint (e g login.ain): /api/auth/login

You could have query parameters in yet another template file (e g user-leviathan-login.ain):


Ain performs no validation on the url (as backends differ on what a valid url looks like). If your call does not go through use ain -p as mentioned in troubleshooting and input that directly into the backend to see what it thinks it means.

The [Host] section is mandatory and appends across template files.


Headers to include in the API call.

The [Headers] section appends across template files so you can share common headers (e g Authorization: and Content-Type: application/json)


What http-method to use in the API call (e g GET, POST, PATCH). If omitted the backend default is used (GET in both curl and httpie).

The [Method] section is overridden by latter template files.


If the API call needs a body (POST, PATCH) the content of this section is passed as a file to the backend because formatting may be important (e g yaml). In the template file this section can be pretty-printed for easier eye-balling (e g json).

The file passed to the backend is removed after the API call unless you pass the -l flag. Ain places the file in the $TMPFILE directory (usually /tmp on your box). You can override this in your shell by explicitly setting $TMPFILE if you'd like them elsewhere.

The [Body] sections is overridden by latter template files.


This section contains config for ain (any backend-specific config is passed via the [BackendOptions] section).

Currently the only option supported is Timeout=

The timeout is enforced during the whole execution of ain (both running executables and the actual API call). If omitted defaults to no timeout. Note that this is the only section where executables have no effect, since the timeout needs to be known before the executables are invoked.

The [Cnnfig] sections is overridden by latter template files.


The [Backend] specifies what command should be used to run the actual API call.

Valid options are curl or httpie.

The [Backend] section is mandatory and is overridden by latter template files.


Any options meant for the backends. These are appended straight to the backend-command invocation (curl or httpie).

The [BackendOptions] section appends across template files.

Environment variables

Anything inside ${} in a template is replaced with the value found in the environment.

Ain also reads any .env files in the folder from where it's run. You can pass a custom .env file via the -e flag.

This enables you to specify things that vary across API calls either permanently in the .env file or one-shot via the command-line. Example: PORT=5000 ain base.ain create-blog-post.ain

Environment-variables are expanded first and can be used with any executable. Example $(cat ${ENV}/token.json).

Ain uses envparse for, well, parsing environment variables, so anything it can do, so can you!


Anything inside a $() is replaced with the result from running that command and capturing it's output (STDIN). The command can return multiple rows which will be inserted as separate rows in the template (e g returning two headers).

An example is getting JWT tokens into a separate script and share that across templates.

More complex scripting can be done in-line with the xargs bash -c (hack)[]. Example:

Authorization: Bearer $(bash -c "./ | jq -r '.token'")

Ain expects the first word in an executable to be on your $PATH and the rest to be arguments (hence the need for quotes to bash -c as this is passed as one argument).

Executables are captured and replaced in the template after any environment-variables so if the script returns an environment-variable name it won't be expanded into any value.


Ain has two types of errors: fatals and errors. Errors are things internal to ain (it's not your fault) such as not finding the backend-binary.

Fatals are errors in the template (it's your fault). Ain will try to parse as much of the templates as possible aggregating fatals before reporting back to you. Fatals include the template file name where the fatal occurred, line-number and a small context of the template:

$ ain templates/example.ain
Fatal error in file: templates/example.ain
Cannot find value for variable PORT on line 2:
1   [Host]
2 > http://localhost:${PORT}

Sharing is caring

Ain can print out the command instead of running it via the -p flag. This enables you to inspect how the curl or httpie API call would look like or share the command:

ain -p base.ain create-blog-post.ain >

Piping it into bash is equivalent to running the command without -p.

ain -p base.ain create-blog-post.ain | bash

A note on line-endings. Ain uses line-feed (\n) when printing it's output. If you're on windows and storing ain:s result to a file, this may cause trouble. Instead of trying to guess what line ending we're on (WSL, docker, cygwin etc makes this a wild goose chase), you'll have to manually convert them if the receiving progrogram complains.

Instructions here:


If the templates are valid but the backend-call fails, ain -p can show you what the command ain runs looks like.

Ain in a bigger context

But wait! There's more!

With ain being terminal friendly there are few neat tricks in the wiki

You might also like...
An enhanced HTTP client for Go
An enhanced HTTP client for Go

Heimdall Description Installation Usage Making a simple GET request Creating a hystrix-like circuit breaker Creating a hystrix-like circuit breaker wi

Enriches the standard go http client with retry functionality.

httpRetry Enriches the standard go http client with retry functionality using a wrapper around the Roundtripper interface. The advantage of this libra

http client for golang
http client for golang

Request HTTP client for golang, Inspired by Javascript-axios Python-request. If you have experience about axios or requests, you will love it. No 3rd

Simple HTTP and REST client library for Go

Resty Simple HTTP and REST client library for Go (inspired by Ruby rest-client) Features section describes in detail about Resty capabilities Resty Co

A nicer interface for golang stdlib HTTP client

rq A nicer interface for golang stdlib HTTP client Documents rq: here client: here jar: here Why? Because golang HTTP client is a pain in the a... Fea

a Go HTTP client with timeouts

go-httpclient requires Go 1.1+ as of v0.4.0 the API has been completely re-written for Go 1.1 (for a Go 1.0.x compatible release see 1adef50) Provides

GoRequest -- Simplified HTTP client ( inspired by nodejs SuperAgent )
GoRequest -- Simplified HTTP client ( inspired by nodejs SuperAgent )

GoRequest GoRequest -- Simplified HTTP client ( inspired by famous SuperAgent lib in Node.js ) "Shooting Requests like a Machine Gun" - Gopher Sending

gout to become the Swiss Army Knife of the http client @^^@--->  gout 是http client领域的瑞士军刀,小巧,强大,犀利。具体用法可看文档,如使用迷惑或者API用得不爽都可提issues
gout to become the Swiss Army Knife of the http client @^^@--- gout 是http client领域的瑞士军刀,小巧,强大,犀利。具体用法可看文档,如使用迷惑或者API用得不爽都可提issues

gout gout 是go写的http 客户端,为提高工作效率而开发 构架 feature 支持设置 GET/PUT/DELETE/PATH/HEAD/OPTIONS 支持设置请求 http header(可传 struct,map,array,slice 等类型) 支持设置 URL query(可

Retry, Race, All, Some, etc strategies for http.Client calls

reqstrategy Package reqstrategy provides functions for coordinating http.Client calls. It wraps typical call strategies like making simultaneous reque

  • Multiple endpoint in same ain file

    Multiple endpoint in same ain file

    Hi. hope you are fine. :)

    Is there a way to run the same query on multiple endpoint for example:

    curl -XDELETE http://elastisearch.local/index_1 curl -XDELETE http://elastisearch.local/index_2 curl -XDELETE http://elastisearch.local/index_3

    btw. thank you for this awesome tool.

    opened by mikhi-nde 4
  • Response processing?

    Response processing?

    I gave this a try for some elasticsearch querying work but couldn't find anything that'd let me do response processing.

    For simple stuff I can pipe to jq just fine, but it might be nice to be able to encode something like jq into the ain file to get just that portion of the response, or indented JSON response output.

    If it's there and I missed it let me know, but seems like for now pipe is the only option.

    opened by matschaffer 2
Jonas Lundberg
It itches, therefore I scratch
Jonas Lundberg
fhttp is a fork of net/http that provides an array of features pertaining to the fingerprint of the golang http client.

fhttp The f stands for flex. fhttp is a fork of net/http that provides an array of features pertaining to the fingerprint of the golang http client. T

Flexagon 61 Jan 1, 2023
A Go HTTP client library for creating and sending API requests

Sling Sling is a Go HTTP client library for creating and sending API requests. Slings store HTTP Request properties to simplify sending requests and d

Dalton Hubble 1.5k Jan 7, 2023
Gotcha is an high level HTTP client with a got-like API

Gotcha is an alternative to Go's http client, with an API inspired by got. It can interface with other HTTP packages through an adapter.

Sleeyax 20 Dec 7, 2022
Fast HTTP package for Go. Tuned for high performance. Zero memory allocations in hot paths. Up to 10x faster than net/http

fasthttp Fast HTTP implementation for Go. Currently fasthttp is successfully used by VertaMedia in a production serving up to 200K rps from more than

Aliaksandr Valialkin 18.9k Jan 2, 2023
Speak HTTP like a local. (the simple, intuitive HTTP console, golang version)

http-gonsole This is the Go port of the http-console. Speak HTTP like a local Talking to an HTTP server with curl can be fun, but most of the time it'

mattn 65 Jul 14, 2021
NATS HTTP Round Tripper - This is a Golang http.RoundTripper that uses NATS as a transport.

This is a Golang http.RoundTripper that uses NATS as a transport. Included is a http.RoundTripper for clients, a server that uses normal HTTP Handlers and any existing http handler mux and a Caddy Server transport.

R.I.Pienaar 81 Dec 6, 2022
Simple HTTP package that wraps net/http

Simple HTTP package that wraps net/http

Kris 0 Jan 17, 2022
Http-conection - A simple example of how to establish a HTTP connection using Golang

A simple example of how to establish a HTTP connection using Golang

Jonathan Gonzaga 0 Feb 1, 2022
Full-featured, plugin-driven, extensible HTTP client toolkit for Go

gentleman Full-featured, plugin-driven, middleware-oriented toolkit to easily create rich, versatile and composable HTTP clients in Go. gentleman embr

Tom 986 Dec 23, 2022
An enhanced http client for Golang

go-http-client An enhanced http client for Golang Documentation on ?? This package provides you a http client package for your http requests. Y

Furkan Bozdag 51 Dec 23, 2022