Making API Calls with cURL: Your Command-Line API Toolkit

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?

AdvantageDescription
UniversalPre-installed on macOS, Linux, and Windows 10+
FastNo GUI to load, instant results
ScriptableCan be used in shell scripts and CI/CD pipelines
PreciseFull control over every aspect of the request
StandardAPI 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:

  1. Sends an HTTP GET request to that URL
  2. Prints the response body to your terminal
  3. 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

FlagLong FormPurposeExample
-X--requestSet the HTTP method-X POST
-H--headerAdd a request header-H "Content-Type: application/json"
-d--dataSend request body data-d '{"name": "John"}'
-i--includeShow response headers-i
-v--verboseShow full request/response-v
-s--silentHide progress bar-s
-o--outputSave response to file-o response.json
-w--write-outCustom 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:

  1. The method (-X POST)
  2. The content type header (-H "Content-Type: application/json")
  3. 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:

PartMeaning
curlThe cURL command
-X POSTUse the POST method
URLWhere 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

MistakeSymptomFix
Missing quotes around URL with ?Shell error or wrong URLWrap URL in double quotes
Wrong Content-Type400 Bad Request or 415Add -H "Content-Type: application/json"
Invalid JSON in body400 Bad RequestValidate JSON at jsonlint.com
Single quotes inside data on WindowsParse errorUse double quotes and escape inner quotes
Forgetting -X for non-GET methodsSends GET instead of POSTAlways specify -X POST, -X PUT, etc.
No -d with POSTEmpty body, 400 errorInclude -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

  1. cURL is the universal command-line tool for testing REST APIs.
  2. By default, curl URL sends a GET request.
  3. Use -X to specify the method, -H for headers, and -d for the request body.
  4. Use -v for debugging, -i for headers, and -s for clean output.
  5. Always set Content-Type: application/json when sending JSON data.
  6. Wrap URLs with query parameters in double quotes.
  7. Use @filename to send data from a file.
  8. 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:

  1. Retrieve all users from https://jsonplaceholder.typicode.com/users

  2. 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}'
  1. Update post 1 with a new title using PATCH

  2. Delete post 5

  3. Get the status code for a request to /posts/99999 (hint: use -s -o /dev/null -w "%{http_code}")

  4. 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.

Subscribe to our newsletter

Get the latest posts delivered right to your inbox.

Subscribe on LinkedIn