Skip to content(if available)orjump to list(if available)

Just Use Curl

Just Use Curl

119 comments

·October 21, 2025

hypeatei

There's also hurl: https://hurl.dev

You define all your requests in a plaintext format and can inject variables etc... plus the name is kinda funny.

anotherhue

Emacs had something like this long ago (of course it did).

https://github.com/pashky/restclient.el

I also like httpie but they seem to have gone commercial.

blueflow

Basically a shell script with a curl invocation in it, except you need to install extra software to execute it. Misses the point of the article.

hypeatei

I mean, the article did say:

  It doesn't need to render a fucking Chromium instance to make a web request. It doesn't depend on a service to run. It doesn't require an "Enterprise" subscription for basic features.
So I'd say it meets all of the criteria except being on your machine already.

millerm

Yup. Article mentioned `jq` as well. That's an external tool (and so is cURL if we are all being honest here).

bravetraveler

Their words, not mine; the first header: "It's already on your machine". We can belabor it, but the domain is 'justuse'. No room for 'except' [unless you're reasonable, of course].

The 'egregious' things are charging to share what will fit very well in SCM (preventing real automation)... and breaking due to Online First/only. It makes sense to require the endpoint I'm talking to. Why would Postman need AWS/us-east-1 [0] for a completely unrelated API? Joyful rent-seeking.

cURL, your suggestion (hurl), or HTTPie all make far more sense. Store what they need in the place where you already store stuff. Profit, for free: never go down. Automate/take people out of the button-pushing pattern for a gold star.

0: https://news.ycombinator.com/item?id=45645172

cube00

If you're able to use a curl release after March 2022, --json [1] is a great shortcut for REST API calls.

[1]: https://curl.se/docs/manpage.html#--json

KORraN

For the record, since version 7.82.0.

novoreorx

When I made the script httpstat [1] 9 years ago, I had this exact thought - if I want to show the statistics of a HTTP request, why not just use curl, why bother working out the figures myself? And since then, the more I use curl, the more I find it robust, sophesticated and irreplaciable. It's the only thing and everything I need.

[1]: https://github.com/reorx/httpstat

c0balt

> The UI/UX is perfect

While I like curl, this is highly subjective, some people just prefer a GUI that can guide you and/or be visually explored.

This whole piece also reads like someone is quite angry at people preferring a different workflow than them. Some aspects, like shell history, are also not the magic bullet they propose here as it doesn't, e. G., cover the actual responses.

Curl's ability to do almost everything is a minor curse here too as it means that any documentation (man pages, options help) is very large.

catapart

Just fyi: the writing style is a meme. I think it's following the "just use html"[0] format (though, that may be its own riff on the meme).

Not to detract from you point; just to say that the author is probably not as angry as this could make them seem.

[0] https://justfuckingusehtml.com/

alt187

Thanks! I didn't know this gem, definitely saving it.

dlopes7

Not to mention saving headers, tokens, doing multiple requests using the results from the previous, etc.

This guy would say "just use bash" and ignore the average user experience.

andoando

You can do all that in a shell script though

dns_snek

Of course you can, but shell scripting really fucking sucks.

One moment you have a properly quoted JSON string, the next moment you have a list of arguments, oops you need to escape the value for this program to interpret it right, but another program needs it to be double-escaped, is that \, \\, or \\\\? What subset of shell scripting are we doing? Fish? modern Linux bash, macOS-compatible bash? The lowest common denominator? My head is spinning already!

If I want to script something I'm writing Python these days. I've lost too much sleep over all the "interesting" WTF situations you get yourself into with shell scripting. I've never used Hurl but it's been on my radar and I think that's probably the sweet spot for tasks like this.

skydhash

That’s mostly what I do when I need to interact with an API:

  _plz() {
    curl [rest of common args]
  }
Then:

  _plz GET endpoint

runjake

> This whole piece also reads like someone is quite angry at people

It’s a meme that originated with https://thebestmotherfucking.website/ or one like it.

taylodl

Curl and jq are plenty to get the job done. As the author points out, you can capture all your curl commands in scripts and then orchestrate with other scripts for testing purposes. On top of all the benefits the author has already mentioned, you get a boost if you're doing your development work in a VM, as I do. It's less you have to install, configure, and manage. Sure, that can be automated, but it's just more stuff you have to take care of and the longer you have to wait for a fresh VM to be ready.

runxiyu

Using -X POST is often wrong as it "changes the actual method string in the HTTP request [... and] does not change behavior accordingly" (Stenberg, 2015).

Although, it is correct for the article's mention of "Send POST requests"... just that typically people don't send POST requests out of the blue with no data.

kaoD

I think you misinterpret the text you're quoting (but it's hard to tell since you didn't include a link).

-X POST is not wrong, it's just superfluous when using other flags like -d where the method can be inferred.

POST requests are often sent with no data (anything that is not idempotent should, unless there's another verb that could fit better).

jmholla

Here's the article in question. [0] I think runxiyu is correct.

The author delves a bit more into the issue.

> One of most obvious problems is that if you also tell curl to follow HTTP redirects (using -L or --location), the -X option will also be used on the redirected-to requests which may not at all be what the server asks for and the user expected. Dropping the -X will make curl adhere to what the server asks for. And if you want to alter what method to use in a redirect, curl already have dedicated options for that named --post301, --post302 and --post303!

Per the man page (`man 1 curl`),

> The method string you set with -X, --request will be used for all requests, which if you for example use -L, --location may cause unintended side-effects when curl does not change request method according to the HTTP 30x response codes - and similar.

`-d` and `--data` will appropriately change the headers of their requests. Funnily, `--post301` and `--post302` which have a similar effect as `-X POST` are RFC 7231 compliant, browsers just don't do that. [2][3] This is so ubiquitous that the error codes 307 and 308 were added to support the original behavior of repeating the request verbatim at the target address. Compare the following:

    > nc -l -p 8080 -q 1 <<< $'HTTP/1.1 301 Moved Permanently\nLocation: http://localhost:8081\n\n' & nc -l -p 8081 -q 1 <<< $'HTTP/1.1 200 OK\nContent-Length: 0\n\n' & curl -L --data test localhost:8080; wait
    > nc -l -p 8080 -q 1 <<< $'HTTP/1.1 301 Moved Permanently\nLocation: http://localhost:8081\n\n' & nc -l -p 8081 -q 1 <<< $'HTTP/1.1 200 OK\nContent-Length: 0\n\n' & curl -X POST -L --data test localhost:8080; wait
    > nc -l -p 8080 -q 1 <<< $'HTTP/1.1 308 Permanent Redirect\nLocation: http://localhost:8081\n\n' & nc -l -p 8081 -q 1 <<< $'HTTP/1.1 200 OK\nContent-Length: 0\n\n' & curl -L --data test localhost:8080; wait
What happens here:

1. In the 301 case with just `--data`, the request turns into a GET request when sent to the redirect.

2. In the 301 case with `-X POST`, the request stays a `POST` request, but doesn't send any data to the redirect.

3. Finally, in the case where the server returns a 308, we see the POST request is kept and the data is resent.

To further expand slightly on a different thing that might surprise some people, the data options will automatically set the content type by adding the header, `Content-Type: application/x-www-form-urlencoded`, as if sending form data from a browser. This behvaior can be overridden with a manual `-H`, `--header` argument (e.g., `-H 'Content-Type: application/json`).

Edit: cube00 pointed out that newer versions of curl than mine have `--json` which will do that automatically. [4]

[0]: https://daniel.haxx.se/blog/2015/09/11/unnecessary-use-of-cu...

[1]: https://www.rfc-editor.org/rfc/rfc7231

[2]: https://evertpot.com/http/301-moved-permanently

[3]: https://evertpot.com/http/302-found

[4]: https://news.ycombinator.com/item?id=45655409

brap

After decades it should have been obvious by now that the vast majority of users prefer GUIs.

CaptainOfCoit

Yeah, hence why no one uses terminals for doing work with git, codex, claude-code, neovim and curl, everyone and their mother are using desktop GUIs for those things, clearly.

c-hendricks

Inversely, plenty of people use GUIs for all of those things.

CaptainOfCoit

Yes, I agree, and have no qualms with people who prefer GUIs. But to say that "majority of users (developers?)" prefer GUIs seems outlandish at best.

aitchnyu

I use Jetbrains git pull etc since they dont fail on modified files and other problems.

nailer

The ‘vast majority’ using GUIs as mentioned by the parent doesn’t preclude individuals using CLIs.

catapart

Yeeeep.

My response to this article is the same one I have to anyone out here screaming "it's so easy to just do it my way!": if it's so easy, then do it for me!

The ffmpeg fans are the loudest screechers I've found, in this area. They'll point to the trainwreck of a UX that is Handbrake as an example of GUI for terminal commands. And, look - command line utilities are great and Handbrake is a super good product that functions well and does more than I'd ever want it to. But neither of those things are the same thing as having good UX.

If it's so easy to compile a bunch of shell scripts and store them in a directory in a git repo, then package together a bunch of them that every dev would need, sprinkle in a few with placeholders that most devs would need (with some project-specific input), and then serve them to me in a composable GUI that, in real time, builds the command to be run in my shell. Let me watch the clicks edit the command, and then let me watch the "submit" execute the command. There's no surer way for me to learn exactly what commands I need than to see them all the time. And if I have to learn them (so that I can use them) BEFORE I've seen them - in context - a few times at least, then I'm going to have a much harder time remembering. UX, when done right, helps the user.

Put simply: if I can do everything I would be able to do with postman using curl, then I should also be able to wrap curl in a thin DearImgui window that is reactive to user input. And if it's as easy as the author says, their time would have probably been better spent just making the GUI wrapper app and presenting it as a way to get better with curl, rather than writing an edgelordy article about it.

pjc50

What's supposedly wrong with Handbrake UX?

To me it seems like the complexity is just irreducible. There's so many formats, so many bits and pieces that can go in a video stream, they're not very visualizable, and they have surprising edge case interactions. Not to mention there's a lot of "normally the program figures this out for you, but there's an option to override it if broken" knobs and dials.

skydhash

You can’t compose GUI unless you go the emacs and smalltalk route (MacOS AppleScript is a very poor example). The one thing with CLI tools is how easy to compose them and have something greater. Also more versatile than a GUI app.

Also they’re more stable than anything else. You can coast for decades on a script.

catapart

Anything you can do in a terminal can be done with what I suggested because what I suggested was literally using buttons to write a terminal command.

Aperocky

define "user"

people needing/using curl would have been a very distinct subset of users.

internet_points

TIL about jo, why did I not know about that ten years ago

    $ jo details[author]=Dickens details[age]=dead books="$(jo -a 'Oliver Twist' 'Great Expectations')"
    {"details":{"author":"Dickens","age":"dead"},"books":["Oliver Twist","Great Expectations"]}

zamadatix

Curl is great for running individual API calls, API clients are great for when you're actually working on the API or architecting an app with one. Not that there are things one can't do with API development from the CLI, but there is a lot more to API clients than that feature list (it doesn't even include anything around stuff one might want to do with openapi definitions!) and by the time you string it all together you get why people like the tool that did that for them instead.

null

[deleted]

eddywebs

This is a very funny, love it ! every developer needs to know basics like such, I hope this site continues.

garciasn

This person is my spirit animal. "You can remember 400 Kubernetes commands but not curl -x POST?"

That was exactly what I needed this morning.

jonathanberger

Does anyone have suggestions for when I need to use bearer auth and the token is super long?

With curl I end up finding the command becomes hard to read, even taking advantage of backslashes. With Postman, it tidily hides the token out of the way on a separate tab and gets out of my way.

humlex

What i do is assign the token to a variable. I typically copy the secret to my clipboard, and then use the pbpaste command in macos terminal when assigning it to avoid secrets in my command history.

KORraN

I love the second part of your tip, thank you.

miningape

this.

A while ago I was working on a DSL to solve this exact issue (env switching, http requests + chained requests e.g. to an auth server to retrieve a token) - but I haven't had the time recently, and I moved jobs to a GraphQL shop, so it feels a bit more pointless now :D

skydhash

I think curl can load headers or other data from a file. And you can always $(cat token.txt) in the cli.