Ruby client for Zerobounce.net API. Developed based on this version by Aaron Frase.
- 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.
Add this line to your application's Gemfile:
gem 'zerobounce-sdk'And then execute:
$bundleOr install it yourself as:
$gem install zerobounce-sdkImport
require 'zerobounce'Zerobounce.configure do |config|
config.apikey = '<zerobounce-api-key>'
# Optional: Set custom API URLs
config.api_root_url = 'https://api.zerobounce.net/v2'
config.bulk_api_root_url = 'https://bulkapi.zerobounce.net/v2'
# Optional: the root the S3 and SFTP/FTP jobs use (the bulk host without
# /v2). Left unset it is derived from bulk_api_root_url.
config.bulk_root_url = 'https://bulkapi.zerobounce.net'
end# Global API (default)
Zerobounce.configure do |config|
config.apikey = '<zerobounce-api-key>'
config.api_root_url = Zerobounce::ApiUrls::DEFAULT_URL
config.bulk_api_root_url = Zerobounce::ApiUrls::BULK_DEFAULT_URL
end
# European API (convenience)
Zerobounce.configure do |config|
config.apikey = '<zerobounce-api-key>'
config.api_root_url = Zerobounce::ApiUrls::EU_URL
config.bulk_api_root_url = Zerobounce::ApiUrls::BULK_DEFAULT_URL
end
# US API (convenience)
Zerobounce.configure do |config|
config.apikey = '<zerobounce-api-key>'
config.api_root_url = Zerobounce::ApiUrls::US_URL
config.bulk_api_root_url = Zerobounce::ApiUrls::BULK_DEFAULT_URL
endZerobounce.config.apikey = '<zerobounce-api-key>'
# Optional: Set custom API URLs
Zerobounce.config.api_root_url = 'https://api.zerobounce.net/v2'
Zerobounce.config.bulk_api_root_url = 'https://bulkapi.zerobounce.net/v2'Create a .env file in your project root:
ZEROBOUNCE_API_KEY=your_api_key_here
ZEROBOUNCE_API_URL=https://api.zerobounce.net/v2
ZEROBOUNCE_BULK_API_URL=https://bulkapi.zerobounce.net/v2
# Optional. Only needed to point the S3 and SFTP/FTP jobs somewhere of their own.
ZEROBOUNCE_BULK_ROOT_URL=https://bulkapi.zerobounce.netThe gem will automatically load these environment variables when initialized. No additional configuration needed in your code.
Set environment variables in your system:
export ZEROBOUNCE_API_KEY=your_api_key_here
export ZEROBOUNCE_API_URL=https://api.zerobounce.net/v2
export ZEROBOUNCE_BULK_API_URL=https://bulkapi.zerobounce.net/v2
# Optional. Only needed to point the S3 and SFTP/FTP jobs somewhere of their own.
export ZEROBOUNCE_BULK_ROOT_URL=https://bulkapi.zerobounce.netIf you don't specify api_root_url or bulk_api_root_url, the gem will use the following defaults:
- Main API:
https://api.zerobounce.net/v2 - Bulk API:
https://bulkapi.zerobounce.net/v2 - S3 and SFTP/FTP jobs:
https://bulkapi.zerobounce.net— the bulk host without/v2
bulk_root_url (ZEROBOUNCE_BULK_ROOT_URL) is the one base URL that is derived rather than defaulted: left unset, it is bulk_api_root_url with the trailing /v2 stripped, so pointing bulk_api_root_url at a mock server moves the S3 and SFTP/FTP endpoints with it. Set it to send those jobs somewhere on their own.
These defaults are defined in the Zerobounce::ApiUrls class constants:
Zerobounce::ApiUrls::DEFAULT_URL- Main API URL (Global)Zerobounce::ApiUrls::EU_URL- European API URLZerobounce::ApiUrls::US_URL- US API URLZerobounce::ApiUrls::BULK_DEFAULT_URL- Bulk API URL
You can use the predefined constants in your configuration. The EU and US URLs are provided as convenience options:
# Global API (default)
Zerobounce.configure do |config|
config.apikey = 'your-api-key'
config.api_root_url = Zerobounce::ApiUrls::DEFAULT_URL
config.bulk_api_root_url = Zerobounce::ApiUrls::BULK_DEFAULT_URL
end
# European API
Zerobounce.configure do |config|
config.apikey = 'your-api-key'
config.api_root_url = Zerobounce::ApiUrls::EU_URL
config.bulk_api_root_url = Zerobounce::ApiUrls::BULK_DEFAULT_URL
end
# US API
Zerobounce.configure do |config|
config.apikey = 'your-api-key'
config.api_root_url = Zerobounce::ApiUrls::US_URL
config.bulk_api_root_url = Zerobounce::ApiUrls::BULK_DEFAULT_URL
endYou can override the default URLs for custom deployments or testing:
Zerobounce.configure do |config|
config.apikey = 'your-api-key'
# Use custom URLs (e.g., for testing or custom deployments)
config.api_root_url = 'https://custom-api.example.com/v2'
config.bulk_api_root_url = 'https://custom-bulk-api.example.com/v2'
endThe configuration follows this priority order (highest to lowest):
- Explicitly set in code (
config.api_root_url = '...') - Environment variables (
ZEROBOUNCE_API_URL,ZEROBOUNCE_BULK_API_URL,ZEROBOUNCE_BULK_ROOT_URL) - Default constants (
ApiUrls::DEFAULT_URL,ApiUrls::BULK_DEFAULT_URL);bulk_root_urlfalls back tobulk_api_root_urlwithout/v2
Credits
Zerobounce.credits
=> 79 # returns -1 for invalid keyActivity
Zerobounce.activity('ss@gmail.com')
=> {"found"=>true, "active_in_days"=>"180"}Usage statistics
Zerobounce.api_usage(Date.today, Date.today)
=>
{"total"=>0,
"status_valid"=>0,
"status_invalid"=>0,
"status_catch_all"=>0,
"status_do_not_mail"=>0,
"status_spamtrap"=>0,
"status_unknown"=>0,
"sub_status_toxic"=>0,
"sub_status_disposable"=>0,
"sub_status_role_based"=>0,
"sub_status_possible_trap"=>0,
"sub_status_global_suppression"=>0,
"sub_status_timeout_exceeded"=>0,
"sub_status_mail_server_temporary_error"=>0,
"sub_status_mail_server_did_not_respond"=>0,
"sub_status_greylisted"=>0,
"sub_status_antispam_system"=>0,
"sub_status_does_not_accept_mail"=>0,
"sub_status_exception_occurred"=>0,
"sub_status_failed_syntax_check"=>0,
"sub_status_mailbox_not_found"=>0,
"sub_status_unroutable_ip_address"=>0,
"sub_status_possible_typo"=>0,
"sub_status_no_dns_entries"=>0,
"sub_status_role_based_catch_all"=>0,
"sub_status_mailbox_quota_exceeded"=>0,
"sub_status_forcible_disconnect"=>0,
"sub_status_failed_smtp_connection"=>0,
"sub_status_accept_all"=>0,
"sub_status_mx_forward"=>0,
"sub_status_alternate"=>0,
"sub_status_allowed"=>0,
"sub_status_blocked"=>0,
"sub_status_gold"=>0,
"sub_status_role_based_accept_all"=>0,
"start_date"=>"4/28/2023",
"end_date"=>"4/28/2023"}Validate single eMail
Zerobounce.validate('valid@example.com')
# Zerobounce.validate('valid@example.com', '192.168.0.1') # optional IP address
=>
{"address"=>"valid@example.com",
"status"=>"valid",
"sub_status"=>"",
"free_email"=>false,
"did_you_mean"=>nil,
"account"=>nil,
"domain"=>nil,
"domain_age_days"=>"9692",
"smtp_provider"=>"example",
"mx_found"=>"true",
"mx_record"=>"mx.example.com",
"firstname"=>"zero",
"lastname"=>"bounce",
"gender"=>"male",
"country"=>nil,
"region"=>nil,
"city"=>nil,
"zipcode"=>nil,
"processed_at"=>"2023-04-28 15:18:45.631"}Validate batch
emails
=>
["disposable@example.com",
"invalid@example.com",
"valid@example.com",
"toxic@example.com",
"donotmail@example.com",
"spamtrap@example.com"]
# ip_addresses
=> ["192.168.0.1", "192.168.0.1", "192.168.0.1", "192.168.0.1", "192.168.0.1", "192.168.0.1"]
# Zerobounce.validate_batch(emails, ip_addresses) # optional ip_addresses parameter
Zerobounce.validate_batch(emails)
=>
[{"address"=>"disposable@example.com",
"status"=>"do_not_mail",
"sub_status"=>"disposable",
"free_email"=>false,
"did_you_mean"=>nil,
"account"=>nil,
"domain"=>nil,
"domain_age_days"=>"9692",
"smtp_provider"=>"example",
"mx_found"=>"true",
"mx_record"=>"mx.example.com",
"firstname"=>"zero",
"lastname"=>"bounce",
"gender"=>"male",
"country"=>nil,
"region"=>nil,
"city"=>nil,
"zipcode"=>nil,
"processed_at"=>"2023-04-28 15:21:44.340"},
{"address"=>"invalid@example.com",
"status"=>"invalid",
"sub_status"=>"mailbox_not_found",
"free_email"=>false,
"did_you_mean"=>nil,
"account"=>nil,
"domain"=>nil,
"domain_age_days"=>"9692",
"smtp_provider"=>"example",
"mx_found"=>"true",
"mx_record"=>"mx.example.com",
"firstname"=>"zero",
"lastname"=>"bounce",
"gender"=>"male",
"country"=>nil,
"region"=>nil,
"city"=>nil,
"zipcode"=>nil,
"processed_at"=>"2023-04-28 15:21:44.340"},
{"address"=>"valid@example.com",
"status"=>"valid",
"sub_status"=>"",
"free_email"=>false,
"did_you_mean"=>nil,
"account"=>nil,
"domain"=>nil,
"domain_age_days"=>"9692",
"smtp_provider"=>"example",
"mx_found"=>"true",
"mx_record"=>"mx.example.com",
"firstname"=>"zero",
"lastname"=>"bounce",
"gender"=>"male",
"country"=>nil,
"region"=>nil,
"city"=>nil,
"zipcode"=>nil,
"processed_at"=>"2023-04-28 15:21:44.340"},
{"address"=>"toxic@example.com",
"status"=>"do_not_mail",
"sub_status"=>"toxic",
"free_email"=>false,
"did_you_mean"=>nil,
"account"=>nil,
"domain"=>nil,
"domain_age_days"=>"9692",
"smtp_provider"=>"example",
"mx_found"=>"true",
"mx_record"=>"mx.example.com",
"firstname"=>"zero",
"lastname"=>"bounce",
"gender"=>"male",
"country"=>nil,
"region"=>nil,
"city"=>nil,
"zipcode"=>nil,
"processed_at"=>"2023-04-28 15:21:44.340"},
{"address"=>"donotmail@example.com",
"status"=>"do_not_mail",
"sub_status"=>"role_based",
"free_email"=>false,
"did_you_mean"=>nil,
"account"=>nil,
"domain"=>nil,
"domain_age_days"=>"9692",
"smtp_provider"=>"example",
"mx_found"=>"true",
"mx_record"=>"mx.example.com",
"firstname"=>"zero",
"lastname"=>"bounce",
"gender"=>"male",
"country"=>nil,
"region"=>nil,
"city"=>nil,
"zipcode"=>nil,
"processed_at"=>"2023-04-28 15:21:44.340"},
{"address"=>"spamtrap@example.com",
"status"=>"spamtrap",
"sub_status"=>"",
"free_email"=>false,
"did_you_mean"=>nil,
"account"=>nil,
"domain"=>nil,
"domain_age_days"=>"9692",
"smtp_provider"=>"example",
"mx_found"=>"true",
"mx_record"=>"mx.example.com",
"firstname"=>"zero",
"lastname"=>"bounce",
"gender"=>"male",
"country"=>nil,
"region"=>nil,
"city"=>nil,
"zipcode"=>nil,
"processed_at"=>"2023-04-28 15:21:44.340"}]Send file
# sample file can be found in the files folder of this repository
validate_file_path = File.join(Dir.pwd, 'files', 'validation.csv')
=> "/home/zb/wrappers/ruby-test/files/validation.csv"
Zerobounce.validate_file_send(validate_file_path)
=>
{"success"=>true,
"message"=>"File Accepted",
"file_name"=>"validation.csv",
"file_id"=>"75d854a6-565c-49f9-b4c8-b3344480ec4c"}
# file_id will be required for next steps
# optional named parameters:
email_address_column: 1,
first_name_column: 2,
last_name_column: 3,
gender_column: 4,
has_header_row: true,
return_url: nil, # results callback url
allow_phase_2: true # optional; validation bulk sendfile only (omit or nil to skip)
# Zerobounce.validate_file_send(validate_file_path, email_address_column: 1, gender_column: 4)Bulk validation uses https://bulkapi.zerobounce.net/v2. See v2 send file, v2 file status, and v2 get file.
Check file
file_id = "75d854a6-565c-49f9-b4c8-b3344480ec4c"
=> "75d854a6-565c-49f9-b4c8-b3344480ec4c"
Zerobounce.validate_file_check(file_id)
=>
{"success"=>true,
"file_id"=>"75d854a6-565c-49f9-b4c8-b3344480ec4c",
"file_name"=>"validation.csv",
"upload_date"=>"2023-04-28T15:25:41Z",
"file_status"=>"Greylisted",
"complete_percentage"=>"0%",
"file_phase_2_status"=>"N/A", # when present (validation bulk v2)
"error_reason"=>nil,
"return_url"=>nil}Get file
file_id = "75d854a6-565c-49f9-b4c8-b3344480ec4c"
=> "75d854a6-565c-49f9-b4c8-b3344480ec4c"
Zerobounce.validate_file_get(file_id)
=> "\"email\",\"first\",\"last\",\"gender\",\"ip\",\"ZB Status\",\"ZB Sub Status\",\"ZB Account\",\"ZB Domain\",\"ZB First Name\",\"ZB Last Name\",\"ZB Gender\",\"ZB Free Email\",\"ZB MX Found\",\"ZB MX Record\",\"ZB SMTP Provider\",\"ZB Did You Mean\"\n\"disposable@example.com\",\"First\",\"Last\",\"male\",\"127.0.0.1\",\"do_not_mail\",\"disposable\",\"\",\"\",\"zero\",\"bounce\",\"male\",\"False\",\"true\",\"mx.example.com\",\"example\",\"\"\n\"invalid@example.com\",\"First\",\"Last\",\"male\",\"127.0.0.1\",\"invalid\",\"mailbox_not_found\",\"\",\"\",\"zero\",\"bounce\",\"male\",\"False\",\"true\",\"mx.example.com\",\"example\",\"\"\n\"valid@example.com\",\"First\",\"Last\",\"male\",\"127.0.0.1\",\"valid\",\"\",\"\",\"\",\"zero\",\"bounce\",\"male\",\"False\",\"true\",\"mx.example.com\",\"example\",\"\"\n\"toxic@example.com\",\"First\",\"Last\",\"male\",\"127.0.0.1\",\"do_not_mail\",\"toxic\",\"\",\"\",\"zero\",\"bounce\",\"male\",\"False\",\"true\",\"mx.example.com\",\"example\",\"\"\n\"donotmail@example.com\",\"First\",\"Last\",\"male\",\"127.0.0.1\",\"do_not_mail\",\"role_based\",\"\",\"\",\"zero\",\"bounce\",\"male\",\"False\",\"true\",\"mx.example.com\",\"example\",\"\"\n\"spamtrap@example.com\",\"First\",\"Last\",\"male\",\"127.0.0.1\",\"spamtrap\",\"\",\"\",\"\",\"zero\",\"bounce\",\"male\",\"False\",\"true\",\"mx.example.com\",\"example\",\"\"\n\"abuse@example.com\",\"First\",\"Last\",\"male\",\"127.0.0.1\",\"abuse\",\"\",\"\",\"\",\"zero\",\"bounce\",\"male\",\"False\",\"true\",\"mx.example.com\",\"example\",\"\"\n\"unknown@example.com\",\"First\",\"Last\",\"male\",\"127.0.0.1\",\"unknown\",\"mail_server_temporary_error\",\"\",\"\",\"zero\",\"bounce\",\"male\",\"False\",\"true\",\"mx.example.com\",\"example\",\"\"\n\"catch_all@example.com\",\"First\",\"Last\",\"male\",\"127.0.0.1\",\"catch-all\",\"\",\"\",\"\",\"zero\",\"bounce\",\"male\",\"False\",\"true\",\"mx.example.com\",\"example\",\"\"\n\"antispam_system@example.com\",\"First\",\"Last\",\"male\",\"127.0.0.1\",\"unknown\",\"antispam_system\",\"\",\"\",\"zero\",\"bounce\",\"male\",\"False\",\"true\",\"mx.example.com\",\"example\",\"\"\n\"does_not_accept_mail@example.com\",\"First\",\"Last\",\"male\",\"127.0.0.1\",\"invalid\",\"does_not_accept_mail\",\"\",\"\",\"zero\",\"bounce\",\"male\",\"False\",\"true\",\"mx.example.com\",\"example\",\"\"\n\"exception_occurred@example.com\",\"First\",\"Last\",\"male\",\"127.0.0.1\",\"unknown\",\"exception_occurred\",\"\",\"\",\"zero\",\"bounce\",\"male\",\"False\",\"true\",\"mx.example.com\",\"example\",\"\"\n\"failed_smtp_connection@example.com\",\"First\",\"Last\",\"male\",\"127.0.0.1\",\"unknown\",\"failed_smtp_connection\",\"\",\"\",\"zero\",\"bounce\",\"male\",\"False\",\"true\",\"mx.example.com\",\"example\",\"\"\n\"failed_syntax_check@example.com\",\"First\",\"Last\",\"male\",\"127.0.0.1\",\"invalid\",\"failed_syntax_check\",\"\",\"\",\"zero\",\"bounce\",\"male\",\"False\",\"true\",\"mx.example.com\",\"example\",\"\"\n\"forcible_disconnect@example.com\",\"First\",\"Last\",\"male\",\"127.0.0.1\",\"unknown\",\"forcible_disconnect\",\"\",\"\",\"zero\",\"bounce\",\"male\",\"False\",\"true\",\"mx.example.com\",\"example\",\"\"\n\"global_suppression@example.com\",\"First\",\"Last\",\"male\",\"127.0.0.1\",\"do_not_mail\",\"global_suppression\",\"\",\"\",\"zero\",\"bounce\",\"male\",\"False\",\"true\",\"mx.example.com\",\"example\",\"\"\n\"greylisted@example.com\",\"First\",\"Last\",\"male\",\"127.0.0.1\",\"unknown\",\"greylisted\",\"\",\"\",\"zero\",\"bounce\",\"male\",\"False\",\"true\",\"mx.example.com\",\"example\",\"\"\n\"leading_period_removed@example.com\",\"First\",\"Last\",\"male\",\"127.0.0.1\",\"valid\",\"leading_period_removed\",\"\",\"\",\"zero\",\"bounce\",\"male\",\"False\",\"true\",\"mx.example.com\",\"example\",\"\"\n\"mail_server_did_not_respond@example.com\",\"First\",\"Last\",\"male\",\"127.0.0.1\",\"unknown\",\"mail_server_did_not_respond\",\"\",\"\",\"zero\",\"bounce\",\"male\",\"False\",\"true\",\"mx.example.com\",\"example\",\"\"\n\"mail_server_temporary_error@example.com\",\"First\",\"Last\",\"male\",\"127.0.0.1\",\"unknown\",\"mail_server_temporary_error\",\"\",\"\",\"zero\",\"bounce\",\"male\",\"False\",\"true\",\"mx.example.com\",\"example\",\"\"\n\"mailbox_quota_exceeded@example.com\",\"First\",\"Last\",\"male\",\"127.0.0.1\",\"invalid\",\"mailbox_quota_exceeded\",\"\",\"\",\"zero\",\"bounce\",\"male\",\"False\",\"true\",\"mx.example.com\",\"example\",\"\"\n\"mailbox_not_found@example.com\",\"First\",\"Last\",\"male\",\"127.0.0.1\",\"invalid\",\"mailbox_not_found\",\"\",\"\",\"zero\",\"bounce\",\"male\",\"False\",\"true\",\"mx.example.com\",\"example\",\"\"\n\"no_dns_entries@example.com\",\"First\",\"Last\",\"male\",\"127.0.0.1\",\"invalid\",\"no_dns_entries\",\"\",\"\",\"zero\",\"bounce\",\"male\",\"False\",\"true\",\"mx.example.com\",\"example\",\"\"\n\"possible_trap@example.com\",\"First\",\"Last\",\"male\",\"127.0.0.1\",\"do_not_mail\",\"possible_trap\",\"\",\"\",\"zero\",\"bounce\",\"male\",\"False\",\"true\",\"mx.example.com\",\"example\",\"\"\n\"possible_typo@example.com\",\"First\",\"Last\",\"male\",\"127.0.0.1\",\"invalid\",\"possible_typo\",\"\",\"\",\"zero\",\"bounce\",\"male\",\"False\",\"true\",\"mx.example.com\",\"example\",\"\"\n\"role_based@example.com\",\"First\",\"Last\",\"male\",\"127.0.0.1\",\"do_not_mail\",\"role_based\",\"\",\"\",\"zero\",\"bounce\",\"male\",\"False\",\"true\",\"mx.example.com\",\"example\",\"\"\n\"timeout_exceeded@example.com\",\"First\",\"Last\",\"male\",\"127.0.0.1\",\"unknown\",\"timeout_exceeded\",\"\",\"\",\"zero\",\"bounce\",\"male\",\"False\",\"true\",\"mx.example.com\",\"example\",\"\"\n\"unroutable_ip_address@example.com\",\"First\",\"Last\",\"male\",\"127.0.0.1\",\"invalid\",\"unroutable_ip_address\",\"\",\"\",\"zero\",\"bounce\",\"male\",\"False\",\"true\",\"mx.example.com\",\"example\",\"\"\n\"free_email@example.com\",\"First\",\"Last\",\"male\",\"127.0.0.1\",\"valid\",\"\",\"\",\"\",\"zero\",\"bounce\",\"male\",\"True\",\"true\",\"mx.example.com\",\"example\",\"\"\n\"role_based_catch_all@example.com\",\"First\",\"Last\",\"male\",\"127.0.0.1\",\"do_not_mail\",\"role_based_catch_all\",\"\",\"\",\"zero\",\"bounce\",\"male\",\"False\",\"true\",\"mx.example.com\",\"example\",\"\"\n"Optional v2 get file query parameters use Zerobounce::GetFileOptions and Zerobounce::DownloadType (PHASE_1, PHASE_2, COMBINED). Set activity_data for validation validate_file_get only; it is not sent for scoring_file_get.
opts = Zerobounce::GetFileOptions.new(
download_type: Zerobounce::DownloadType::COMBINED,
activity_data: true
)
csv = Zerobounce.validate_file_get(file_id, opts)Non-success HTTP responses or JSON error bodies (including some HTTP 200 responses with "success": false) raise RuntimeError with the API message. To inspect a raw body string, use Zerobounce.get_file_json_indicates_error?(body).
Delete file
file_id = "75d854a6-565c-49f9-b4c8-b3344480ec4c"
=> "75d854a6-565c-49f9-b4c8-b3344480ec4c"
Zerobounce.validate_file_delete(file_id)
=>
{"success"=>true,
"message"=>"File Deleted",
"file_name"=>"validation.csv",
"file_id"=>"75d854a6-565c-49f9-b4c8-b3344480ec4c"}Send file
scoring_file_path = File.join(Dir.pwd, 'files', 'scoring.csv')
=> "/home/odin/zb/wrappers/ruby-test/files/scoring.csv"
Zerobounce.scoring_file_send(scoring_file_path)
=>
{"success"=>true,
"message"=>"File Accepted",
"file_name"=>"scoring.csv",
"file_id"=>"89fb7262-b845-4fa1-aa25-e486347ec64e"}
# file_id will be required for the next steps
# optional named parameters
email_address_column: 1,
has_header_row: true,
return_url: nil # results callback url
# Zerobounce.scoring_file_send(scoring_file_path, email_address_column: 1)File check
file_id = "89fb7262-b845-4fa1-aa25-e486347ec64e"
=> "89fb7262-b845-4fa1-aa25-e486347ec64e"
Zerobounce.scoring_file_check(file_id)
=>
{"success"=>true,
"file_id"=>"89fb7262-b845-4fa1-aa25-e486347ec64e",
"file_name"=>"scoring.csv",
"upload_date"=>"2023-04-28T16:57:18Z",
"file_status"=>"Complete",
"complete_percentage"=>"100% Complete.",
"return_url"=>nil}Get file
file_id = "89fb7262-b845-4fa1-aa25-e486347ec64e"
=> "89fb7262-b845-4fa1-aa25-e486347ec64e"
Zerobounce.scoring_file_get(file_id)
=> "\"email\",\"ZeroBounceQualityScore\"\r\n\"disposable@example.com\",\"0\"\r\n\"invalid@example.com\",\"10\"\r\n\"valid@example.com\",\"10\"\r\n\"toxic@example.com\",\"2\"\r\n\"donotmail@example.com\",\"0\"\r\n\"spamtrap@example.com\",\"0\"\r\n\"abuse@example.com\",\"10\"\r\n\"unknown@example.com\",\"10\"\r\n\"catch_all@example.com\",\"10\"\r\n\"antispam_system@example.com\",\"0\"\r\n\"does_not_accept_mail@example.com\",\"0\"\r\n\"exception_occurred@example.com\",\"0\"\r\n\"failed_smtp_connection@example.com\",\"0\"\r\n\"failed_syntax_check@example.com\",\"0\"\r\n\"forcible_disconnect@example.com\",\"0\"\r\n\"global_suppression@example.com\",\"0\"\r\n\"greylisted@example.com\",\"0\"\r\n\"leading_period_removed@example.com\",\"0\"\r\n\"mail_server_did_not_respond@example.com\",\"0\"\r\n\"mail_server_temporary_error@example.com\",\"0\"\r\n\"mailbox_quota_exceeded@example.com\",\"0\"\r\n\"mailbox_not_found@example.com\",\"0\"\r\n\"no_dns_entries@example.com\",\"0\"\r\n\"possible_trap@example.com\",\"0\"\r\n\"possible_typo@example.com\",\"0\"\r\n\"role_based@example.com\",\"0\"\r\n\"timeout_exceeded@example.com\",\"0\"\r\n\"unroutable_ip_address@example.com\",\"0\"\r\n\"free_email@example.com\",\"0\"\r\n\"role_based_catch_all@example.com\",\"0\""
# Optional second argument: GetFileOptions with download_type only (activity_data is not sent for scoring getfile)
# Zerobounce.scoring_file_get(file_id, Zerobounce::GetFileOptions.new(download_type: Zerobounce::DownloadType::PHASE_2))Delete file
file_id = "89fb7262-b845-4fa1-aa25-e486347ec64e"
=> "89fb7262-b845-4fa1-aa25-e486347ec64e"
Zerobounce.scoring_file_delete(file_id)
=> {"success"=>false, "message"=>"File cannot be found."}This applies for both 'Email validation' and 'AI Scoring' functionalities.
The data of the submitted CSV file will be included in the result CSV file. Therefore, the first columns of the result CSV file will consist of the submitted data and the following columns will provide information specific to each functionality.
The additional columns that would be included in result CSV file:
- ZB Status
- ZB Sub Status
- ZB Account
- ZB Domain
- ZB First Name
- ZB Last Name
- ZB Gender
- ZB Free Email
- ZB MX Found
- ZB MX Record
- ZB SMTP Provider
- ZB Did You Mean
- ZB City
- ZB Region/State
- ZB Zip Code
- ZB Country
When has_header_row: true is provided to validate_file_send() method, original columns of the submitted file will be retained.
When has_header_row: false, the columns that were explicitly specified in validate_file_send() will be given the following column title:
email_address_column => "Email Address"
first_name_column => "First Name"
last_name_column => "Last Name"
gender_column => "Gender"
Any additional that were not (or can not be) specified by parameters, will be given column title "Custom".
The additional column that would be included in result CSV file:
- ZeroBounceQualityScore
When has_header_row: true is provided to scoring_file_send() method, original columns of the submitted file will be retained.
When has_header_row: false is provided to scoring_file_send() method, column title for the email column (as well as the other columns) will be Email Address.
Guess Format by Domain
# New keyword argument syntax (recommended)
Zerobounce.guessformat(domain: "zerobounce.net")
=>
{"email"=>"",
"domain"=>"zerobounce.net",
"format"=>"first.last",
"status"=>"",
"sub_status"=>"",
"confidence"=>"high",
"did_you_mean"=>"",
"failure_reason"=>"",
"other_domain_formats"=>
[{"format"=>"first", "confidence"=>"high"},
{"format"=>"last.first", "confidence"=>"high"},
{"format"=>"lfirst", "confidence"=>"high"},
{"format"=>"lastfirst", "confidence"=>"high"},
{"format"=>"firstl", "confidence"=>"high"},
{"format"=>"last", "confidence"=>"medium"},
{"format"=>"first.middle.last", "confidence"=>"medium"},
{"format"=>"first-last", "confidence"=>"medium"},
{"format"=>"l.first", "confidence"=>"medium"},
{"format"=>"f.last", "confidence"=>"medium"},
{"format"=>"f-last", "confidence"=>"medium"},
{"format"=>"first.l", "confidence"=>"medium"},
{"format"=>"first-l", "confidence"=>"medium"},
{"format"=>"firstlast", "confidence"=>"medium"},
{"format"=>"first_l", "confidence"=>"medium"},
{"format"=>"f_last", "confidence"=>"medium"},
{"format"=>"last.f", "confidence"=>"medium"},
{"format"=>"last-f", "confidence"=>"medium"},
{"format"=>"last-first", "confidence"=>"medium"},
{"format"=>"first_last", "confidence"=>"medium"},
{"format"=>"last_f", "confidence"=>"medium"},
{"format"=>"last_first", "confidence"=>"medium"},
{"format"=>"flast", "confidence"=>"medium"},
{"format"=>"lastf", "confidence"=>"medium"},
{"format"=>"l-first", "confidence"=>"low"},
{"format"=>"l_first", "confidence"=>"low"}]}
# With names for better accuracy (new syntax)
Zerobounce.guessformat(domain: "zerobounce.net", first_name: "John", middle_name: 'Deere', last_name: "Doe")
# Backwards compatible syntax (still supported)
Zerobounce.guessformat("zerobounce.net")
Zerobounce.guessformat("zerobounce.net", first_name: "John", middle_name: 'Deere', last_name: "Doe")Guess Format by Company Name
# New keyword argument syntax (recommended)
Zerobounce.guessformat(company_name: "Zero Bounce")
=>
{"email"=>"",
"company_name"=>"Zero Bounce",
"format"=>"first.last",
"status"=>"",
"sub_status"=>"",
"confidence"=>"high",
"did_you_mean"=>"",
"failure_reason"=>"",
"other_domain_formats"=>[...]}
# With names for better accuracy (new syntax)
Zerobounce.guessformat(first_name: "John", last_name: "Doe", company_name: "Zero Bounce")Find Email Address
# Find email by domain
Zerobounce.find_email("John", domain: "zerobounce.net")
=>
{
"email": "john@zerobounce.net",
"email_confidence": "medium",
"domain": "zerobounce.net",
"company_name": "ZeroBounce",
"did_you_mean": "",
"failure_reason": ""
}
# Find email by company name
Zerobounce.find_email("John", company_name: "Zero Bounce")
=>
{
"email": "john@zerobounce.net",
"email_confidence": "medium",
"domain": "zerobounce.net",
"company_name": "ZeroBounce",
"did_you_mean": "",
"failure_reason": ""
}
# With additional name information for better accuracy
Zerobounce.find_email("John", domain: "zerobounce.net", middle_name: "Deere", last_name: "Doe")
# The first name is optional. Without one the call answers with the domain's
# address format rather than one person's address.
Zerobounce.find_email(domain: "zerobounce.net")Find Domain Information
# Find domain format by domain
Zerobounce.find_domain(domain: "zerobounce.net")
=>
{
"domain": "zerobounce.net",
"company_name": "Hertza, LLC",
"format": "first.last",
"confidence": "high",
"did_you_mean": "",
"failure_reason": "",
"other_domain_formats": [
{"format": "first", "confidence": "high"},
{"format": "last.first", "confidence": "high"},
{"format": "lastfirst", "confidence": "high"},
{"format": "firstl", "confidence": "high"},
{"format": "lfirst", "confidence": "high"},
{"format": "firstlast", "confidence": "high"},
{"format": "last_middle_f", "confidence": "high"},
{"format": "last", "confidence": "high"},
{"format": "f.last", "confidence": "medium"},
{"format": "last-f", "confidence": "medium"},
{"format": "l.first", "confidence": "medium"},
{"format": "last_f", "confidence": "medium"},
{"format": "first.middle.last", "confidence": "medium"},
{"format": "first-last", "confidence": "medium"},
{"format": "last.f", "confidence": "medium"},
{"format": "last_first", "confidence": "medium"},
{"format": "f-last", "confidence": "medium"},
{"format": "first.l", "confidence": "medium"},
{"format": "first-l", "confidence": "medium"},
{"format": "first_l", "confidence": "medium"},
{"format": "first_last", "confidence": "medium"},
{"format": "f_last", "confidence": "medium"},
{"format": "last-first", "confidence": "medium"},
{"format": "flast", "confidence": "medium"},
{"format": "lastf", "confidence": "medium"},
{"format": "l_first", "confidence": "medium"},
{"format": "l-first", "confidence": "medium"},
{"format": "first-middle-last", "confidence": "low"},
{"format": "firstmlast", "confidence": "low"},
{"format": "last.middle.first", "confidence": "low"},
{"format": "last_middle_first", "confidence": "low"},
{"format": "first_middle_last", "confidence": "low"},
{"format": "last-middle-first", "confidence": "low"},
{"format": "first-m-last", "confidence": "low"},
{"format": "firstmiddlelast", "confidence": "low"},
{"format": "last.m.first", "confidence": "low"},
{"format": "lastmfirst", "confidence": "low"},
{"format": "lastmiddlefirst", "confidence": "low"},
{"format": "last_m_first", "confidence": "low"},
{"format": "first.m.last", "confidence": "low"},
{"format": "first_m_last", "confidence": "low"},
{"format": "last-m-first", "confidence": "low"}
]
}
# Find domain format by company name
Zerobounce.find_domain(company_name: "Zero Bounce")
=>
{
"domain": "zerobounce.net",
"company_name": "Zero Bounce",
"format": "first.last",
"confidence": "high",
"did_you_mean": "",
"failure_reason": "",
"other_domain_formats": [...]
}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.
warmup = Zerobounce::Warmup.new('<YOUR_WARMUP_API_KEY>')
warmup.list_sending_accounts
warmup.progress_today
warmup.download_seeds # CSV text, columns seed,providerAdd 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):
warmup.add_sending_account('sender@example.com', 'self_managed', engagement_rule_id: 0)Or have ZeroBounce provision the mailbox. A 200 means the request was accepted; provisioning happens afterwards.
warmup.add_sending_account(
'sender@example.com', 'zb_managed',
email_content: { types_of_emails: %w[newsletter marketing] },
managed_sending: { timezone_offset: -5, warmup_start_date: Date.new(2026, 3, 4) }
)A custom engagement rule can be changed only once every seven days; inside that window the API answers 429.
warmup.create_custom_engagement('sender@example.com', 20, 20, 35) # reply, important, click %check_domain_dmarc takes an email address, not a domain — the live API rejects a bare domain even though the docs describe one:
warmup.check_domain_dmarc('sender@example.com')Every documented empty success (204, or an empty 200) comes back as nil, and a payload that is not a JSON object — /seeds is an array, /seed-count a bare number — comes back as the parsed value itself. Failures raise Zerobounce::ApiResponseError, whose #status_code gives the HTTP status. Nothing is stored by test_smtp_configuration, and the credentials you pass it are masked out of any error it raises.
sudo apt install -y rbenv
git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build
rbenv install 3.2.1
rbenv global 3.2.1
rbenv rehash
ruby -v
gem install bundler -v "~>2.4.6"
bundle installFrom the sdk-docs/ folder in the SDKs monorepo:
cd sdk-docs
docker compose build ruby
docker compose run --rm rubyOr from this directory:
docker build -t zb-ruby-sdk .
docker run --rm zb-ruby-sdkbundle exec rspecYou should see an output like this
running tests
.....................................................
Finished in 6.81 seconds (files took 0.40587 seconds to load)
53 examples, 0 failuresThe tests use the following environment parameter:
- ZEROBOUNCE_API_KEY – Your API key; used in mock tests as the valid key sample (any value will work for mock tests).
An invalid API key for error-handling tests is hardcoded in the spec; no env var is required.
To set your key:
export ZEROBOUNCE_API_KEY=your_api_key_hereA .env.example file is provided.
Tests use webmock and vcr for mocking HTTP requests. This means that actual requests were made and recorded in the spec/cassettes with an (at the time) valid API key used for testing purposes. This key has been invalidated in the meantime, however it is provided in the .env.example file for the mock tests to work. If you do not wish to use this key for mocks, you can replace it with any value in the .yml files under spec/cassettes or delete all of them and rerun the tests so that vcr records them with a new key.
rspec never touches the network: the live suite lives outside spec/, so CI does not even load it. It needs real keys, which come from the environment and are never written anywhere:
export ZEROBOUNCE_API_KEY=... # validation API
export ZEROBOUNCE_WARMUP_API_KEY=... # Warmup API
ruby -Ilib live/live_api.rbBy 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.
- Bump
VERSIONinlib/zerobounce/version.rb, commit, tag (vX.Y.Z), push tag. - Actions → Publish → Run workflow with that tag.
Registry: zerobounce-sdk on RubyGems