ZetCode

Linux curl Command

last modified October 23, 2026

The curl command in Linux transfers data to and from servers using URLs. It supports many protocols, including HTTP, HTTPS, FTP, and SFTP, and is the standard tool for downloading files, inspecting HTTP responses, and testing web APIs from the command line.

curl handles custom headers, form and JSON data, file uploads, authentication, and cookies. The examples use the httpbin.org service, which echoes back the details of a request, and example.com. The jq tool is used to pick fields from the JSON responses.

Basic usage

In its simplest form, curl sends a GET request and writes the response body to standard output.

curl https://httpbin.org/json

The JSON document returned by the server is printed on the terminal. If no protocol is given in the URL, curl assumes HTTP.

$ curl https://httpbin.org/json
{
  "slideshow": {
    "author": "Yours Truly", 
    "date": "date of publication", 
    "slides": [
      {
        "title": "Wake up to WonderWidgets!", 
        "type": "all"
      }, 
      {
        "items": [
          "Why <em>WonderWidgets</em> are great", 
          "Who <em>buys</em> WonderWidgets"
        ], 
        "title": "Overview", 
        "type": "all"
      }
    ], 
    "title": "Sample Slide Show"
  }
}

Progress meter and silent mode

When the output goes to a file or a pipe, curl shows a progress meter on standard error. The -s option disables it.

curl -o page.html https://example.com
curl -s https://example.com | grep -o "<title>.*</title>"
curl -s https://nonexistent.example; echo "exit: $?"
curl -sS https://nonexistent.example

The meter shows the size, speed, and elapsed time of the transfer. Silent mode also hides error messages, so a failure is visible only through the exit status; code 6 means that the host could not be resolved. Adding -S brings error messages back, which makes -sS a good default for scripts.

$ curl -o page.html https://example.com
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   577    0   577    0     0   3012      0 --:--:-- --:--:-- --:--:--  3020
$ curl -s https://example.com | grep -o "<title>.*</title>"
<title>Example Domain</title>
$ curl -s https://nonexistent.example; echo "exit: $?"
exit: 6
$ curl -sS https://nonexistent.example
curl: (6) Could not resolve host: nonexistent.example

Saving to a file

The -o option saves the response to a given file, while -O uses the file name from the URL.

curl -s -O https://httpbin.org/robots.txt

The file robots.txt is saved in the current directory, next to page.html from the previous example. For URLs that do not end with a file name, use -o. Combined with -L and -C -, curl can also follow redirects to the actual file and resume an interrupted download.

$ curl -s -O https://httpbin.org/robots.txt
$ ls -l page.html robots.txt
-rw-rw-r-- 1 jano jano 577 Oct 23 10:44 page.html
-rw-rw-r-- 1 jano jano  30 Oct 23 10:44 robots.txt
$ cat robots.txt
User-agent: *
Disallow: /deny

Response headers

The -I option sends a HEAD request and prints only the response headers.

curl -I https://example.com

The headers contain the status line, content type, caching information, and the server software. To print the headers together with the body of a normal request, use -i. The -v option shows the full conversation, including the request headers and TLS details.

$ curl -I https://example.com
HTTP/2 200 
date: Fri, 23 Oct 2026 08:44:34 GMT
content-type: text/html; charset=utf-8
server: cloudflare
last-modified: Wed, 21 Oct 2026 20:09:25 GMT
allow: GET, HEAD
accept-ranges: bytes
age: 10863
cf-cache-status: HIT
cf-ray: a4ef75eb2e41c258-BTS
alt-svc: h3=":443"; ma=86400

Following redirects

By default, curl does not follow redirects. The -L option makes it follow the Location header.

curl -sI https://httpbin.org/redirect/2
curl -sL https://httpbin.org/redirect/2 | jq .url

The first request returns a 302 status with the next location. With -L, curl follows both redirects and receives the final response from /get.

$ curl -sI https://httpbin.org/redirect/2
HTTP/2 302 
date: Fri, 23 Oct 2026 08:44:35 GMT
content-type: text/html; charset=utf-8
content-length: 247
server: gunicorn/19.9.0
location: /relative-redirect/1
access-control-allow-origin: *
access-control-allow-credentials: true

$ curl -sL https://httpbin.org/redirect/2 | jq .url
"https://httpbin.org/get"

Custom headers

The -H option adds or replaces a request header. It can be repeated.

curl -s -H 'Accept: application/json' -H 'X-Request-Id: 42' https://httpbin.org/headers
curl -s -A 'my-client/1.0' https://httpbin.org/user-agent

The server echoes the headers it received; we remove a tracing header added by the httpbin infrastructure. The -A option is a shortcut for setting the User-Agent header.

$ curl -s -H 'Accept: application/json' -H 'X-Request-Id: 42' https://httpbin.org/headers | jq '.headers | del(."X-Amzn-Trace-Id")'
{
  "Accept": "application/json",
  "Host": "httpbin.org",
  "User-Agent": "curl/7.81.0"
}
$ curl -s -A 'my-client/1.0' https://httpbin.org/user-agent
{
  "user-agent": "my-client/1.0"
}

Query parameters

The -G option appends the data given with -d or --data-urlencode to the URL as a query string.

curl -s -G -d lang=en --data-urlencode 'city=New York' https://httpbin.org/get

The --data-urlencode option encodes special characters such as spaces, which plain -d does not do. Without encoding, a value like John Doe would produce an invalid URL.

$ curl -s -G -d lang=en --data-urlencode 'city=New York' https://httpbin.org/get | jq '{args, url}'
{
  "args": {
    "city": "New York",
    "lang": "en"
  },
  "url": "https://httpbin.org/get?lang=en&city=New+York"
}

