Back to Blog
Developer ToolsAPITerminalTutorial

cURL for Developers -- Test Any API From Your Terminal in Minutes

Published on April 6, 202612 min read

cURL for Developers -- Test Any API From Your Terminal in Minutes

Every developer needs to test APIs. Whether you are debugging a broken endpoint, verifying a deployment, or exploring a third-party API, you need a way to make HTTP requests and inspect the responses.

Postman and Thunder Client are great tools. But cURL is everywhere -- it is installed on every Mac, Linux, and modern Windows machine. It works over SSH. It works in CI/CD pipelines. It works in Docker containers. Learning cURL means you can test APIs anywhere, on any machine, without installing anything.


The Basics

Your First GET Request

> curl https://api.github.com/users/octocat

That is it. cURL makes a GET request by default and prints the response body to your terminal. You just hit the GitHub API without opening a browser.

Pretty-Print JSON Responses

Raw JSON is hard to read. Pipe the output through jq to format it:

> curl -s https://api.github.com/users/octocat | jq .

The -s flag (silent) suppresses the progress bar so only the JSON output goes to jq.

If you do not have jq installed:

  • macOS: brew install jq
  • Ubuntu: sudo apt install jq
  • Python alternative: pipe through python3 -m json.tool

See Response Headers

> curl -i https://api.github.com/users/octocat

The -i flag includes response headers in the output. You can see the status code, content type, rate limit headers, cache headers, and more.

See ONLY Headers (No Body)

> curl -I https://api.github.com/users/octocat

Capital -I sends a HEAD request -- you get headers without downloading the response body. Useful for checking if a URL is valid, what content type it returns, or how big the response is.

Verbose Mode (See Everything)

> curl -v https://api.github.com/users/octocat

The -v flag shows the full conversation: DNS resolution, TCP connection, TLS handshake, request headers, response headers, and response body. This is your best debugging tool when something is not working.


Making Different Types of Requests

POST Request with JSON Body

> curl -X POST https://api.example.com/users \

> -H "Content-Type: application/json" \

> -d '{"name": "Alice", "email": "alice@example.com"}'

Breaking it down:

  • -X POST -- use the POST method
  • -H -- add a header (Content-Type tells the server we are sending JSON)
  • -d -- the request body (data)

PUT Request (Update)

> curl -X PUT https://api.example.com/users/123 \

> -H "Content-Type: application/json" \

> -d '{"name": "Alice Updated"}'

PATCH Request (Partial Update)

> curl -X PATCH https://api.example.com/users/123 \

> -H "Content-Type: application/json" \

> -d '{"email": "newemail@example.com"}'

DELETE Request

> curl -X DELETE https://api.example.com/users/123


Authentication

Bearer Token (Most Common)

> curl https://api.example.com/me \

> -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."

Basic Auth

> curl -u username:password https://api.example.com/resource

cURL encodes the credentials in Base64 and sends them in the Authorization header automatically.

API Key in Header

> curl https://api.example.com/data \

> -H "X-API-Key: your-api-key-here"

API Key in Query Parameter

> curl "https://api.example.com/data?api_key=your-api-key-here"

Note the quotes around the URL -- without them, the shell might interpret the & character in URLs with multiple query parameters.


Working with Headers

Set Custom Headers

> curl https://api.example.com/data \

> -H "Accept: application/json" \

> -H "X-Request-ID: abc123" \

> -H "Cache-Control: no-cache"

You can add as many -H flags as you need.

Common Headers You Will Use

  • Content-Type: application/json -- telling the server your request body is JSON
  • Accept: application/json -- asking the server to respond with JSON
  • Authorization: Bearer TOKEN -- authentication
  • Cache-Control: no-cache -- bypass caching
  • User-Agent: MyApp/1.0 -- identify your client

File Uploads

Upload a File with multipart/form-data

> curl -X POST https://api.example.com/upload \

> -F "file=@/path/to/photo.jpg" \

> -F "description=My vacation photo"

The -F flag creates a multipart form submission. The @ symbol tells cURL to read from a file rather than sending the literal text.

Upload Raw File Content

> curl -X POST https://api.example.com/upload \

> -H "Content-Type: image/jpeg" \

> --data-binary @/path/to/photo.jpg

--data-binary sends the file contents as the raw request body without any encoding.


Downloading Files

Save Response to a File

> curl -o output.json https://api.example.com/data

> curl -O https://example.com/file.zip

-o lets you specify the filename. -O uses the filename from the URL.

Download with Progress Bar

> curl -# -O https://example.com/large-file.zip

The -# flag shows a progress bar instead of the default progress meter.

Resume a Failed Download

> curl -C - -O https://example.com/large-file.zip

-C - tells cURL to figure out where the download left off and resume from there.


Debugging API Issues

Follow Redirects

By default, cURL does not follow redirects. If you get a 301 or 302, add -L:

> curl -L https://example.com/old-url

Set a Timeout

Prevent cURL from hanging forever on a slow server:

> curl --connect-timeout 5 --max-time 10 https://api.example.com/slow-endpoint

--connect-timeout is how long to wait for the connection. --max-time is the total time allowed for the entire operation.

Send Request Body from a File

For large or complex JSON bodies, save them in a file:

> curl -X POST https://api.example.com/data \

> -H "Content-Type: application/json" \

> -d @request-body.json

The @ prefix reads the file contents as the request body.

Check Response Time

> curl -o /dev/null -s -w "\nHTTP Code: %{http_code}\nTotal Time: %{time_total}s\nDNS Time: %{dns_time}s\nConnect Time: %{time_connect}s\nTLS Time: %{time_appconnect}s\nFirst Byte: %{time_starttransfer}s\n" https://example.com

This outputs timing information without the response body:

  • time_total -- entire request duration
  • time_starttransfer -- time to first byte (TTFB)
  • dns_time -- DNS resolution time
  • time_connect -- TCP connection time
  • time_appconnect -- TLS handshake time

This is incredibly useful for diagnosing slow API responses. Is it DNS? The connection? TLS? Or the server processing?


Real-World Examples

Test a REST API CRUD Flow

> # Create a user

> curl -s -X POST http://localhost:3000/api/users \

> -H "Content-Type: application/json" \

> -d '{"name": "Alice", "email": "alice@example.com"}' | jq .

>

> # Read the user

> curl -s http://localhost:3000/api/users/1 | jq .

>

> # Update the user

> curl -s -X PUT http://localhost:3000/api/users/1 \

> -H "Content-Type: application/json" \

> -d '{"name": "Alice Smith"}' | jq .

>

> # Delete the user

> curl -s -X DELETE http://localhost:3000/api/users/1 | jq .

Check if a Website Is Up

> curl -s -o /dev/null -w "%{http_code}" https://example.com

Returns just the HTTP status code. Perfect for monitoring scripts.

Test Webhook Endpoints

> curl -X POST http://localhost:3000/webhooks/stripe \

> -H "Content-Type: application/json" \

> -d '{"type": "payment_intent.succeeded", "data": {"object": {"id": "pi_123", "amount": 2000}}}'

Download and Inspect Response Headers

> curl -s -D - https://example.com -o /dev/null

-D - dumps headers to stdout while -o /dev/null discards the body.

Send a GraphQL Query

> curl -X POST https://api.example.com/graphql \

> -H "Content-Type: application/json" \

> -H "Authorization: Bearer YOUR_TOKEN" \

> -d '{"query": "{ users { id name email } }"}'

Test CORS Headers

> curl -I -X OPTIONS https://api.example.com/endpoint \

> -H "Origin: https://your-frontend.com" \

> -H "Access-Control-Request-Method: POST"

Check the response for Access-Control-Allow-Origin and Access-Control-Allow-Methods headers.


Saving and Reusing Requests

Use a .curlrc File

Create ~/.curlrc with default options:

> -s

> --connect-timeout 10

> -H "Accept: application/json"

Now every cURL command uses these defaults automatically.

Save Requests as Shell Scripts

For APIs you test frequently, save the commands in a script:

> #!/bin/bash

> # test-api.sh

>

> BASE_URL="http://localhost:3000/api"

> TOKEN="your-dev-token"

>

> echo "=== Health Check ==="

> curl -s "$BASE_URL/health" | jq .

>

> echo "=== Get Users ==="

> curl -s -H "Authorization: Bearer $TOKEN" "$BASE_URL/users" | jq .

>

> echo "=== Create User ==="

> curl -s -X POST "$BASE_URL/users" \

> -H "Authorization: Bearer $TOKEN" \

> -H "Content-Type: application/json" \

> -d '{"name": "Test User", "email": "test@example.com"}' | jq .

Make it executable with chmod +x test-api.sh and run it with ./test-api.sh.

Use Environment Variables for Secrets

Never hardcode tokens in scripts that might be committed to Git:

> export API_TOKEN="your-token-here"

> curl -H "Authorization: Bearer $API_TOKEN" https://api.example.com/me


cURL vs Other Tools

When to Use cURL

  • Quick one-off requests
  • Debugging on remote servers via SSH
  • CI/CD pipelines and shell scripts
  • When you need to see the raw HTTP conversation (-v)
  • When you do not want to install anything

When to Use Postman / Thunder Client

  • Complex APIs with many endpoints to organize
  • Team collaboration (shared collections)
  • Automated test suites with assertions
  • OAuth2 flows with automatic token refresh
  • When you need a visual interface

When to Use HTTPie

If you find cURL syntax too verbose, HTTPie is a friendlier alternative:

> http GET api.example.com/users

> http POST api.example.com/users name=Alice email=alice@example.com

It is more readable but less universal -- cURL is installed everywhere, HTTPie is not.


Quick Reference

TaskCommand
GET requestcurl URL
POST with JSONcurl -X POST -H "Content-Type: application/json" -d '{}' URL
Add auth headercurl -H "Authorization: Bearer TOKEN" URL
See headerscurl -i URL
Follow redirectscurl -L URL
Save to filecurl -o file.txt URL
Upload filecurl -F "file=@path" URL
Verbose debugcurl -v URL
Pretty JSONcurl -s URL | jq .
Response timecurl -o /dev/null -s -w "%{time_total}" URL
Status code onlycurl -s -o /dev/null -w "%{http_code}" URL

cURL is one of those tools that feels clunky at first but becomes second nature after a week of daily use. Once you are comfortable with it, you will reach for it before opening any GUI tool because it is simply faster for most tasks.

For more developer guides and free tools, check out our blog and explore our developer tools.

Explore Our Free Tools & Games

Check out our curated collection of completely free browser games, tools, and extensions.

Browse Free Stuff

More Articles

Developer ToolsJSON

Stop Squinting at Messy JSON - Format It Instantly (Free Tool Inside)

Messy JSON is a productivity killer. Learn why formatting matters, common JSON pitfalls developers hit daily, and try our free browser-based JSON Formatter that works instantly with zero sign-ups.

7 min readRead More→
Browser GamesFree Games

Best Free Browser Games You Can Play Right Now in 2025

Discover the top free browser games of 2025 that require no downloads, no installs, and no sign-ups. From puzzle games to multiplayer adventures, these games run right in your browser.

8 min readRead More→
Developer ToolsFree Tools

Free Developer Tools Every Programmer Needs in Their Toolkit

A comprehensive guide to the best free developer tools available online. From JSON formatters to regex testers, these browser-based tools will supercharge your productivity.

10 min readRead More→
Chrome ExtensionsProductivity

10 Free Chrome Extensions That Will Boost Your Productivity

These free Chrome extensions will transform how you browse, work, and manage your time online. From tab management to dark mode, these extensions are must-haves.

7 min readRead More→