CPR (C++ Requests)

C++ HTTP client inspired by Python Requests library. Provides modern C++17 API as wrapper around libcurl. Enables intuitive HTTP request construction with features including JSON processing, authentication, SSL configuration, and proxy support.

HTTP ClientC++libcurlSimpleAuthenticationAsynchronous

GitHub Overview

libcpr/cpr

C++ Requests: Curl for People, a spiritual port of Python Requests.

Stars7,160
Watchers130
Forks1,012
Created:March 31, 2015
Language:C++
License:Other

Topics

c-plus-pluscpphacktoberfesthttplibcurllibraryrequests

Star History

libcpr/cpr Star History
Data as of: 10/22/2025, 09:55 AM

Library

cpr

Overview

cpr is "C++ Requests: Curl for People" developed as a modern C++ HTTP client library inspired by Python's Requests library. Providing powerful libcurl functionality through a concise and intuitive API, it enables efficient HTTP communication implementation in C++. Comprehensive features including synchronous/asynchronous requests, comprehensive authentication systems, complete SSL/TLS support, multipart uploads, and proxy configuration meet enterprise-level HTTP communication requirements, establishing itself as the de facto standard HTTP library in C++.

Details

cpr 2025 edition continues to evolve as a modern HTTP client library based on C++17. Designed to abstract libcurl's complex API while maintaining full access to its powerful features, it provides an easy-to-use HTTP communication environment for C++ developers. Supporting diverse development patterns including session-based API, convenience functions, multi-request processing, and interceptor features. With easy integration via CMake FetchContent, cross-platform support, and continuous development, it is widely adopted in enterprise development as an HTTP communication solution that combines performance and maintainability.

Key Features

  • Python-like API: Intuitive and readable interface inspired by the Requests library
  • libcurl Wrapper: Access to powerful libcurl features through simple C++ API
  • Sync/Async Support: Both blocking and non-blocking request patterns
  • Comprehensive Authentication: Support for Basic, Digest, Bearer Token, and NTLM authentication
  • Session Management: Efficient connection pooling and configuration reuse
  • Multi-Request: Parallel execution support for multiple HTTP requests

Pros and Cons

Pros

  • Intuitive API design with low learning cost for C++ developers
  • Hides libcurl complexity while maintaining access to all features
  • Flexible implementation patterns with sync/async/multi-request support
  • Easy project integration via CMake FetchContent
  • Enterprise-level support with rich authentication methods and SSL/TLS configuration
  • Cross-platform support (Linux, Windows, macOS)

Cons

  • Build configuration complexity due to libcurl dependency
  • Requires C++17 or later, unavailable with older compilers
  • Requires understanding of build systems compared to Python Requests
  • Increased memory usage with large numbers of parallel requests
  • Environment constraints due to libcurl version requirements (>= 7.64.0)
  • More complex error handling compared to Python due to C++ characteristics

Reference Pages

Code Examples

Installation and Basic Setup

# Using FetchContent in CMakeLists.txt (Recommended)
include(FetchContent)
FetchContent_Declare(cpr 
    GIT_REPOSITORY https://github.com/libcpr/cpr.git
    GIT_TAG 1.10.5)  # Specify latest version
FetchContent_MakeAvailable(cpr)

# Link to target
target_link_libraries(your_target_name PRIVATE cpr::cpr)
# System installation (for use with find_package)
git clone https://github.com/libcpr/cpr.git
cd cpr && mkdir build && cd build
cmake .. -DCPR_USE_SYSTEM_CURL=ON
cmake --build . --parallel
sudo cmake --install .
// Basic include
#include <cpr/cpr.h>
#include <iostream>

// Version check
#include <cpr/cpr.h>
std::cout << "cpr version: " << CPR_VERSION << std::endl;

Basic Requests (GET/POST/PUT/DELETE)

#include <cpr/cpr.h>

// Basic GET request
cpr::Response r = cpr::Get(cpr::Url{"https://api.example.com/users"});
std::cout << r.status_code << std::endl;  // 200
std::cout << r.header["content-type"] << std::endl;  // application/json
std::cout << r.text << std::endl;  // Response body

// GET request with parameters
cpr::Response r = cpr::Get(
    cpr::Url{"https://api.example.com/users"},
    cpr::Parameters{{"page", "1"}, {"limit", "10"}, {"sort", "created_at"}}
);
std::cout << r.url << std::endl;  // Final URL with parameters

