curl build.
Click the request together — method, URL, headers, body, auth, common flags — and copy a fully-quoted shell-safe one-liner. Round-trips back into a multi-line view for documentation. No request is actually sent; this is a composition tool.
curl -L --compressed -H 'Accept: application/json' -H 'Authorization: Bearer ' https://api.example.com/users
curl -L \ --compressed \ -H 'Accept: application/json' \ -H 'Authorization: Bearer ' https://api.example.com/users
Daniel Stenberg's 35-year project.
curl began life in 1996 as httpget, a tiny tool Daniel Stenberg wrote to fetch currency exchange rates for an IRC bot. Renamed urlget, then curl, the project added FTP, then HTTPS, then dozens of other protocols. Today curl supports HTTP, HTTPS, FTP, FTPS, GOPHER, GOPHERS, DICT, FILE, IMAP, IMAPS, LDAP, LDAPS, MQTT, POP3, POP3S, RTMP, RTMPS, RTSP, SCP, SFTP, SMB, SMBS, SMTP, SMTPS, TELNET, TFTP, WS, and WSS. The underlying library, libcurl, is embedded in essentially every major operating system, application framework, IoT device, and network appliance — Stenberg estimates curl runs in over ten billion installations.
The reason curl became universal is that it was always pragmatic about HTTP. While Mozilla and Microsoft fought over standards in the late 1990s, curl just shipped. It accepted broken servers, broken proxies, broken certificates, broken redirects — and it accepted them with command-line flags so you could see exactly which workarounds were in play. By the time HTTP/2 and HTTP/3 needed a reference client, curl had been the de facto reference for two decades.
The project is funded primarily by Stenberg's day job (he worked at Mozilla, then wolfSSL) and contributions from a small set of corporate sponsors. Stenberg responds personally to roughly half of all GitHub issues. The project's transparency about its own bus factor and security posture has made it a model for open-source maintenance — read the annual security retrospectives at curl.se for a master class in operating a foundational tool.
Twenty flags cover most use.
curl ships with hundreds of options, but daily use settles on a small set. -X METHOD sets the HTTP method (GET is implicit). -H 'Header: value' adds a request header; repeat for each. -d 'body' sends a body; --data-raw, --data-binary, and --data-urlencode are the strict variants. -i includes response headers in output; -I sends HEAD and shows only headers. -L follows redirects (curl will not follow by default — surprise to many). -s silences the progress meter; -S re-enables errors when silenced.
-u user:pass sends Basic auth (avoid in shell history; use .netrc instead). -k disables TLS verification — useful for staging, dangerous as a habit. --http2, --http3 force a protocol version; otherwise curl negotiates. -4 and -6 force IPv4 / IPv6. -w '%{http_code}\n' writes a custom output template; combined with -s -o /dev/null, this is the standard latency probe pattern.
-v verbose mode shows request and response headers and TLS handshake details. For really deep debugging, --trace and --trace-ascii dump the entire wire transcript. -o file writes response to file; -O uses the URL's filename. --resolve host:port:ip overrides DNS for testing — invaluable for testing how a service responds before pointing real DNS at it. The full reference at curl.se/docs/manpage.html is comprehensive but the commands above cover ~95% of real-world use.
The single hardest part of using curl.
More than half of curl bugs reported by users are actually shell-quoting bugs. The shell parses the command line before curl sees any of it; quoting determines what becomes one argument vs many, what gets variable-expanded, and what passes through literally. Single quotes preserve everything literally except a single quote itself; double quotes interpolate $vars, backticks, and $(); backslash escapes the next character.
For most curl invocations, single quotes around each argument is the safest default. The builder on this page emits single quotes and escapes embedded single quotes via the POSIX '\'' idiom (close quote, escape quote, open quote). On Windows PowerShell, single and double quotes have different rules; the safe move is to invoke curl.exe with @'…'@ here-string syntax, or to use Invoke-RestMethod natively.
For JSON bodies specifically, the trick is to put the entire JSON body inside single quotes, which preserves all the double quotes and escapes inside. The classic mistake is using double quotes around a JSON body — the shell then interprets the inner double quotes, mangling the JSON. If your JSON contains a literal single quote (rare), use the heredoc form: curl --data @body.json … with the body in a separate file.
When to reach for HTTPie or xh.
curl is the universal lowest-common-denominator. Two contemporary alternatives optimise for different workflows. HTTPie (Jakub Roztocil, 2012) renames the verbs and uses a more discoverable syntax: http POST api.example.com/users name=ada role=admin sends a JSON body with two fields. Output is colourised JSON by default. The trade-off is that HTTPie is not installed by default everywhere and the syntax is different enough that copying examples between curl-land and HTTPie-land requires manual translation.
xh (Mehrad Sadeghi, 2020) is a Rust-written HTTPie clone with the same syntax but native binary, no Python dependency, and dramatically faster startup. Default for command-line API exploration on machines where you can install one extra tool. For terminal-heavy workflows, the ergonomics of HTTPie/xh beat curl.
For testing-suite use, curl scripts have been replaced by tools that run in CI: hurl (Orange Labs, 2020) takes plain-text request files and runs assertions, all from a single binary; bruno (Anoop M D, 2023) is a Postman alternative that stores collections as plain-text files in version control. Postman remains the dominant interactive tool but its team-collaboration features have aged poorly compared to git-native alternatives. For one-off requests in a terminal: curl. For repeatable test suites: hurl or bruno. For interactive exploration: HTTPie or Bruno's GUI.
That you probably didn't know curl could do.
curl can speak many protocols beyond HTTP. curl smtp://mail.example.com --mail-from sender@example.com --mail-rcpt recv@example.com -T body.txt sends an email. curl mqtt://broker.example.com -d 'hello' --mqtt-topic test publishes to MQTT. curl ws://echo.websocket.org opens a WebSocket connection (since curl 7.86, October 2022). curl sftp://user:pass@host/file transfers via SSH.
curl supports parallel transfers natively. curl -Z url1 url2 url3 downloads three URLs in parallel using HTTP/2 connection coalescing where possible. --parallel-max 50 caps concurrency; --parallel-immediate starts the next transfer as soon as one completes rather than waiting for the batch.
curl is also a useful network diagnostic. curl -w '@curl-format.txt' with a format file printing namelookup, connect, appconnect, pretransfer, redirect, starttransfer, and total times produces a per-stage latency breakdown — the same information browser DevTools shows graphically. For TLS-specific debugging, curl -v --tls-max 1.2 … forces a specific TLS version; --ciphers restricts the cipher suite list, useful for testing what a server actually accepts.
For developer workflows, curl integrates with everything. Browser DevTools' "Copy as cURL" feature exports the actual request the browser made, including all cookies, headers, and credentials — paste into a terminal to replay. Postman's import accepts curl commands. The curlconverter npm package turns curl commands into Python, JavaScript, Go, Rust, and a dozen other languages — invaluable for moving from "curl works" to "ship it as code."