Sending POST data

The -d option sends data in the request body. It switches the method to POST and, by default, uses the form content type.

curl -s -d 'name=John&occupation=gardener' https://httpbin.org/post
curl -s -X POST -H 'Content-Type: application/json' -d '{"name": "John", "age": 34}' https://httpbin.org/post

The first command submits form data. The second one sends a JSON document; the Content-Type header tells the server how to parse it. The -X POST option is redundant here but makes the intention explicit. Since version 7.82, curl provides the --json option, which sets the data and the JSON headers at once. With -d @file.json, the data is read from a file.

$ curl -s -d 'name=John&occupation=gardener' https://httpbin.org/post | jq .form
{
  "name": "John",
  "occupation": "gardener"
}
$ curl -s -X POST -H 'Content-Type: application/json' -d '{"name": "John", "age": 34}' https://httpbin.org/post | jq .json
{
  "age": 34,
  "name": "John"
}

Uploading files

The -F option submits a multipart form, as a browser does for forms with file inputs.

curl -s -F 'file=@notes.txt' -F 'note=report' https://httpbin.org/post

The @ prefix tells curl to upload the contents of notes.txt as a file field. Fields without the prefix are sent as regular form values.

$ echo "sample data" > notes.txt
$ curl -s -F 'file=@notes.txt' -F 'note=report' https://httpbin.org/post | jq '{files, form}'
{
  "files": {
    "file": "sample data\n"
  },
  "form": {
    "note": "report"
  }
}

Other HTTP methods

The -X option sets the request method, which is needed for REST APIs.

curl -s -X PUT -d 'status=done' https://httpbin.org/put
curl -s -o /dev/null -w '%{http_code}\n' -X DELETE https://httpbin.org/delete

The first command sends a PUT request with form data. The second one sends a DELETE request, discards the body, and prints only the status code. Do not use -X HEAD; use -I instead.

$ curl -s -X PUT -d 'status=done' https://httpbin.org/put | jq .form
{
  "status": "done"
}
$ curl -s -o /dev/null -w '%{http_code}\n' -X DELETE https://httpbin.org/delete
200

Basic authentication

The -u option sends a user name and password with HTTP basic authentication.

curl -s -u john:secret https://httpbin.org/basic-auth/john/secret

The correct credentials are accepted, and wrong ones lead to status 401. If only the user name is given, curl prompts for the password, which keeps it out of the shell history. Bearer tokens are sent with a header, for example -H "Authorization: Bearer $TOKEN".

$ curl -s -u john:secret https://httpbin.org/basic-auth/john/secret
{
  "authenticated": true, 
  "user": "john"
}
$ curl -s -o /dev/null -w '%{http_code}\n' -u john:wrong https://httpbin.org/basic-auth/john/secret
401

Cookies

The -c option saves cookies received from the server to a file, and -b sends cookies from a file or a string.

curl -s -c cookies.txt https://httpbin.org/cookies/set/session/abc123 -o /dev/null
curl -s -b cookies.txt https://httpbin.org/cookies
curl -s -b 'theme=dark' https://httpbin.org/cookies

The first request stores the session cookie in a cookie jar in the Netscape format. The second request sends it back, which is how a login session is kept across several curl calls. The last command sends a cookie given directly on the command line.

$ curl -s -c cookies.txt https://httpbin.org/cookies/set/session/abc123 -o /dev/null
$ grep session cookies.txt
httpbin.org	FALSE	/	FALSE	0	session	abc123
$ curl -s -b cookies.txt https://httpbin.org/cookies
{
  "cookies": {
    "session": "abc123"
  }
}
$ curl -s -b 'theme=dark' https://httpbin.org/cookies
{
  "cookies": {
    "theme": "dark"
  }
}

Writing out transfer details

The -w option prints information about the completed transfer using variables.

curl -s -o /dev/null -w 'status: %{http_code}\nsize: %{size_download} bytes\ntype: %{content_type}\n' https://example.com

We discard the body and print the status code, the number of downloaded bytes, and the content type. Other useful variables include %{time_total} and %{url_effective}, which makes -w handy for simple monitoring.

$ curl -s -o /dev/null -w 'status: %{http_code}\nsize: %{size_download} bytes\ntype: %{content_type}\n' https://example.com
status: 200
size: 577 bytes
type: text/html; charset=utf-8

Failing on HTTP errors

By default, curl considers any HTTP response a success, even 404 or 500. The -f option makes it fail on HTTP errors.

curl -s https://httpbin.org/status/404; echo "exit: $?"
curl -sf https://httpbin.org/status/404; echo "exit: $?"
curl -sSf https://httpbin.org/status/500

Without -f, the exit status is 0; with it, curl exits with code 22 and prints no body. Use -f in scripts so that failed requests are detected. The --retry option repeats requests that fail with transient errors.

$ curl -s https://httpbin.org/status/404; echo "exit: $?"
exit: 0
$ curl -sf https://httpbin.org/status/404; echo "exit: $?"
exit: 22
$ curl -sSf https://httpbin.org/status/500
curl: (22) The requested URL returned error: 500

Best practices for curl

See also

For working with web APIs interactively, the xh command is a friendlier alternative: it builds JSON bodies, headers, and query parameters from short key=value items, formats the responses, and fails on HTTP errors by default. Its --curl option translates a request into the equivalent curl command.

Source

curl manual

In this article, we have used the curl command to download files, inspect headers, follow redirects, send query parameters, form and JSON data, upload files, authenticate, work with cookies, and handle errors.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Linux tutorials.