// POST request (sending JSON data)
cpr::Response r = cpr::Post(
    cpr::Url{"https://api.example.com/users"},
    cpr::Body{R"({"name": "John Doe", "email": "[email protected]", "age": 30})"},
    cpr::Header{{"Content-Type", "application/json"}, 
               {"Authorization", "Bearer your-token"}}
);

if (r.status_code == 201) {
    std::cout << "User created successfully: " << r.text << std::endl;
} else {
    std::cout << "Error: " << r.status_code << " - " << r.text << std::endl;
}

// POST request (sending form data)
cpr::Response r = cpr::Post(
    cpr::Url{"https://api.example.com/login"},
    cpr::Payload{{"username", "testuser"}, {"password", "secret123"}}
);

// PUT request (data update)
cpr::Response r = cpr::Put(
    cpr::Url{"https://api.example.com/users/123"},
    cpr::Body{R"({"name": "Jane Doe", "email": "[email protected]"})"},
    cpr::Header{{"Content-Type", "application/json"},
               {"Authorization", "Bearer your-token"}}
);

// DELETE request
cpr::Response r = cpr::Delete(
    cpr::Url{"https://api.example.com/users/123"},
    cpr::Header{{"Authorization", "Bearer your-token"}}
);

if (r.status_code == 204) {
    std::cout << "User deleted successfully" << std::endl;
}

// Detailed response attribute inspection
std::cout << "Status code: " << r.status_code << std::endl;
std::cout << "Reason: " << r.reason << std::endl;
std::cout << "Final URL: " << r.url << std::endl;
std::cout << "Elapsed time: " << r.elapsed << " seconds" << std::endl;
std::cout << "Uploaded bytes: " << r.uploaded_bytes << " bytes" << std::endl;
std::cout << "Downloaded bytes: " << r.downloaded_bytes << " bytes" << std::endl;

Authentication and Header Configuration (Advanced Configuration)

#include <cpr/cpr.h>

// Custom header configuration
cpr::Response r = cpr::Get(
    cpr::Url{"https://api.example.com/data"},
    cpr::Header{
        {"User-Agent", "MyApp/1.0 (cpr C++)"},
        {"Accept", "application/json"},
        {"Accept-Language", "en-US,ja-JP"},
        {"X-API-Version", "v2"},
        {"X-Request-ID", "req-12345"}
    }
);

// Basic authentication
cpr::Response r = cpr::Get(
    cpr::Url{"https://api.example.com/private"},
    cpr::Authentication{"username", "password", cpr::AuthMode::BASIC}
);

// Digest authentication
cpr::Response r = cpr::Get(
    cpr::Url{"https://api.example.com/digest"},
    cpr::Authentication{"username", "password", cpr::AuthMode::DIGEST}
);

// Bearer Token authentication (OAuth)
cpr::Response r = cpr::Get(
    cpr::Url{"https://api.example.com/protected"},
    cpr::Bearer{"your-jwt-access-token"}
);

// Conditional compilation based on libcurl version
#if CPR_LIBCURL_VERSION_NUM >= 0x073D00
    // Use Bearer class with newer libcurl
    cpr::Response r = cpr::Get(
        cpr::Url{"https://api.example.com/bearer"},
        cpr::Bearer{"ACCESS_TOKEN"}
    );
#else
    // Manually set header with older libcurl
    cpr::Response r = cpr::Get(
        cpr::Url{"https://api.example.com/bearer"},
        cpr::Header{{"Authorization", "Bearer ACCESS_TOKEN"}}
    );
#endif

// Timeout configuration
cpr::Response r = cpr::Get(
    cpr::Url{"https://api.example.com/slow"},
    cpr::Timeout{10000}  // 10 second timeout
);

// Proxy configuration
cpr::Response r = cpr::Get(
    cpr::Url{"https://api.example.com/data"},
    cpr::Proxies{{"http", "http://proxy.example.com:8080"},
                {"https", "http://proxy.example.com:8080"}}
);

// Authenticated proxy
cpr::Response r = cpr::Get(
    cpr::Url{"https://api.example.com/data"},
    cpr::Proxies{{"http", "http://proxy.example.com:8080"}},
    cpr::ProxyAuthentication{{"http", cpr::EncodedAuthentication{"user", "pass"}}}
);

