Using curl to Make REST API Requests

Sooner or later, you will need to talk to an API without writing any application code. You may want to see what an endpoint returns, reproduce a bug someone reported, or test a token before you put it into a script. curl is the quickest way to do that from the terminal.
A REST API exposes resources over HTTP and usually returns JSON. REST stands for representational state transfer, an architectural style built on a set of constraints for web services. You use the standard HTTP methods to create, view, update, or delete those resources.
Every API request is made up of four parts:
- Endpoint: The URL the client uses to communicate with the server.
- HTTP method: Tells the server what action the client wants to perform. The most common methods are
GET,POST,PUT,PATCH, andDELETE. - Headers: Pass additional information between the client and server, such as authentication tokens and content type.
- Body: The data sent to the server.
This guide explains how to use curl
to interact with RESTful APIs. curl is a command-line utility for transferring data from or to a remote server. It is installed by default on macOS and most Linux distributions.
curl Options
The syntax of the curl command is:
curl [options] [URL...]The options used in this guide are:
-X,--request- The HTTP method to use.-i,--include- Include the response headers in the output.-sS,--silent --show-error- Hide progress output but still show errors.--fail-with-body- Return a non-zero exit code on HTTP errors while still printing the response body.-d,--data- The data to send with the request.-G,--get- Send the-dor--data-urlencodedata as query parameters in a GET request.--data-urlencode- URL-encode the data before sending it.-H,--header- An additional header to send.-u,--user- The username and password for Basic authentication.-w,--write-out- Print details about the transfer, such as the status code, after it completes.
The examples use JSONPlaceholder , a free fake API for testing. It accepts every write request and returns a realistic response, but nothing is actually saved on the server, so you can run each command as many times as you like.
HTTP GET
The GET method requests a resource from the server. GET is the default method when making HTTP requests with curl, so you do not need -X.
The following example returns a JSON list of all posts:
curl https://jsonplaceholder.typicode.com/postsTo filter results, append query parameters to the URL. Wrap the URL in quotes, because shells treat ? and & as special characters. In zsh, the default shell on macOS, an unquoted ? fails with no matches found:
curl "https://jsonplaceholder.typicode.com/posts?userId=1"[
{
"userId": 1,
"id": 1,
"title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
"body": "quia et suscipit\nsuscipit recusandae consequuntur expedita..."
},
...
]The server returns only the posts that belong to user 1.
When a parameter value contains spaces or other special characters, let curl build the query string for you. The -G option appends the --data-urlencode data to the URL instead of sending it in the body:
curl -G --data-urlencode "title=qui est esse" \
https://jsonplaceholder.typicode.com/postscurl sends the request as GET /posts?title=qui+est+esse, and the API returns the single post with that title.
Many APIs can respond in more than one format. To ask for JSON explicitly, set the Accept header:
curl -H "Accept: application/json" https://jsonplaceholder.typicode.com/posts/1HTTP POST
The POST method sends data to the server to create a new resource.
The following command sends a POST request with form-encoded data using the -d option:
curl -X POST -d "userId=5&title=Hello World&body=Post body." \
https://jsonplaceholder.typicode.com/posts{
"userId": "5",
"title": "Hello World",
"body": "Post body.",
"id": 101
}The API created a new post and assigned it the ID 101. Notice that userId came back as the string "5". Form data has no types, so every value arrives as text.
By default, when -d is used without a Content-Type header, curl sends Content-Type: application/x-www-form-urlencoded. Strictly speaking, -X POST is redundant here because -d already switches the method to POST, but many people keep it for readability.
To send JSON data, set the Content-Type header to application/json:
curl -X POST -H "Content-Type: application/json" \
-d '{"userId": 5, "title": "Hello World", "body": "Post body."}' \
https://jsonplaceholder.typicode.com/posts{
"userId": 5,
"title": "Hello World",
"body": "Post body.",
"id": 101
}This time userId is a number, because the server parsed the body as JSON. Most REST APIs expect JSON, so this is the form you will use most often.
For more examples, including the --json shorthand, see How to Use curl to Make POST Requests
.
HTTP PUT
The PUT method replaces a resource on the server with the data provided in the request body. Send the complete resource, because many APIs reset any field you leave out to an empty or default value.
The following command replaces the post with ID 5:
curl -X PUT -H "Content-Type: application/json" \
-d '{"userId": 5, "title": "Hello World", "body": "Post body."}' \
https://jsonplaceholder.typicode.com/posts/5{
"userId": 5,
"title": "Hello World",
"body": "Post body.",
"id": 5
}The response contains exactly the fields we sent, plus the id from the URL.
HTTP PATCH
The PATCH method applies partial updates to a resource. Unlike PUT, it only updates the fields included in the request body and leaves all other fields unchanged.
To change only the title of post 5, send just that field:
curl -X PATCH -H "Content-Type: application/json" \
-d '{"title": "Hello Universe"}' \
https://jsonplaceholder.typicode.com/posts/5{
"userId": 1,
"id": 5,
"title": "Hello Universe",
"body": "repudiandae veniam quaerat sunt sed..."
}The title changed, while userId and body kept their original values.
HTTP DELETE
The DELETE method removes the specified resource from the server. There is no confirmation prompt, so double-check the URL before you run it against a real API:
curl -X DELETE https://jsonplaceholder.typicode.com/posts/5JSONPlaceholder returns an empty JSON object ({}) and a 200 status code. Other APIs often reply with 204 No Content, which indicates success without a response body. An empty body alone does not establish success, so check the status code to confirm.
Authentication
Most APIs require authentication. curl supports several common authentication methods.
Bearer Token
Bearer token authentication is used by OAuth 2.0 APIs. Pass the token in the Authorization header:
curl -H "Authorization: Bearer {ACCESS_TOKEN}" https://api.example.com/dataBasic Authentication
For APIs that use HTTP Basic authentication, pass the username and password with the -u option:
curl -u username:password https://api.example.com/datacurl encodes the credentials and sends them in the Authorization header automatically. If you pass only the username (-u username), curl prompts for the password, which keeps it out of your shell history.
API Key
Some APIs accept an API key as a custom header:
curl -H "X-API-Key: {API_KEY}" https://api.example.com/dataOther APIs expect the key as a query parameter:
curl "https://api.example.com/data?api_key={API_KEY}"curl -H "Authorization: Bearer $API_TOKEN" ..., and load secrets from a .env file that is listed in .gitignore. Never commit credentials to version control.Inspecting Responses
Use the -i flag to include the HTTP response status line and headers in the output:
curl -i https://jsonplaceholder.typicode.com/posts/1HTTP/2 200
content-type: application/json; charset=utf-8
...
{
"userId": 1,
"id": 1,
"title": "sunt aut facere...",
"body": "quia et suscipit..."
}The first line shows the protocol and the status code, followed by the response headers, a blank line, and the body.
To pretty-print a JSON response, pipe the output to python3:
curl -s https://jsonplaceholder.typicode.com/posts/1 | python3 -m json.toolIf you have jq installed, it provides color-highlighted formatted output:
curl -s https://jsonplaceholder.typicode.com/posts/1 | jqChecking the Status Code
In scripts, you often care only about whether the request succeeded. The -w option prints selected details after the transfer, and %{http_code} holds the response status:
curl -s -o /dev/null -w '%{http_code}\n' https://jsonplaceholder.typicode.com/posts/1200Here -s hides the progress meter and -o /dev/null discards the body, so the status code is the only thing printed. Request a post that does not exist, such as /posts/9999, and the same command prints 404.
By default, curl exits with status 0 even when the server returns an error such as 404 or 500. For automation scripts and CI checks, add --fail-with-body to make those failures explicit:
curl -sS --fail-with-body https://jsonplaceholder.typicode.com/posts/9999curl: (22) The requested URL returned error: 404
{}curl now exits with code 22 and still prints the error body, which often explains what went wrong. The --fail-with-body option requires curl 7.76.0 or newer. On older systems such as RHEL 8, use --fail instead, which fails the same way but discards the body.
When you need to see everything, including the request headers curl sends, use -v for verbose output.
Quick Reference
For a printable quick reference, see the curl cheatsheet .
| Command | Description |
|---|---|
curl URL | GET request |
curl -G --data-urlencode "key=a b" URL | GET with encoded query parameters |
curl -X POST -d "data" URL | POST with form data |
curl -X POST -H "Content-Type: application/json" -d '{}' URL | POST with JSON body |
curl -X PUT -H "Content-Type: application/json" -d '{}' URL | PUT (full replace) |
curl -X PATCH -H "Content-Type: application/json" -d '{}' URL | PATCH (partial update) |
curl -X DELETE URL | DELETE request |
curl -H "Authorization: Bearer TOKEN" URL | Bearer token auth |
curl -u user:pass URL | Basic auth |
curl -i URL | Include response headers |
curl -s -o /dev/null -w '%{http_code}\n' URL | Print only the status code |
curl -sS --fail-with-body URL | Fail on HTTP errors and print error body |
curl -s URL | python3 -m json.tool | Pretty-print JSON response |
Troubleshooting
SSL certificate problem: unable to get local issuer certificate
The server is using a self-signed or untrusted certificate. In a controlled test environment where you trust the host, add -k to skip certificate verification. Do not use -k against untrusted or production servers.
401 Unauthorized or 403 Forbidden
Your authentication credentials are missing, incorrect, or expired. Verify the token or API key and check the API documentation for the correct header name and format.
Connection refused
The server is not reachable on the specified host and port. Confirm the endpoint URL is correct and that the server is running.
Response is not valid JSON
The server may be returning an error page in HTML. Use -i to inspect the full response including the status code and headers to diagnose the issue.
zsh: no matches found
The URL contains ? or * and is not quoted, so zsh tries to expand it as a filename pattern. Wrap the URL in double quotes.
curl: (6) Could not resolve host
The hostname cannot be resolved. Check the URL for typos and verify the host is reachable.
FAQ
What is the difference between PUT and PATCH?
PUT replaces the entire resource with the data in the request body. Send the complete resource, because the API determines how omitted fields are handled, and many APIs reset them to empty or default values. PATCH applies partial updates. Use PATCH when you want to change specific fields without replacing the entire resource.
Do I always need to specify -X GET?
No. GET is the default method in curl. Options such as -d select POST automatically. Use -X to explicitly select methods such as PUT, PATCH, or DELETE.
How do I send multiple headers?
Repeat the -H flag for each header:
curl -H "Authorization: Bearer TOKEN" -H "Accept: application/json" URLHow do I save the API response to a file?
Use the -o flag to write the output to a file:
curl -o response.json https://jsonplaceholder.typicode.com/posts/1Conclusion
curl handles every step of working with a REST API, from a quick GET to an authenticated PATCH. When a request does not behave as expected, start with -i or -w '%{http_code}' to see what the server actually returned.
For more curl examples and options, see the curl Command in Linux
guide.
Tags
Linuxize Weekly Newsletter
A quick weekly roundup of new tutorials, news, and tips.
About the authors

Dejan Panovski
Dejan Panovski is the founder of Linuxize, an RHCSA-certified Linux system administrator and DevOps engineer based in Skopje, Macedonia. Author of 1000+ Linux tutorials with 20+ years of experience turning complex Linux tasks into clear, reliable guides.
View author page