Day 4 — API Testing with curl#

Goal: Use curl to send HTTP requests from the terminal — GET, POST, headers, JSON bodies — so you can test and debug APIs without leaving the command line.


What Is curl?#

curl (Client URL) is a command-line tool for making HTTP requests. It comes pre-installed on most Linux systems.

curl --version
# curl 8.x.x ...

Think of curl as a browser for your terminal — but instead of rendering a web page, it shows you the raw HTTP response.


Basic GET Request#

# The simplest curl command:
curl https://httpbin.org/get

This sends a GET request and prints the response body. httpbin.org is a free API for testing HTTP requests.

Useful flags for GET#

# Show response headers too (-i = include headers):
curl -i https://httpbin.org/get

# Show ONLY headers (-I = head request):
curl -I https://httpbin.org/get

# Silent mode — hide progress bar (-s):
curl -s https://httpbin.org/get

# Follow redirects (-L):
curl -L https://httpbin.org/redirect/1

# Show verbose debug info (-v):
curl -v https://httpbin.org/get

🧠 Knowledge Check#

Q1: You want to see both the response body and the HTTP headers that the server sends back. Which curl flag should you use?

  • A) -v
  • B) -i
  • C) -I
  • D) -s
Answer

B-i includes the response headers in the output before the response body.


GET with Query Parameters#

Query parameters go after ? in the URL:

# Single parameter:
curl "https://httpbin.org/get?name=Alice"

# Multiple parameters (use & to separate):
curl "https://httpbin.org/get?name=Alice&role=student&course=TDS"

Important: Always put the URL in quotes if it contains &, ?, or other special characters.


Setting Headers#

Use -H to add headers:

# Accept JSON:
curl -H "Accept: application/json" https://httpbin.org/get

# Multiple headers:
curl -H "Accept: application/json" \
     -H "X-Custom-Header: hello" \
     https://httpbin.org/headers

# Authorization header:
curl -H "Authorization: Bearer my-token-123" \
     https://httpbin.org/bearer

POST Requests — Sending Data#

Sending JSON#

# POST with JSON body (-d = data, -X = method):
curl -X POST https://httpbin.org/post \
     -H "Content-Type: application/json" \
     -d '{"name": "Alice", "age": 25}'

Shorter way — POST is implied with -d#

# curl automatically uses POST when you provide -d:
curl https://httpbin.org/post \
     -H "Content-Type: application/json" \
     -d '{"name": "Alice", "age": 25}'

Sending form data#

# URL-encoded form data (like an HTML form):
curl -X POST https://httpbin.org/post \
     -d "name=Alice&age=25"

# File upload:
curl -X POST https://httpbin.org/post \
     -F "[email protected]"

Reading data from a file#

# Create a JSON file:
echo '{"name": "Alice", "email": "[email protected]"}' > /tmp/user.json

# Send it:
curl -X POST https://httpbin.org/post \
     -H "Content-Type: application/json" \
     -d @/tmp/user.json

🧠 Knowledge Check#

Q1: What does the -d flag in curl do?

  • A) Downloads a file
  • B) Deletes a resource
  • C) Sends data as the request body and automatically sets the method to POST
  • D) Disables SSL verification
Answer

C — The -d (or --data) flag sends the specified data in the request body. When used, curl automatically assumes you want to make a POST request.


PUT, PATCH, DELETE Requests#

# PUT — replace a resource:
curl -X PUT https://httpbin.org/put \
     -H "Content-Type: application/json" \
     -d '{"name": "Alice Updated", "age": 26}'

# PATCH — partial update:
curl -X PATCH https://httpbin.org/patch \
     -H "Content-Type: application/json" \
     -d '{"age": 26}'

# DELETE — remove a resource:
curl -X DELETE https://httpbin.org/delete

Working with the Response#

Saving response to a file#

# Save response body:
curl -s https://httpbin.org/get -o response.json

# Save to file named from URL:
curl -s -O https://example.com/data.csv

Piping to other tools#

# Pretty-print JSON with jq:
curl -s https://httpbin.org/get | jq .

# Extract specific fields:
curl -s https://httpbin.org/get | jq '.headers."User-Agent"'

# Count response size:
curl -s https://httpbin.org/get | wc -c

# Search response:
curl -s https://httpbin.org/get | grep "origin"

Getting just the status code#

# Output only the HTTP status code:
curl -s -o /dev/null -w "%{http_code}\n" https://httpbin.org/get
# 200

curl -s -o /dev/null -w "%{http_code}\n" https://httpbin.org/status/404
# 404

Authentication with curl#

Basic auth#

curl -u "username:password" https://httpbin.org/basic-auth/username/password

Bearer token#

curl -H "Authorization: Bearer my-token-123" https://httpbin.org/bearer

API key in header#

curl -H "X-API-Key: abc123" https://api.example.com/data

Debugging with -v (Verbose)#

The -v flag shows the full request and response:

curl -v https://httpbin.org/get
* Trying 34.193.x.x:443...
* Connected to httpbin.org
> GET /get HTTP/2                    ← request line
> Host: httpbin.org                  ← request headers
> User-Agent: curl/8.x.x
> Accept: */*
>
< HTTP/2 200                         ← response status
< content-type: application/json     ← response headers
< content-length: 256
<
{                                     ← response body
  "args": {},
  "headers": {...},
  ...
}

Lines starting with > are what you sent. Lines starting with < are what the server returned.


curl Cheatsheet#

FlagWhat it doesExample
-X METHODSet HTTP method-X POST, -X DELETE
-H "..."Add a header-H "Content-Type: application/json"
-d '...'Send data (body)-d '{"key": "value"}'
-d @fileSend data from file-d @data.json
-iInclude response headerscurl -i url
-IHeaders only (HEAD request)curl -I url
-sSilent (no progress bar)curl -s url
-vVerbose (debug)curl -v url
-LFollow redirectscurl -L url
-o fileSave output to file-o response.json
-u user:passBasic authentication-u admin:secret
-w "format"Custom output format-w "%{http_code}"
-F "key=val"Form/file upload-F "[email protected]"

Q&A#

Q: What is the difference between -d and -F?

A:

  • -d sends data as the request body (typically JSON or form-encoded)
  • -F sends data as multipart/form-data — used for file uploads
# -d: JSON data
curl -d '{"name": "Alice"}' -H "Content-Type: application/json" url

# -F: file upload
curl -F "[email protected]" -F "caption=My photo" url
Q: How do I send a POST with an empty body?

A:

curl -X POST https://api.example.com/action -d ''
# or:
curl -X POST https://api.example.com/action -H "Content-Length: 0"
Q: How do I install jq for pretty-printing JSON?

A:

sudo apt install jq -y

# Now you can:
curl -s https://httpbin.org/get | jq .        # pretty-print
curl -s https://httpbin.org/get | jq '.origin' # extract field

jq is an essential tool for working with JSON on the command line.

Q: Can I use curl with HTTPS?

A: Yes, curl handles HTTPS by default. It verifies SSL certificates automatically. If you get certificate errors on a dev/test server, you can skip verification with -k (insecure — only for testing):

curl -k https://self-signed-server.local/api

Exercises#

Exercise 1: Basic GET requests

# 1. Fetch your public IP:
curl -s https://httpbin.org/ip

# 2. Get just the status code of google.com:
curl -s -o /dev/null -w "%{http_code}\n" https://www.google.com

# 3. Get the headers of github.com:
curl -I https://github.com
Expected results
# 1. Your IP in JSON:
{"origin": "your.ip.address"}

# 2. Status code (after redirects):
200

# 3. Response headers including:
HTTP/2 200
content-type: text/html; charset=utf-8
server: GitHub.com
...

Exercise 2: POST with JSON

Send a POST request to httpbin with your name and course:

curl -s -X POST https://httpbin.org/post \
     -H "Content-Type: application/json" \
     -d '{"name": "YOUR_NAME", "course": "TDS", "year": 2026}'
What should you see?

httpbin echoes back your request. Look for the json field in the response:

{
  "json": {
    "name": "YOUR_NAME",
    "course": "TDS",
    "year": 2026
  },
  ...
}

This confirms the server received your JSON data correctly.


Exercise 3: Headers and authentication

# 1. Send a custom header:
curl -s https://httpbin.org/headers \
     -H "X-Student-Name: Alice" \
     -H "X-Course: TDS" | jq '.headers'

# 2. Test basic auth (username: testuser, password: testpass):
curl -s -u "testuser:testpass" https://httpbin.org/basic-auth/testuser/testpass

# 3. Test with WRONG password — what status code do you get?
curl -s -o /dev/null -w "%{http_code}\n" \
     -u "testuser:wrongpass" https://httpbin.org/basic-auth/testuser/testpass
Answers
  1. You’ll see your custom headers in the response:

    {"X-Course": "TDS", "X-Student-Name": "Alice", ...}
  2. Successful auth returns:

    {"authenticated": true, "user": "testuser"}
  3. Wrong password returns status code 401 (Unauthorized).


Exercise 4: Chain curl with jq

# Install jq if not present:
sudo apt install jq -y

# Fetch data and extract specific fields:
curl -s https://httpbin.org/get | jq '{ip: .origin, agent: .headers."User-Agent"}'
Expected output
{
  "ip": "your.ip.address",
  "agent": "curl/8.x.x"
}

jq extracted just the fields you asked for from the full response.


Exercise 5: MCQ

Q1: What does curl -s -o /dev/null -w "%{http_code}\n" URL output?

  • A) The full response body
  • B) The response headers
  • C) Just the HTTP status code
  • D) The URL itself
Answer

C — This clever combination:

  • -s = silent (no progress bar)
  • -o /dev/null = discard the response body
  • -w "%{http_code}\n" = print just the status code

Q2: Which flag makes curl show the full request AND response debug info?

  • A) -s
  • B) -v
  • C) -i
  • D) -I
Answer

B-v (verbose) shows the full request headers (lines starting with >) and response headers (lines starting with <), plus connection details.


Q3: You need to send {"action": "start"} as a POST request. Which curl command is correct?

  • A) curl -X GET url -d '{"action": "start"}'
  • B) curl url -H "Content-Type: application/json" -d '{"action": "start"}'
  • C) curl url -d "action=start"
  • D) curl -X POST url -d {"action": "start"}
Answer

B — This sets the Content-Type to JSON and sends the JSON body. -d implies POST automatically. Option D is missing quotes around the JSON.