// Cookie configuration
cpr::Response r = cpr::Get(
    cpr::Url{"https://api.example.com/user-data"},
    cpr::Cookies{{"session_id", "abc123"}, {"user_pref", "dark_mode"}}
);

Session Management and Error Handling

#include <cpr/cpr.h>
#include <iostream>

// Session-based API (efficient multiple requests)
void sessionExample() {
    cpr::Session session;
    session.SetUrl(cpr::Url{"https://api.example.com"});
    session.SetHeader(cpr::Header{
        {"User-Agent", "MyApp/1.0"},
        {"Accept", "application/json"}
    });
    session.SetAuthentication(cpr::Authentication{"user", "pass", cpr::AuthMode::BASIC});
    
    // Multiple requests with same session configuration
    cpr::Response users = session.Get(cpr::Url{"https://api.example.com/users"});
    cpr::Response posts = session.Get(cpr::Url{"https://api.example.com/posts"});
    
    // Change parameters and make request
    session.SetParameters(cpr::Parameters{{"page", "2"}});
    cpr::Response page2 = session.Get();
}

// Comprehensive error handling
cpr::Response safeRequest(const std::string& url) {
    cpr::Response r = cpr::Get(cpr::Url{url}, cpr::Timeout{5000});
    
    // Error checking
    if (r.error.code != cpr::ErrorCode::OK) {
        std::cerr << "Request error: " << r.error.message << std::endl;
        return r;
    }
    
    // Status code specific handling
    switch (r.status_code) {
        case 200:
            std::cout << "Success: " << r.text.substr(0, 100) << "..." << std::endl;
            break;
        case 401:
            std::cerr << "Authentication error: Please check your token" << std::endl;
            break;
        case 403:
            std::cerr << "Permission error: Access denied" << std::endl;
            break;
        case 404:
            std::cerr << "Not found: Resource does not exist" << std::endl;
            break;
        case 429:
            std::cerr << "Rate limit: Please wait before retrying" << std::endl;
            break;
        default:
            if (r.status_code >= 500) {
                std::cerr << "Server error: " << r.status_code << std::endl;
            } else {
                std::cerr << "Unexpected status: " << r.status_code << std::endl;
            }
    }
    
    return r;
}

// SSL/TLS configuration
void sslExample() {
    // Basic HTTPS (certificate verification enabled by default)
    cpr::Response r = cpr::Get(cpr::Url{"https://secure-api.example.com/data"});
    
    // Using client certificate
    cpr::SslOptions ssl_opts = cpr::Ssl(
        cpr::ssl::CertFile{"client.pem"},
        cpr::ssl::KeyFile{"client.key"}
    );
    cpr::Response r_cert = cpr::Get(
        cpr::Url{"https://secure-api.example.com/data"}, 
        ssl_opts
    );
    
    // Specify TLS version
    cpr::SslOptions tls_opts = cpr::Ssl(cpr::ssl::TLSv1_2{});
    cpr::Response r_tls = cpr::Get(
        cpr::Url{"https://secure-api.example.com/data"}, 
        tls_opts
    );
}

Asynchronous Requests and Multi-Requests

#include <cpr/cpr.h>
#include <future>
#include <vector>

// Basic asynchronous requests
void asyncExample() {
    // Asynchronous GET (using Future)
    cpr::AsyncResponse future_response = cpr::GetAsync(
        cpr::Url{"https://api.example.com/slow-endpoint"}
    );
    
    // Execute other processes
    std::cout << "Executing other processes..." << std::endl;
    
    // Get response (blocking)
    cpr::Response r = future_response.get();
    std::cout << "Async request completed: " << r.status_code << std::endl;
    
    // Callback-based asynchronous request
    auto callback_response = cpr::GetCallback(
        [](cpr::Response r) {
            std::cout << "Callback executed: " << r.status_code << std::endl;
            return r.text;
        },
        cpr::Url{"https://api.example.com/callback-endpoint"}
    );
}

