This API wrapper contains methods for interacting easily with ZeroBounce API. More information about ZeroBounce you can find in the official documentation.
- Keep API keys on a trusted server. Do not embed them in mobile apps or browser JavaScript that untrusted users can inspect.
- Custom API base URLs (when supported) must use
https://. Do not pass end-user-controlled hosts into those settings. - Request URLs include
api_keyas a query parameter (ZeroBounce API contract). Do not log full request URLs or enable payload debug logging in production.
Before installing the wrapper, you have to make sure that CMake is installed on your system. It can be downloaded from here.
You can install by using CMake with FetchContent. Add the following to your CMakeLists.txt:
include(FetchContent)
FetchContent_Declare(
ZeroBounce
GIT_REPOSITORY https://github.com/zerobounce/zerobounce-cpp-api-wrapper.git
GIT_TAG <DESIRED_TAG>
)
FetchContent_MakeAvailable(ZeroBounce)This will produce the target ZeroBounce which you can link against the typical way:
target_link_libraries(your_target_name PRIVATE ZeroBounce)If using Visual Studio Code, you can build the library using CMake and Cmake Tools extensions. After the extensions have been installed, you simply open the commands with CTRL + SHIFT + P and use CMake: Configure followed by CMake: Build.
Alternatively, you can also use the CLI commands in the root directory of your project.
mkdir build
cmake -S . -B build -G "Unix Makefiles"
cmake --build buildYou have to make sure that MinGW is installed on your system. It can be installed following the steps here.
mkdir build
cmake -S . -B build -G "MinGW Makefiles"
cmake --build buildBe aware that the library may require some .dll files (found in the build directory) in order to work properly in your project.
Include the library in your file:
#include <ZeroBounce/ZeroBounce.h>Initialize the wrapper with your api key and preferred api:
ZeroBounce::getInstance()->initialize("<YOUR_API_KEY>", ZBApiURL::Default);Then you can use any of the wrapper methods, for example:
ZeroBounce::getInstance()->getCredits(
[](ZBCreditsResponse response) {
cout << response.toString() << endl;
},
[](ZBErrorResponse errorResponse) {
cout << errorResponse.toString() << endl;
}
);std::tm startDate = {0};
startDate.tm_year = 118;
startDate.tm_mon = 0;
startDate.tm_mday = 1;
std::tm endDate = {0};
endDate.tm_year = 123;
endDate.tm_mon = 11;
endDate.tm_mday = 12;
ZeroBounce::getInstance()->getApiUsage(
startDate,
endDate,
[](ZBGetApiUsageResponse response) {
cout << response.toString() << endl;
},
[](ZBErrorResponse errorResponse) {
cout << errorResponse.toString() << endl;
}
);std::string email = "valid@example.com"; // The email address you want to validate
std::string ipAddress = "127.0.0.1"; // The IP Address the email signed up from (Optional)
ZeroBounce::getInstance()->validate(
email,
ipAddress,
[](ZBValidateResponse response) {
cout << response.toString() << endl;
},
[](ZBErrorResponse errorResponse) {
cout << errorResponse.toString() << endl;
}
);std::vector<ZBEmailToValidate> emails = {
{"valid@example.com", "1.1.1.1"},
{"invalid@example.com"}
};
ZeroBounce::getInstance()->validateBatch(
emails,
[](ZBValidateBatchResponse response) {
cout << response.toString() << endl;
},
[](ZBErrorResponse errorResponse) {
cout << errorResponse.toString() << endl;
}
);std::string email = "valid@example.com"; // Subscriber email address
ZeroBounce::getInstance()->getActivityData(
email,
[](ZBActivityDataResponse response) {
cout << response.toString() << endl;
},
[](ZBErrorResponse errorResponse) {
cout << errorResponse.toString() << endl;
}
);std::string filePath = "<FILE_PATH>"; // The path of the csv or txt file
int emailAddressColumn = 3; // The index of "email" column in the file. Index starts at 1
SendFileOptions options; // Additional options
options.returnUrl = "https://domain.com/called/after/processing/request";
options.firstNameColumn = 4; // The index of "first name" column in the file
options.lastNameColumn = 5; // The index of "last name" column in the file
options.genderColumn = 6; // The index of "gender" column in the file
options.ipAddressColumn = 7; // The index of "IP address" column in the file
options.hasHeaderRow = true; // If this is `true` the first row is considered as table headers
options.removeDuplicate = true; // If this is `true` the duplicate emails will be removed
ZeroBounce::getInstance()->sendFile(
filePath,
emailAddressColumn,
options,
[](ZBSendFileResponse response) {
cout << response.toString() << endl;
},
[](ZBErrorResponse errorResponse) {
cout << errorResponse.toString() << endl;
}
);std::string fileId = "<FILE_ID>"; // The returned file ID when calling sendfile API
std::string localDownloadPath = "<FILE_DOWNLOAD_PATH>"; // The location where the downloaded file will be saved
ZeroBounce::getInstance()->getFile(
fileId,
localDownloadPath,
[](ZBGetFileResponse response) {
cout << response.toString() << endl;
},
[](ZBErrorResponse errorResponse) {
cout << errorResponse.toString() << endl;
}
);Bulk validation uses the v2 bulk API. Optional v2 get file query parameters use getFileWithOptions / scoringGetFileWithOptions and a GetFileOptions object: set downloadType to ZBDownloadType::Phase1, ZBDownloadType::Phase2, or ZBDownloadType::Combined, and activityData to std::optional<bool> (std::nullopt to omit, true/false to send) for validation getFileWithOptions only (scoring omits activity_data). On SendFileOptions, set allowPhase2IsSet to true and allowPhase2 as needed to send allow_phase_2 for validation sendFile only (not scoringSendFile).
If the HTTP status indicates failure, or the body is a JSON error (including some HTTP 200 responses with "success": false), the error callback runs with a parsed message—not the get-file success callback. To inspect a raw body yourself, use ZeroBounce::getFileJsonIndicatesError(body); for a short message string use ZeroBounce::formatGetFileErrorMessage(body).
ZBFileStatusResponse includes filePhase2Status when the API returns it.
If CMake fails while configuring libcpr’s bundled curl (for example ZLIB::ZLIB or a missing macOS SDK include path), reconfigure with -DCPR_USE_SYSTEM_CURL=ON so the build uses the system libcurl.
std::string fileId = "<FILE_ID>"; // The returned file ID when calling sendfile API
ZeroBounce::getInstance()->fileStatus(
fileId,
[](ZBFileStatusResponse response) {
cout << response.toString() << endl;
},
[](ZBErrorResponse errorResponse) {
cout << errorResponse.toString() << endl;
}
);std::string fileId = "<FILE_ID>"; // The returned file ID when calling sendfile API
ZeroBounce::getInstance()->deleteFile(
fileId,
[](ZBDeleteFileResponse response) {
cout << response.toString() << endl;
},
[](ZBErrorResponse errorResponse) {
cout << errorResponse.toString() << endl;
}
);std::string filePath = "<FILE_PATH>"; // The path of the csv or txt file
int emailAddressColumn = 3; // The index of "email" column in the file. Index starts at 1
SendFileOptions options; // Additional options
options.returnUrl = "https://domain.com/called/after/processing/request";
options.hasHeaderRow = true; // If this is `true` the first row is considered as table headers
ZeroBounce::getInstance()->scoringSendFile(
filePath,
emailAddressColumn,
options,
[](ZBSendFileResponse response) {
cout << response.toString() << endl;
scoringFileId = response.fileId;
},
[](ZBErrorResponse errorResponse) {
cout << errorResponse.toString() << endl;
}
);std::string fileId = "<FILE_ID>"; // The returned file ID when calling scoringSendfile API
std::string localDownloadPath = "<FILE_DOWNLOAD_PATH>"; // The location where the downloaded file will be saved
ZeroBounce::getInstance()->scoringGetFile(
fileId,
localDownloadPath,
[](ZBGetFileResponse response) {
cout << response.toString() << endl;
},
[](ZBErrorResponse errorResponse) {
cout << errorResponse.toString() << endl;
}
);std::string fileId = "<FILE_ID>"; // The returned file ID when calling scoringSendfile API
ZeroBounce::getInstance()->scoringFileStatus(
fileId,
[](ZBFileStatusResponse response) {
cout << response.toString() << endl;
},
[](ZBErrorResponse errorResponse) {
cout << errorResponse.toString() << endl;
}
);std::string fileId = "<FILE_ID>"; // The returned file ID when calling scoringSendfile API
ZeroBounce::getInstance()->scoringDeleteFile(
scoringFileId,
[](ZBDeleteFileResponse response) {
cout << response.toString() << endl;
},
[](ZBErrorResponse errorResponse) {
cout << errorResponse.toString() << endl;
}
);ZeroBounce::getInstance()->findEmailByDomain(
domain,
first_name,
middle_name,
last_name,
[](ZBFindEmailResponse response) {
cout << response.toString() << endl;
},
[](ZBErrorResponse errorResponse) {
cout << errorResponse.toString() << endl;
}
);
ZeroBounce::getInstance()->findEmailByCompanyName(
company_name,
first_name,
middle_name,
last_name,
[](ZBFindEmailResponse response) {
cout << response.toString() << endl;
},
[](ZBErrorResponse errorResponse) {
cout << errorResponse.toString() << endl;
}
);ZeroBounce::getInstance()->searchDomainByDomain(
domain,
[](ZBDomainSearchResponse response) {
cout << response.toString() << endl;
},
[](ZBErrorResponse errorResponse) {
cout << errorResponse.toString() << endl;
}
);
ZeroBounce::getInstance()->searchDomainByCompanyName(
company_name,
[](ZBDomainSearchResponse response) {
cout << response.toString() << endl;
},
[](ZBErrorResponse errorResponse) {
cout << errorResponse.toString() << endl;
}
);It is recommended that the development should be done on Linux.
After checking out the repo, build the library then run tests.
./build/bin/ZeroBounceTestsYou should see an output like this
[==========] 26 tests from 1 test suite ran. (7 ms total)
[ PASSED ] 26 tests.- Bump
project(ZeroBounce VERSION …)inCMakeLists.txt, commit, tag (vX.Y.Z), push tag. - Actions → Publish → Run workflow with that tag.
No package registry — GitHub release only. Pin the tag in FetchContent or as a submodule.
ZBValidateOptions options;
// domainInfo adds domainWebsiteExists and domainRegistrantCompanyName.
options.domainInfo = true;
// greylistProcessing re-runs a greylisted address instead of returning unknown.
options.greylistProcessing = true;
// callbackUrl is only allowed together with greylistProcessing = true.
options.callbackUrl = std::string("https://example.com/zerobounce-callback");
zb->validate("someone@example.com", "", options,
[](ZBValidateResponse response) {
// While the greylist retry is still running the API returns a message
// instead of a result; validate again in about 30 minutes.
std::cout << response.message << " " << response.domainWebsiteExists << std::endl;
},
[](ZBErrorResponse error) { std::cout << error.toString() << std::endl; }
);Lives on https://bulkapi.zerobounce.net/api/ipt, authenticates with an
Authorization: Bearer header, and spends inbox test credits.
zb->getInboxPlacementProviders(
[](std::string json) { std::cout << json << std::endl; },
[](ZBErrorResponse error) { std::cout << error.toString() << std::endl; }
);
// testType 1 (Email) needs senderEmail; 2 is Unique ID and 3 is Auto Alias.
CreateInboxPlacementTestOptions options;
options.testType = 1;
options.senderEmail = std::string("news@example.com");
options.providers = std::vector<InboxPlacementProviderSelection>{{1, 5}};
options.webhookUrl = std::string("https://example.com/ipt-webhook");
zb->createInboxPlacementTest("October campaign", options, // name: at most 64 characters
[](std::string json) { std::cout << json << std::endl; },
[](ZBErrorResponse error) { std::cout << error.toString() << std::endl; }
);
ListInboxPlacementTestsOptions filters;
filters.status = std::string("finished");
filters.page = 1;
filters.pageSize = 25;
zb->listInboxPlacementTests(filters, onSuccess, onError);
zb->getInboxPlacementTest("123", onSuccess, onError);
zb->setInboxPlacementWebhook("https://example.com/ipt-webhook", onSuccess, onError);
zb->getInboxPlacementWebhook(onSuccess, onError);
zb->deleteInboxPlacementTest("123", onSuccess, onError);Queue a job on a file that already sits in your Amazon S3 bucket, or on your SFTP/FTP server. See the S3 and SFTP docs.
auto ok = [](ZBSendFileResponse response) { std::cout << response.toString() << std::endl; };
auto failed = [](ZBErrorResponse error) { std::cout << error.toString() << std::endl; };
// Amazon S3. fileName is the full path, as returned by listS3files.
// fileName and returnUrl are required on every one of these jobs.
BulkFileJobOptions finder;
finder.fileName = "lists/leads.csv";
finder.hasHeaderRow = true;
finder.returnUrl = "https://example.com/zerobounce-done";
zb->emailFinderS3File(finder, ok, failed);
BulkFileJobOptions domains = finder;
domains.removeDuplicates = true;
zb->domainSearchS3File(domains, ok, failed);
// SFTP/FTP. The server, path and protocol come from your ZeroBounce settings.
// The docs list fileName as optional for validateSFTPFile, but the live API
// requires it there too.
BulkFileJobOptions sftp;
sftp.fileName = "my_file.txt";
sftp.returnUrl = "https://example.com/zerobounce-done";
zb->validateSFTPFile(sftp, ok, failed);
zb->scoreSFTPFile(sftp, ok, failed);
zb->emailFinderSFTPFile(sftp, ok, failed);
zb->domainSearchSFTPFile(sftp, ok, failed);
// Note the field: this one takes export_path, not export_bucket.
// std::nullopt uses the export path from your settings.
zb->exportSFTPFile("<FILE_ID>", std::string("/exports"), ok, failed);The Warmup API is a separate product. It has its own host, its own team-scoped key, and its own client; your validation key will not work with it.
#include "ZeroBounce/ZeroBounceWarmup.h"
ZeroBounceWarmup warmup;
warmup.initialize("<YOUR_WARMUP_API_KEY>");
warmup.listSendingAccounts(onSuccess, onError);
warmup.getProgressToday(onSuccess, onError);
warmup.downloadSeeds(
[](std::string csv) { std::cout << csv << std::endl; }, // columns seed,provider
onError);Add a from-address you run yourself, with one of the five standard engagement rules (0 Default, 1 Slow Pace, 2 Repair, 3 Boost, 4 Protect):
WarmupAddSendingAccountOptions options;
options.engagementRuleId = 0;
warmup.addSendingAccount("sender@example.com", "self_managed", options, onSuccess, onError);A custom engagement rule can be changed only once every seven days; inside that window the API answers 429.
warmup.createCustomEngagement("sender@example.com", 20, 20, 35, onSuccess, onError); // reply, important, click %checkDomainDmarc takes an email address, not a domain — the live API rejects a bare domain even though the docs describe one:
warmup.checkDomainDmarc("sender@example.com", std::nullopt, onSuccess, onError);Every documented empty success (204, or an empty 200) arrives as the JSON text null. Failures carry the HTTP status on ZBErrorResponse::statusCode. Nothing is stored by testSmtpConfiguration, and the credentials you pass it are masked out of any error it raises.
A normal build never compiles it and ctest never runs it: the live suite is its own executable, built only when you ask for it. It needs real keys, which come from the environment and are never written anywhere:
cmake -S . -B build -DZEROBOUNCE_BUILD_LIVE_TESTS=ON
cmake --build build --target ZeroBounceLiveApi
export ZEROBOUNCE_API_KEY=... # validation API
export ZEROBOUNCE_WARMUP_API_KEY=... # Warmup API
./build/bin/ZeroBounceLiveApiBy default it only makes free calls: sandbox addresses, reads, and requests that are expected to fail but still prove the API accepts the payload. It checks that the credit balance did not move. Two flags opt in to more:
| Flag | What it adds |
|---|---|
ZEROBOUNCE_LIVE_SPEND=1 |
billable calls (scoring, activity, email finder) and a bulk round trip that deletes its own job |
ZEROBOUNCE_LIVE_CHANGES=1 |
reversible account changes — a test filter and a self-managed Warmup sending account — each undone in cleanup even when a check fails |
It never creates an Inbox Placement test, sets the Inbox Placement webhook, or starts ZB-managed Warmup onboarding, because the API cannot undo any of those.
Run it by hand before every release: the offline suite proves the SDK sends what we think it sends, and only the live suite proves the API accepts it.
The list evaluator and the Inbox Placement Tester have their own base URLs, separate from the main and bulk hosts.
zb->setListEvaluatorBaseUrl("https://bulkapi.zerobounce.net/v2/listevaluator");
zb->setInboxPlacementBaseUrl("https://bulkapi.zerobounce.net/api/ipt");