curl_easy_perform(3)

πŸ’… curl_easy_perform β€” perform a blocking network transfer

okay bestie, so this is a page from the libcurl manual (that's the "Library Functions Manual" vibe, iykyk) and it's about curl_easy_perform(3) β€” the moment your code finally does the thing. Let's get into it 🫢


✨ SYNOPSIS ✨

like, the literal incantation:

#include <curl/curl.h>

CURLcode curl_easy_perform(CURL *easy_handle);

πŸ¦‹ DESCRIPTION πŸ¦‹

So curl_easy_perform(3) performs a network transfer in a blocking manner β€” meaning it sits there and waits until it's done, or bails earlier if it fails. It's giving "I'll be back when I'm back" energy. If you want non-blocking behavior instead, go look at curl_multi_perform(3) (different vibe entirely).

Timing matters, bestie: you invoke this function after curl_easy_init(3) and after all your curl_easy_setopt(3) calls are made. Then it performs the transfer as described in the options. And here's the thing β€” it must be called with the same easy_handle as input that the curl_easy_init(3) call returned. No swapping handles mid-relationship πŸ”₯

You can do any amount of calls to curl_easy_perform(3) while using the same easy_handle. Honestly, if you intend to transfer more than one file, you're even encouraged to do so β€” slay. libcurl attempts to reuse existing connections for the following transfers, thus making the operations faster, less CPU intense and using less network resources. Iconic behavior, no cap. You probably want to use curl_easy_setopt(3) between the invokes to set options for the following curl_easy_perform(3) call.

⚠️ Now the important warning, and I mean this fr fr: you must never call this function simultaneously from two places using the same easy_handle. Let the function return first before invoking it another time. That's not a suggestion, that's the rule. If you want parallel transfers, you must use several curl easy handles. Periodt. πŸ’€

A network transfer moves data to a peer or from a peer. An application tells libcurl how to receive data by setting the CURLOPT_WRITEFUNCTION(3) and CURLOPT_WRITEDATA(3) options. To tell libcurl what data to send, there are a few more alternatives but two common ones are CURLOPT_READFUNCTION(3) and CURLOPT_POSTFIELDS(3). So cute, so functional πŸŽ€

And one more lowkey gotcha: while the easy_handle is added to a multi handle, it cannot be used by curl_easy_perform(3). Not available, booked, busy. πŸ™ˆ


🌈 PROTOCOLS 🌈

This functionality affects all supported protocols. Every single one. She contains multitudes ✨


πŸ“ EXAMPLE πŸ“

here's the whole little main character moment:

int main(void)
{
  CURL *curl = curl_easy_init();
  if(curl) {
    CURLcode result;
    curl_easy_setopt(curl, CURLOPT_URL, "https://example.com");
    result = curl_easy_perform(curl);
    curl_easy_cleanup(curl);
  }
}

init, setopt, perform, cleanup. The full ritual. Ate. πŸ’…


🧚 AVAILABILITY 🧚

Added in curl 7.1 β€” she's been around, she's a legend πŸ‘‘


⭐ RETURN VALUE ⭐

This function returns a CURLcode indicating success or error.

  • CURLE_OK (0) means everything was OK 😌
  • non-zero means an error occurred β€” see libcurl-errors(3) for the deets

Also, if CURLOPT_ERRORBUFFER(3) was set with curl_easy_setopt(3), there can be an error message stored in the error buffer when non-zero is returned. So you get a little note explaining what went wrong, which is honestly so thoughtful 🫢


πŸ’œ SEE ALSO πŸ’œ

the whole friend group, go say hi 🌸


🌟 that's the page, bestie β€” from the libcurl crew, dated 2026-10-12, tagging itself curl_easy_perform(3). Go forth and perform (blockingly) πŸ’–
πŸ“¦ where this page comes from