// Multi-request (parallel execution)
void multiRequestExample() {
    cpr::MultiPerform multiperform;
    
    // Configure multiple sessions
    cpr::Session session1, session2, session3;
    session1.SetUrl(cpr::Url{"https://api.example.com/users"});
    session2.SetUrl(cpr::Url{"https://api.example.com/posts"});
    session3.SetUrl(cpr::Url{"https://api.example.com/comments"});
    
    // Add to multi-performer
    multiperform.AddSession(&session1);
    multiperform.AddSession(&session2);
    multiperform.AddSession(&session3);
    
    // Execute in parallel
    std::vector<cpr::Response> responses = multiperform.Get();
    
    // Process results
    for (size_t i = 0; i < responses.size(); ++i) {
        std::cout << "Response " << i + 1 << ": " 
                  << responses[i].status_code << std::endl;
    }
}

// Download with progress display
void downloadWithProgress() {
    cpr::Response r = cpr::Get(
        cpr::Url{"https://api.example.com/large-file.zip"},
        cpr::ProgressCallback([](cpr_off_t downloadTotal, cpr_off_t downloadNow, 
                               cpr_off_t uploadTotal, cpr_off_t uploadNow, 
                               intptr_t userdata) -> bool {
            if (downloadTotal > 0) {
                double progress = (double)downloadNow / downloadTotal * 100.0;
                std::cout << "Download progress: " << std::fixed 
                          << std::setprecision(1) << progress << "%\r" << std::flush;
            }
            return true;  // true to continue, false to cancel
        })
    );
    std::cout << "\nDownload completed" << std::endl;
}

File Upload and Practical Examples

#include <cpr/cpr.h>
#include <fstream>

// File upload (multipart)
void fileUploadExample() {
    // Single file upload
    cpr::Response r = cpr::Post(
        cpr::Url{"https://api.example.com/upload"},
        cpr::Multipart{
            {"file", cpr::File{"document.pdf"}},
            {"description", "Important document"},
            {"category", "documents"}
        },
        cpr::Header{{"Authorization", "Bearer your-token"}}
    );
    
    // Multiple files + metadata
    cpr::Response r_multi = cpr::Post(
        cpr::Url{"https://api.example.com/upload-multiple"},
        cpr::Multipart{
            {"file1", cpr::File{"image1.jpg"}},
            {"file2", cpr::File{"image2.jpg"}},
            {"album_name", "Travel Photos"},
            {"public", "false"}
        }
    );
}

// Streaming download
void streamingDownload() {
    std::ofstream file("large-dataset.zip", std::ios::binary);
    
    cpr::Response r = cpr::Download(
        file,
        cpr::Url{"https://api.example.com/files/large-dataset.zip"},
        cpr::Header{{"Authorization", "Bearer your-token"}}
    );
    
    if (r.status_code == 200) {
        std::cout << "File download completed" << std::endl;
    } else {
        std::cout << "Download error: " << r.status_code << std::endl;
    }
}

// API Client class (practical example)
class APIClient {
private:
    cpr::Session session;
    std::string base_url;
    
public:
    APIClient(const std::string& url, const std::string& token) : base_url(url) {
        session.SetHeader(cpr::Header{
            {"Content-Type", "application/json"},
            {"Accept", "application/json"},
            {"User-Agent", "MyApp/1.0 (cpr)"},
            {"Authorization", "Bearer " + token}
        });
    }
    
    cpr::Response get(const std::string& endpoint) {
        session.SetUrl(cpr::Url{base_url + endpoint});
        return session.Get();
    }
    
    cpr::Response post(const std::string& endpoint, const std::string& json_data) {
        session.SetUrl(cpr::Url{base_url + endpoint});
        session.SetBody(cpr::Body{json_data});
        return session.Post();
    }
    
    cpr::Response put(const std::string& endpoint, const std::string& json_data) {
        session.SetUrl(cpr::Url{base_url + endpoint});
        session.SetBody(cpr::Body{json_data});
        return session.Put();
    }
    
    cpr::Response delete_resource(const std::string& endpoint) {
        session.SetUrl(cpr::Url{base_url + endpoint});
        return session.Delete();
    }
};

// Usage example
void apiClientExample() {
    APIClient client("https://api.example.com/v1", "your-jwt-token");
    
    // Get user list
    cpr::Response users = client.get("/users?page=1&limit=10");
    
    // Create new user
    std::string user_data = R"({
        "name": "John Doe",
        "email": "[email protected]",
        "department": "engineering"
    })";
    cpr::Response created = client.post("/users", user_data);
    
    if (created.status_code == 201) {
        std::cout << "User created successfully: " << created.text << std::endl;
    }
}