We’ve been using curl to test APIs since the late 90s. It’s fine. It works. But let’s be honest: in 2025, typing out curl commands for REST APIs is like manually calculating your taxes with a pencil. Sure, you can do it, but why would you?
Enter restish. It’s a modern CLI for REST APIs that actually understands REST semantics. Here’s why you should probably switch.
The Problem With curl
curl is a general-purpose HTTP client. It doesn’t know anything about REST APIs, JSON, or modern API conventions. This means you end up typing commands like:
curl -X POST https://api.example.com/users \
-H "Content-Type: application/json" \
-H "Authorization: Bearer eyJhbGc..." \
-d '{"name": "John", "email": "john@example.com"}'
Every. Single. Time.
You’re manually specifying:
- The HTTP method
- Content type headers
- Authentication headers
- JSON data formatting
And if you want to see pretty output? Pipe it to jq. Want to save auth tokens? Write a wrapper script. Need to follow API links? Good luck.
What restish Actually Does
restish is designed specifically for REST APIs. It understands:
- JSON by default
- Authentication flows
- API specifications (OpenAPI, API Blueprint)
- HATEOAS and link following
- Response pagination
Same request in restish:
restish post api.example.com/users name=John email=john@example.com
That’s it. No headers, no JSON formatting, no manual auth management. restish figures it out.
Authentication That Doesn’t Suck
With curl, you manage tokens manually:
# Get a token
TOKEN=$(curl -X POST https://api.example.com/auth \
-d '{"username":"user","password":"pass"}' | jq -r .token)
# Use the token
curl -H "Authorization: Bearer $TOKEN" https://api.example.com/users
With restish, auth is configured once:
# Configure OAuth2 (one time setup)
restish api configure api.example.com \
--auth oauth-client-credentials \
--client-id YOUR_CLIENT_ID \
--client-secret YOUR_CLIENT_SECRET
# Now just make requests
restish get api.example.com/users
restish handles token refresh, storage, and injection automatically. This alone is worth the switch.
JSON Handling Built-In
curl approach:
# Ugly unformatted JSON
curl https://api.example.com/users
# Pipe to jq for readability
curl -s https://api.example.com/users | jq
# Extract a field
curl -s https://api.example.com/users | jq -r '.[0].email'
restish approach:
# Pretty JSON by default
restish get api.example.com/users
# Extract fields with built-in filtering
restish get api.example.com/users -f [0].email
# Output in different formats
restish get api.example.com/users -o yaml
restish get api.example.com/users -o csv
No external tools needed. It just works.
OpenAPI Integration
If your API has an OpenAPI spec (and in 2025, it should), restish can use it:
# Link an API spec
restish api configure api.example.com \
--spec-url https://api.example.com/openapi.json
# Now get autocomplete and validation
restish get api.example.com/users/[TAB]
# Shows available endpoints and parameters
restish validates your requests against the spec, shows available operations, and provides inline documentation. curl has no idea specs exist.
Real-World Example: Creating a User
The curl Way (2024 Style)
# Set up auth
export API_TOKEN="your_token_here"
export API_URL="https://api.example.com"
# Create a user
curl -X POST "$API_URL/users" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $API_TOKEN" \
-H "Accept: application/json" \
-d '{
"name": "Jane Doe",
"email": "jane@example.com",
"role": "developer",
"metadata": {
"team": "backend",
"location": "remote"
}
}' | jq
# Get the user ID from output, then fetch details
USER_ID="abc123"
curl -X GET "$API_URL/users/$USER_ID" \
-H "Authorization: Bearer $API_TOKEN" | jq
# Update the user
curl -X PATCH "$API_URL/users/$USER_ID" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $API_TOKEN" \
-d '{"role": "senior-developer"}' | jq
The restish Way (2025 Style)
# Create a user
restish post api.example.com/users \
name="Jane Doe" \
email=jane@example.com \
role=developer \
metadata.team=backend \
metadata.location=remote
# Get details (using response links)
restish links[self]
# Update the user
restish patch api.example.com/users/abc123 role=senior-developer
Less typing, same result. Auth and formatting handled automatically.
Advanced Features That Actually Matter
1. Response Caching
# restish caches responses automatically
restish get api.example.com/users
# Fast on subsequent calls
# curl requires manual caching
curl -s https://api.example.com/users > users.json
cat users.json
2. Link Following (HATEOAS)
# If API returns hypermedia links
restish get api.example.com/users
# Response includes: links.next = "/users?page=2"
# Follow links directly
restish links[next]
# curl requires manual URL construction
curl https://api.example.com/users?page=2
3. Request History
# restish keeps history
restish history
restish rerun 5 # Re-run command #5
# curl needs shell history hacks
history | grep curl
4. API Profiles
# Switch between environments
restish api configure prod.example.com --auth ...
restish api configure staging.example.com --auth ...
# Switch contexts
restish get prod.example.com/users
restish get staging.example.com/users
# curl needs manual URL/token swapping
When curl Is Still Better
Let’s be fair. curl is more appropriate when:
You’re not working with REST APIs: FTP, SMTP, file downloads, etc. restish is REST-only.
You need maximum control: curl exposes every HTTP detail. Sometimes you need that.
Scripting in constrained environments: curl is everywhere by default. restish needs installation.
Non-JSON APIs: If you’re working with XML, form data, or binary protocols, curl is more flexible.
One-off requests: For a single quick request, curl -s url | jq is fine.
When restish Wins
Daily API development: If you’re testing APIs regularly, restish saves hours of typing.
Authenticated APIs: OAuth2, API keys, token refresh—restish handles it all.
Exploring unfamiliar APIs: OpenAPI integration and link following make discovery easier.
Team workflows: Shared API configs mean consistent auth and base URLs across the team.
Documentation and examples: restish commands are more readable in docs and READMEs.
Migration Path
You don’t have to go all-in immediately. Here’s a practical approach:
# Install restish
brew install restish # macOS
# or
curl -fsSL https://rest.sh/install.sh | sh
# Configure your main API
restish api configure api.example.com
# Start using it for authenticated requests
restish get api.example.com/users
# Keep using curl for quick unauthenticated calls
curl https://api.example.com/health
Gradually replace curl commands in your scripts and docs. You’ll notice the difference within a week.
The Bigger Picture
We’re in 2025. REST APIs have been mainstream for over a decade. The tooling should’ve evolved past manually typing HTTP headers and piping to jq.
restish isn’t revolutionary. It’s just common sense. It does for APIs what git did for version control—provides a domain-specific interface that actually understands what you’re trying to do.
curl will always have a place for low-level HTTP work and non-REST protocols. But for modern REST APIs, restish is what you should be reaching for.
Getting Started
Install restish:
# macOS
brew install restish
# Linux
curl -fsSL https://rest.sh/install.sh | sh
# Windows
scoop install restish
Configure your first API:
# Auto-detect configuration
restish api configure api.example.com
# Or specify auth manually
restish api configure api.example.com \
--auth bearer \
--token YOUR_API_TOKEN
Make a request:
restish get api.example.com/endpoint
Check the docs: https://rest.sh
Final Thoughts
curl is 27 years old. It’s done its job admirably. But in 2025, when you’re working with REST APIs, there’s a better tool.
restish eliminates the boilerplate, handles authentication, understands JSON natively, and makes API exploration actually pleasant.
Try it for a week. You probably won’t go back.
Resources:
Choose tools that match the problem. For REST APIs in 2025, that tool is restish.