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.
GitHub Overview
libcpr/cpr
C++ Requests: Curl for People, a spiritual port of Python Requests.
Topics
Star History
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;
}
}