Build a REST API Client in Bash Using Curl and jq
Interacting with RESTful APIs doesn’t always require Python, Node, or Postman. You can harness the simplicity and ubiquity of Bash, combined with the power of curl and jq, to script robust API clients right from the terminal. This article shows you how to do just that—handling authentication, crafting requests, and parsing JSON responses in clean, reusable scripts.
1. Setting Up: The Tools You Need
All you need to build an API client in Bash are two command-line utilities:
- curl: Sends HTTP requests
- jq: Parses and manipulates JSON data
Install them via your package manager if you don’t already have them:
# Ubuntu/Debian
sudo apt install curl jq
# macOS (with Homebrew)
brew install curl jq
2. Making Your First GET Request
Let’s begin by making a simple GET request to a publicly accessible API—the GitHub API, for example. We’ll retrieve information about a GitHub user.
#!/bin/bash
USERNAME="octocat"
API_URL="https://api.github.com/users/$USERNAME"
response=$(curl -s "$API_URL")
# Extract and print name using jq
name=$(echo "$response" | jq -r '.name')
echo "Name: $name"
Explanation:
curl -s: Silently fetches data from the specified URL.jq -r '.name': Extracts thenamefield from the JSON response, with-rfor raw output.
This kind of interaction is perfect for quick data pulls, piping into other scripts, or monitoring systems.
3. Handling Authentication
Most APIs require some form of authentication. For instance, GitHub accepts personal access tokens via headers. Here’s how to authenticate:
#!/bin/bash
TOKEN="your_github_token_here"
USERNAME="octocat"
API_URL="https://api.github.com/users/$USERNAME"
response=$(curl -s -H "Authorization: token $TOKEN" "$API_URL")
echo "$response" | jq
Other common authentication styles include:
- Bearer tokens:
-H "Authorization: Bearer $TOKEN" - API keys in URL:
?apikey=$KEY
Keep tokens secret using environment variables or .env files rather than hardcoding credentials in scripts.
4. Sending POST Requests with JSON
Many APIs expect data to be sent via POST requests. To do this in Bash, you’d serialize your data to JSON and send it with the right Content-Type header.
#!/bin/bash
API_URL="https://jsonplaceholder.typicode.com/posts"
DATA=$(jq -n \
--arg title "Bash + API" \
--arg body "This post was made from a bash script" \
--arg userId "1" \
'{title: $title, body: $body, userId: $userId}')
response=$(curl -s -X POST "$API_URL" \
-H "Content-Type: application/json" \
-d "$DATA")
# Display the created post's ID and title
echo "$response" | jq '.id, .title'
jq -n builds structured JSON on the fly—cleaner and safer than manually concatenating strings.
5. Creating Reusable Bash Functions
For reliability and maintainability, abstracting logic into reusable functions is key. Example: a generic GET function.
#!/bin/bash
API_GET() {
local url=$1
local token=$2
curl -s -H "Authorization: token $token" "$url"
}
API_URL="https://api.github.com/repos/octocat/Hello-World"
TOKEN="your_token_here"
response=$(API_GET "$API_URL" "$TOKEN")
echo "$response" | jq '.full_name, .stargazers_count'
This pattern promotes DRY (Don’t Repeat Yourself) principles and makes testing easier.
6. Error Handling and Status Codes
Always check HTTP response codes to gracefully handle errors:
#!/bin/bash
API_URL="https://api.github.com/repos/octocat/Hello-World"
response=$(curl -s -w "\n%{http_code}" "$API_URL")
# Split the response and the status code
http_body=$(echo "$response" | sed '$d')
status_code=$(echo "$response" | tail -n1)
if [[ "$status_code" -eq 200 ]]; then
echo "$http_body" | jq
else
echo "Error: HTTP $status_code"
fi
-w "%{http_code}" appends the status code, which we later extract to check for errors.
7. Pro Tips for Performance and Maintainability
- Use caching wisely: Save frequent responses to files with
jqand reuse them. - Pipe safely: Always quote JSON variables and fields in bash scripts to avoid unexpected parsing behavior.
- Use set -e in production-grade scripts to exit on error.
- Modularize your code: Put helper functions in separate files and source them when needed.
Conclusion
Bash, curl, and jq form a compact yet powerful toolkit for working with REST APIs directly from the terminal. Whether you’re automating tasks, integrating internal services, or just exploring an API, this stack is light, fast, and surprisingly scalable for many scripting use cases. Happy hacking—and don’t forget to chmod +x your scripts!
Useful links:


