
Making API Calls with cURL: Your Command-Line API Toolkit
Master cURL for REST API testing. Learn to make GET, POST, PUT, PATCH, and DELETE requests from the command line with real, working examples against live APIs.
Making API Calls with cURL: Your Command-Line API Toolkit
It is time to get your hands dirty. In this module, you will learn to use cURL — one of the most powerful and universal tools for interacting with REST APIs. cURL is installed on nearly every computer in the world (macOS, Linux, and Windows 10+), and it is the standard tool developers use to quickly test and debug API endpoints.
By the end of this module, you will be able to make any type of REST API call from your terminal with confidence.
1. What Is cURL?
cURL (Client URL) is a command-line tool for transferring data using various network protocols. For our purposes, we use it to send HTTP requests and receive HTTP responses.
Why cURL?
| Advantage | Description |
|---|---|
| Universal | Pre-installed on macOS, Linux, and Windows 10+ |
| Fast | No GUI to load, instant results |
| Scriptable | Can be used in shell scripts and CI/CD pipelines |
| Precise | Full control over every aspect of the request |
| Standard | API documentation often provides cURL examples |
Checking Your cURL Installation
Open your terminal and type:
curl --version
You should see output like:
curl 8.4.0 (x86_64-apple-darwin23.0) libcurl/8.4.0
Release-Date: 2023-10-11
Protocols: dict file ftp ftps http https ...
Features: alt-svc AsynchDNS GSS-API HSTS HTTP2 ...
If you see a version number, you are ready to go. If not, visit curl.se to install it.
2. Your First cURL Request
The simplest cURL command is a basic GET request:
curl https://jsonplaceholder.typicode.com/posts/1
That is it. By default, cURL sends a GET request. The output will be the JSON response from the server:
{
"userId": 1,
"id": 1,
"title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
"body": "quia et suscipit\nsuscipit recusandae consequuntur..."
}
Understanding the Default Behavior
When you type curl URL, cURL:
- Sends an HTTP GET request to that URL
- Prints the response body to your terminal
- Does not show headers or status codes by default
To see more information, you need to add flags (options).
3. Essential cURL Flags
cURL has hundreds of options, but you only need to know about a dozen for everyday API work. Here are the essential ones:
The Most Important Flags
| Flag | Long Form | Purpose | Example |
|---|---|---|---|
-X | --request | Set the HTTP method | -X POST |
-H | --header | Add a request header | -H "Content-Type: application/json" |
-d | --data | Send request body data | -d '{"name": "John"}' |
-i | --include | Show response headers | -i |
-v | --verbose | Show full request/response | -v |
-s | --silent | Hide progress bar | -s |
-o | --output | Save response to file | -o response.json |
-w | --write-out | Custom output format | -w "%{http_code}" |
Seeing the Full Conversation
The -v (verbose) flag is your best friend for debugging. It shows everything:
curl -v https://jsonplaceholder.typicode.com/posts/1
Output breakdown:
* Trying 104.21.64.1:443...
* Connected to jsonplaceholder.typicode.com
* SSL connection using TLSv1.3
> GET /posts/1 HTTP/2 ← Your request
> Host: jsonplaceholder.typicode.com
> User-Agent: curl/8.4.0
> Accept: */*
>
< HTTP/2 200 ← Server response status
< content-type: application/json
< cache-control: max-age=43200
<
{ ← Response body
"userId": 1,
"id": 1,
...
}
Lines with > are what you sent. Lines with < are what you received.
Seeing Only Headers
Use -I (capital i) for a HEAD request that returns only headers:
curl -I https://jsonplaceholder.typicode.com/posts/1
HTTP/2 200
content-type: application/json; charset=utf-8
cache-control: max-age=43200
Seeing Headers Plus Body
Use -i (lowercase) to include response headers with the body:
curl -i https://jsonplaceholder.typicode.com/posts/1
4. GET Requests in Detail
GET is the most common HTTP method. It retrieves data without modifying anything.
Get a Collection (All Posts)
curl https://jsonplaceholder.typicode.com/posts
This returns an array of 100 posts. The output can be long, so you can pipe it to a formatter:
# On macOS/Linux — pretty-print JSON
curl -s https://jsonplaceholder.typicode.com/posts | python3 -m json.tool
# Or limit output to first few lines
curl -s https://jsonplaceholder.typicode.com/posts | head -20
Get a Single Resource
curl https://jsonplaceholder.typicode.com/posts/1
Get with Query Parameters
Query parameters filter, sort, or paginate results:
# Get posts by a specific user
curl "https://jsonplaceholder.typicode.com/posts?userId=1"
# Get comments for a specific post
curl "https://jsonplaceholder.typicode.com/comments?postId=1"
Important: When your URL contains ? or &, wrap it in quotes to prevent shell interpretation.
Get Nested Resources
# Get comments for post 1 (nested resource URL)
curl https://jsonplaceholder.typicode.com/posts/1/comments
# Get posts by user 1 (nested resource URL)
curl https://jsonplaceholder.typicode.com/users/1/posts
Both approaches (query parameter and nested URL) work. Use nested URLs when the relationship is clear and direct.
graph TD
A[GET Requests] --> B[Collection]
A --> C[Single Resource]
A --> D[Filtered]
A --> E[Nested]
B --> B1["curl /posts"]
C --> C1["curl /posts/1"]
D --> D1["curl /posts?userId=1"]
E --> E1["curl /posts/1/comments"]
style A fill:#4f46e5,color:#fff
style B fill:#0891b2,color:#fff
style C fill:#0891b2,color:#fff
style D fill:#0891b2,color:#fff
style E fill:#0891b2,color:#fff
5. POST Requests: Creating Data
POST creates a new resource. You need to specify three things:
- The method (
-X POST) - The content type header (
-H "Content-Type: application/json") - The request body (
-d '...')
Basic POST Request
curl -X POST https://jsonplaceholder.typicode.com/posts \
-H "Content-Type: application/json" \
-d '{
"title": "My First API Post",
"body": "This post was created using cURL!",
"userId": 1
}'
Response (201 Created):
{
"title": "My First API Post",
"body": "This post was created using cURL!",
"userId": 1,
"id": 101
}
Understanding Each Part
graph TD
A["curl -X POST"] --> B["Method: POST"]
C["-H Content-Type: application/json"] --> D["Header: telling server<br/>we are sending JSON"]
E["-d JSON payload"] --> F["Body: the actual data<br/>to create"]
B --> G[Complete Request]
D --> G
F --> G
G --> H["Server creates resource<br/>Returns 201 Created"]
style A fill:#4f46e5,color:#fff
style C fill:#059669,color:#fff
style E fill:#d97706,color:#fff
style H fill:#0891b2,color:#fff
Let us break down each flag:
| Part | Meaning |
|---|---|
curl | The cURL command |
-X POST | Use the POST method |
URL | Where to send the request |
-H "Content-Type: application/json" | Tell the server: "I am sending JSON" |
-d '{...}' | The JSON data to send |
\ | Line continuation (for readability) |
POST with Multiline Data
For readability, break long commands across multiple lines using \:
curl -X POST https://jsonplaceholder.typicode.com/posts \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"title": "Learning REST APIs",
"body": "This is a comprehensive guide to REST API fundamentals.",
"userId": 1
}'
POST from a File
If your JSON data is in a file, use @ to reference it:
# Create a file called post.json
echo '{
"title": "From File",
"body": "This data came from a file!",
"userId": 1
}' > post.json
# Use it in cURL
curl -X POST https://jsonplaceholder.typicode.com/posts \
-H "Content-Type: application/json" \
-d @post.json
This is especially useful for large payloads.
6. PUT Requests: Replacing Data
PUT replaces an entire resource. You must include all fields.
curl -X PUT https://jsonplaceholder.typicode.com/posts/1 \
-H "Content-Type: application/json" \
-d '{
"id": 1,
"title": "Completely Updated Title",
"body": "This is entirely new content that replaces the old post.",
"userId": 1
}'
Response (200 OK):
{
"id": 1,
"title": "Completely Updated Title",
"body": "This is entirely new content that replaces the old post.",
"userId": 1
}
Notice that we include the id field as well. With PUT, you are saying: "Here is the complete, new version of this resource."
7. PATCH Requests: Partial Updates
PATCH updates only the fields you specify:
curl -X PATCH https://jsonplaceholder.typicode.com/posts/1 \
-H "Content-Type: application/json" \
-d '{
"title": "Only This Title Changes"
}'
Response (200 OK):
{
"userId": 1,
"id": 1,
"title": "Only This Title Changes",
"body": "quia et suscipit..."
}
Notice that the body and userId remain unchanged. Only the title was updated.
8. DELETE Requests: Removing Data
DELETE is the simplest method. It usually does not require a body:
curl -X DELETE https://jsonplaceholder.typicode.com/posts/1
Response: The server returns a 200 OK with an empty object {}, confirming the deletion.
DELETE with Verbose Output
To see the status code confirming deletion:
curl -v -X DELETE https://jsonplaceholder.typicode.com/posts/1
You should see < HTTP/2 200 in the output.
9. Advanced cURL Techniques
Getting Only the Status Code
Sometimes you just want to know if a request succeeded:
curl -s -o /dev/null -w "%{http_code}" \
https://jsonplaceholder.typicode.com/posts/1
Output: 200
Breakdown:
-s— Silent mode (no progress bar)-o /dev/null— Discard the body-w "%{http_code}"— Print only the status code
Timing Your Requests
To measure API performance:
curl -s -o /dev/null -w "Total time: %{time_total}s\n" \
https://jsonplaceholder.typicode.com/posts/1
Output: Total time: 0.234s
Saving Responses to Files
curl -o response.json https://jsonplaceholder.typicode.com/posts/1
This saves the JSON response to response.json.
Following Redirects
Some URLs redirect. Use -L to follow redirects automatically:
curl -L http://github.com
Setting a Timeout
Prevent cURL from hanging on slow servers:
curl --connect-timeout 5 --max-time 10 \
https://jsonplaceholder.typicode.com/posts/1
--connect-timeout 5— Give up if connection takes more than 5 seconds--max-time 10— Give up if the entire operation takes more than 10 seconds
10. Working with Authentication
Real-world APIs require authentication. Here is how to handle the most common methods:
API Key in Header
curl -H "X-API-Key: your-api-key-here" \
https://api.example.com/data
API Key in Query Parameter
curl "https://api.example.com/data?api_key=your-api-key-here"
Bearer Token (JWT)
curl -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
https://api.example.com/protected/resource
Basic Authentication
curl -u username:password https://api.example.com/data
The -u flag encodes your credentials in Base64 and sends them in the Authorization: Basic header.
11. Practical cURL Cheat Sheet
Here is a complete reference card you can bookmark:
graph TD
A[cURL Cheat Sheet] --> B[GET]
A --> C[POST]
A --> D[PUT]
A --> E[PATCH]
A --> F[DELETE]
A --> G[Debug]
B --> B1["curl URL"]
C --> C1["curl -X POST URL<br/>-H Content-Type: json<br/>-d JSON"]
D --> D1["curl -X PUT URL<br/>-H Content-Type: json<br/>-d JSON"]
E --> E1["curl -X PATCH URL<br/>-H Content-Type: json<br/>-d JSON"]
F --> F1["curl -X DELETE URL"]
G --> G1["-v verbose<br/>-i headers+body<br/>-I headers only<br/>-s silent"]
style A fill:#4f46e5,color:#fff
style B fill:#0891b2,color:#fff
style C fill:#059669,color:#fff
style D fill:#d97706,color:#fff
style E fill:#d97706,color:#fff
style F fill:#dc2626,color:#fff
style G fill:#6b7280,color:#fff
Quick Reference Commands
# GET - Retrieve data
curl https://jsonplaceholder.typicode.com/posts
# GET - Single item
curl https://jsonplaceholder.typicode.com/posts/1
# GET - With query params
curl "https://jsonplaceholder.typicode.com/posts?userId=1"
# POST - Create
curl -X POST https://jsonplaceholder.typicode.com/posts \
-H "Content-Type: application/json" \
-d '{"title": "New", "body": "Content", "userId": 1}'
# PUT - Full update
curl -X PUT https://jsonplaceholder.typicode.com/posts/1 \
-H "Content-Type: application/json" \
-d '{"id": 1, "title": "Updated", "body": "New body", "userId": 1}'
# PATCH - Partial update
curl -X PATCH https://jsonplaceholder.typicode.com/posts/1 \
-H "Content-Type: application/json" \
-d '{"title": "Patched Title"}'
# DELETE - Remove
curl -X DELETE https://jsonplaceholder.typicode.com/posts/1
# Debug - See everything
curl -v https://jsonplaceholder.typicode.com/posts/1
# Status code only
curl -s -o /dev/null -w "%{http_code}" \
https://jsonplaceholder.typicode.com/posts/1
12. Common cURL Mistakes and Fixes
| Mistake | Symptom | Fix |
|---|---|---|
Missing quotes around URL with ? | Shell error or wrong URL | Wrap URL in double quotes |
| Wrong Content-Type | 400 Bad Request or 415 | Add -H "Content-Type: application/json" |
| Invalid JSON in body | 400 Bad Request | Validate JSON at jsonlint.com |
| Single quotes inside data on Windows | Parse error | Use double quotes and escape inner quotes |
Forgetting -X for non-GET methods | Sends GET instead of POST | Always specify -X POST, -X PUT, etc. |
No -d with POST | Empty body, 400 error | Include -d '{...}' with body data |
Windows-Specific Note
On Windows Command Prompt (not PowerShell), you must use double quotes for JSON and escape inner quotes:
curl -X POST https://jsonplaceholder.typicode.com/posts ^
-H "Content-Type: application/json" ^
-d "{\"title\": \"Test\", \"body\": \"Content\", \"userId\": 1}"
On PowerShell or Windows Terminal with bash, you can use single quotes like the examples above.
Summary and Key Takeaways
- cURL is the universal command-line tool for testing REST APIs.
- By default,
curl URLsends a GET request. - Use
-Xto specify the method,-Hfor headers, and-dfor the request body. - Use
-vfor debugging,-ifor headers, and-sfor clean output. - Always set
Content-Type: application/jsonwhen sending JSON data. - Wrap URLs with query parameters in double quotes.
- Use
@filenameto send data from a file. - Every API call you will ever make in tools like Postman or code can be replicated in cURL.
Lesson Review Quiz
?Knowledge Check
What does the -X flag do in cURL?
?Knowledge Check
Which cURL command correctly creates a new post?
?Knowledge Check
What does curl -v do?
Practice Exercise
Complete the following tasks using only cURL:
-
Retrieve all users from
https://jsonplaceholder.typicode.com/users -
Create a new post with any title and body:
curl -X POST https://jsonplaceholder.typicode.com/posts \
-H "Content-Type: application/json" \
-d '{"title": "YOUR TITLE", "body": "YOUR BODY", "userId": 1}'
-
Update post 1 with a new title using PATCH
-
Delete post 5
-
Get the status code for a request to
/posts/99999(hint: use-s -o /dev/null -w "%{http_code}") -
Compare the verbose output (
-v) of a GET request and a POST request. Notice the differences in the request section.
In the next module, we will learn about JSON — the data format that powers all REST API communication.