This SDK contains methods for interacting easily with ZeroBounce API. More information about ZeroBounce you can find in the official documentation.
pip install zerobouncesdkImport the sdk in your file:
from zerobouncesdk import ZeroBounceInitialize the SDK with your API key. You can optionally specify a base URL to use a different API region or a custom endpoint:
Default: Uses the default ZeroBounce API endpoint
from zerobouncesdk import ZeroBounce
zero_bounce = ZeroBounce("<YOUR_API_KEY>")Using predefined API regions: Use one of the available API regions
from zerobouncesdk import ZeroBounce, ZBApiUrl
# Use USA region
zero_bounce = ZeroBounce("<YOUR_API_KEY>", base_url=ZBApiUrl.API_USA_URL)
# Use EU region
zero_bounce = ZeroBounce("<YOUR_API_KEY>", base_url=ZBApiUrl.API_EU_URL)
# Use default region (explicit)
zero_bounce = ZeroBounce("<YOUR_API_KEY>", base_url=ZBApiUrl.API_DEFAULT_URL)Using a custom URL string: Provide your own base URL
zero_bounce = ZeroBounce("<YOUR_API_KEY>", base_url="https://custom-api.example.com/v2")Available API regions:
ZBApiUrl.API_DEFAULT_URL- Default ZeroBounce API (https://api.zerobounce.net/v2/)ZBApiUrl.API_USA_URL- USA region API (https://api-us.zerobounce.net/v2/)ZBApiUrl.API_EU_URL- EU region API (https://api-eu.zerobounce.net/v2/)
Then you can use any of the SDK methods, for example:
from zerobouncesdk import ZeroBounce
zero_bounce = ZeroBounce("<YOUR_API_KEY>")
response = zero_bounce.get_credits()
print("ZeroBounce get_credits response: " + str(response))from datetime import datetime
from zerobouncesdk import ZeroBounce, ZBException
zero_bounce = ZeroBounce("<YOUR_API_KEY>")
start_date = datetime(2019, 8, 1); # The start date of when you want to view API usage
end_date = datetime(2019, 9, 1); # The end date of when you want to view API usage
try:
response = zero_bounce.get_api_usage(start_date, end_date)
print("ZeroBounce get_api_usage response: " + str(response))
except ZBException as e:
print("ZeroBounce get_api_usage error: " + str(e))from zerobouncesdk import ZeroBounce, ZBException
zero_bounce = ZeroBounce("<YOUR_API_KEY>")
email = "[email protected]"; # Subscriber email address
try:
response = zero_bounce.get_activity(email)
print("ZeroBounce get_activity response: " + str(response))
except ZBException as e:
print("ZeroBounce get_activity error: " + str(e))from zerobouncesdk import ZeroBounce, ZBException
zero_bounce = ZeroBounce("<YOUR_API_KEY>")
# Option 1: Use find_email_format with domain
domain = "example.com" # The email domain for which to find the email format
first_name = "John" # The first name of the person whose email format is being searched
middle_name = "Quill" # Optional: The middle name of the person
last_name = "Doe" # Optional: The last name of the person
try:
response = zero_bounce.find_email_format(
first_name=first_name,
domain=domain,
middle_name=middle_name,
last_name=last_name
)
print("Email: " + str(response.email))
print("Email Confidence: " + str(response.email_confidence))
except ZBException as e:
print("ZeroBounce find_email_format error: " + str(e))# Option 2: Use find_email_format with company_name
from zerobouncesdk import ZeroBounce, ZBException
zero_bounce = ZeroBounce("<YOUR_API_KEY>")
company_name = "Acme Corp" # The company name for which to find the email format
first_name = "Jane" # The first name of the person
try:
response = zero_bounce.find_email_format(
first_name=first_name,
company_name=company_name
)
print("Email: " + str(response.email))
print("Domain: " + str(response.domain))
print("Company: " + str(response.company_name))
except ZBException as e:
print("ZeroBounce find_email_format error: " + str(e))# Option 3: Use find_domain to discover email formats for a domain
from zerobouncesdk import ZeroBounce, ZBException
zero_bounce = ZeroBounce("<YOUR_API_KEY>")
domain = "example.com" # The email domain to analyze
try:
response = zero_bounce.find_domain(domain=domain)
print("Domain: " + str(response.domain))
print("Format: " + str(response.format))
print("Confidence: " + str(response.confidence))
print("Other formats: " + str(len(response.other_domain_formats)))
except ZBException as e:
print("ZeroBounce find_domain error: " + str(e))# Option 4: Use find_domain with company_name
from zerobouncesdk import ZeroBounce, ZBException
zero_bounce = ZeroBounce("<YOUR_API_KEY>")
company_name = "Acme Corp" # The company name to analyze
try:
response = zero_bounce.find_domain(company_name=company_name)
print("Domain: " + str(response.domain))
print("Company: " + str(response.company_name))
print("Format: " + str(response.format))
print("Confidence: " + str(response.confidence))
for fmt in response.other_domain_formats:
print(f" Alternative: {fmt.format} (confidence: {fmt.confidence})")
except ZBException as e:
print("ZeroBounce find_domain error: " + str(e))from zerobouncesdk import ZeroBounce, ZBException
zero_bounce = ZeroBounce("<YOUR_API_KEY>")
email = "<EMAIL_ADDRESS>" # The email address you want to validate
ip_address = "127.0.0.1" # The IP Address the email signed up from (Optional)
try:
response = zero_bounce.validate(email, ip_address)
print("ZeroBounce validate response: " + str(response))
except ZBException as e:
print("ZeroBounce validate error: " + str(e))from zerobouncesdk import ZeroBounce, ZBException, ZBValidateBatchElement
zero_bounce = ZeroBounce("<YOUR_API_KEY>")
email_batch = [
ZBValidateBatchElement("[email protected]", "127.0.0.1"),
ZBValidateBatchElement("[email protected]"),
] # The batch of emails you want to validate
try:
response = zero_bounce.validate_batch(email_batch)
print("ZeroBounce validate_batch response: " + str(response))
except ZBException as e:
print("ZeroBounce validate_batch error: " + str(e))from zerobouncesdk import ZeroBounce, ZBException
zero_bounce = ZeroBounce("<YOUR_API_KEY>")
file_path = './email_file.csv' # The csv or txt file
email_address_column = 1 # The index of "email" column in the file. Index starts at 1
return_url = "https://domain.com/called/after/processing/request"
first_name_column = None # The index of "first name" column in the file
last_name_column = None # The index of "last name" column in the file
gender_column = None # The index of "gender" column in the file
ip_address_column = None # The index of "IP address" column in the file
has_header_row = False # If the first row from the submitted file is a header row
remove_duplicate = True # If you want the system to remove duplicate emails
try:
response = zero_bounce.send_file(
file_path,
email_address_column,
return_url,
first_name_column,
last_name_column,
gender_column,
ip_address_column,
has_header_row,
remove_duplicate,
)
print("ZeroBounce send_file response: " + str(response))
except ZBException as e:
print("ZeroBounce send_file error: " + str(e))from zerobouncesdk import ZeroBounce, ZBException
zero_bounce = ZeroBounce("<YOUR_API_KEY>")
file_id = "<FILE_ID>" # The returned file ID when calling sendFile API
try:
response = zero_bounce.file_status(file_id)
print("ZeroBounce file_status response: " + str(response))
except ZBException as e:
print("ZeroBounce file_status error: " + str(e))from zerobouncesdk import ZeroBounce, ZBException
zero_bounce = ZeroBounce("<YOUR_API_KEY>")
file_id="<FILE_ID>" # The returned file ID when calling sendFile API
local_download_path = "./dwnld_file.csv" # The path where the file will be downloaded
try:
response = zero_bounce.get_file(file_id, local_download_path)
print("ZeroBounce get_file response: " + str(response))
except ZBException as e:
print("ZeroBounce get_file error: " + str(e))from zerobouncesdk import ZeroBounce, ZBException
zero_bounce = ZeroBounce("<YOUR_API_KEY>")
file_id="<FILE_ID>" # The returned file ID when calling sendFile API
try:
response = zero_bounce.delete_file(file_id)
print("ZeroBounce delete_file response: " + str(response))
except ZBException as e:
print("ZeroBounce delete_file error: " + str(e))from zerobouncesdk import ZeroBounce, ZBException
zero_bounce = ZeroBounce("<YOUR_API_KEY>")
file_path = './email_file.csv' # The csv or txt file
email_address_column = 1 # The index of "email" column in the file. Index starts at 1
return_url = "https://domain.com/called/after/processing/request"
has_header_row = False # If the first row from the submitted file is a header row
remove_duplicate = True # If you want the system to remove duplicate emails
try:
response = zero_bounce.scoring_send_file(
file_path,
email_address_column,
return_url,
has_header_row,
remove_duplicate,
)
print("ZeroBounce send_file response: " + str(response))
except ZBException as e:
print("ZeroBounce send_file error: " + str(e))from zerobouncesdk import ZeroBounce, ZBException
zero_bounce = ZeroBounce("<YOUR_API_KEY>")
file_id = "<FILE_ID>" # The returned file ID when calling scoringSendFile API
try:
response = zero_bounce.scoring_file_status(file_id)
print("ZeroBounce file_status response: " + str(response))
except ZBException as e:
print("ZeroBounce file_status error: " + str(e))from zerobouncesdk import ZeroBounce, ZBException
zero_bounce = ZeroBounce("<YOUR_API_KEY>")
file_id="<FILE_ID>" # The returned file ID when calling scoringSendFile API
local_download_path = "./dwnld_file.csv" # The path where the file will be downloaded
try:
response = zero_bounce.scoring_get_file(file_id, local_download_path)
print("ZeroBounce get_file response: " + str(response))
except ZBException as e:
print("ZeroBounce get_file error: " + str(e))from zerobouncesdk import ZeroBounce, ZBException
zero_bounce = ZeroBounce("<YOUR_API_KEY>")
file_id="<FILE_ID>" # The returned file ID when calling scoringSendFile API
try:
response = zero_bounce.scoring_delete_file(file_id)
print("ZeroBounce delete_file response: " + str(response))
except ZBException as e:
print("ZeroBounce delete_file error: " + str(e))
⚠️ Deprecated: Theguess_formatmethod is deprecated and will be removed in future versions. Usefind_email_formatorfind_domaininstead (see examples above).
from zerobouncesdk import ZeroBounce, ZBException
zero_bounce = ZeroBounce("<YOUR_API_KEY>")
domain = "example.com" # The email domain for which to find the email format
first_name = "John" # The first name of the person whose email format is being searched
middle_name = "Quill" # The middle name of the person whose email format is being searched
last_name = "Doe" # The last name of the person whose email format is being searched
try:
response = zero_bounce.guess_format(domain, first_name, middle_name, last_name)
print("ZeroBounce guess format response: " + response)
except ZBException as e:
print("ZeroBounce guess format error: " + str(e))python -m venv venv # python 3.12+
source venv/bin/activate
pip install -e .python -m tests -v
# output:
python -m tests -v
test_api_regions (tests.zero_bounce_integration_test.ZeroBounceIntegrationTestCase.test_api_regions)
Test that different API regions work. ... skipped 'ZEROBOUNCE_API_KEY environment variable not set'
test_error_handling_invalid_key (tests.zero_bounce_integration_test.ZeroBounceIntegrationTestCase.test_error_handling_invalid_key)
Test error handling with invalid API key. ... skipped 'ZEROBOUNCE_API_KEY environment variable not set'
test_find_domain_with_domain (tests.zero_bounce_integration_test.ZeroBounceIntegrationTestCase.test_find_domain_with_domain)
Test find_domain with domain parameter. ... skipped 'ZEROBOUNCE_API_KEY environment variable not set'
test_find_email_format_with_domain (tests.zero_bounce_integration_test.ZeroBounceIntegrationTestCase.test_find_email_format_with_domain)
Test find_email_format with domain parameter. ... skipped 'ZEROBOUNCE_API_KEY environment variable not set'
test_get_credits (tests.zero_bounce_integration_test.ZeroBounceIntegrationTestCase.test_get_credits)
Test getting credits from the API. ... skipped 'ZEROBOUNCE_API_KEY environment variable not set'
test_init_blank_key (tests.zero_bounce_integration_test.ZeroBounceIntegrationTestCase.test_init_blank_key)
Test that blank API key raises exception. ... skipped 'ZEROBOUNCE_API_KEY environment variable not set'
test_init_invalid_base_url_type (tests.zero_bounce_integration_test.ZeroBounceIntegrationTestCase.test_init_invalid_base_url_type)
Test that invalid base_url type raises exception. ... skipped 'ZEROBOUNCE_API_KEY environment variable not set'
test_init_with_custom_base_url_string (tests.zero_bounce_integration_test.ZeroBounceIntegrationTestCase.test_init_with_custom_base_url_string)
Test initializing with a custom base URL string. ... skipped 'ZEROBOUNCE_API_KEY environment variable not set'
test_init_with_enum_base_url (tests.zero_bounce_integration_test.ZeroBounceIntegrationTestCase.test_init_with_enum_base_url)
Test initializing with enum base URL. ... skipped 'ZEROBOUNCE_API_KEY environment variable not set'
test_validate_email (tests.zero_bounce_integration_test.ZeroBounceIntegrationTestCase.test_validate_email)
Test validating an email address. ... skipped 'ZEROBOUNCE_API_KEY environment variable not set'
test_activity_valid (tests.zero_bounce_test_case.ZeroBounceTestCase.test_activity_valid) ... ok
test_api_usage_valid (tests.zero_bounce_test_case.ZeroBounceTestCase.test_api_usage_valid) ... ok
test_blank_file_id (tests.zero_bounce_test_case.ZeroBounceTestCase.test_blank_file_id) ... ok
test_credits_invalid_key (tests.zero_bounce_test_case.ZeroBounceTestCase.test_credits_invalid_key) ... ok
test_credits_valid (tests.zero_bounce_test_case.ZeroBounceTestCase.test_credits_valid) ... ok
test_delete_file_valid (tests.zero_bounce_test_case.ZeroBounceTestCase.test_delete_file_valid) ... ok
test_file_status_valid (tests.zero_bounce_test_case.ZeroBounceTestCase.test_file_status_valid) ... ok
test_find_domain_both_domain_and_company_name (tests.zero_bounce_test_case.ZeroBounceTestCase.test_find_domain_both_domain_and_company_name) ... ok
test_find_domain_confidence_enum_conversion (tests.zero_bounce_test_case.ZeroBounceTestCase.test_find_domain_confidence_enum_conversion) ... ok
test_find_domain_neither_domain_nor_company_name (tests.zero_bounce_test_case.ZeroBounceTestCase.test_find_domain_neither_domain_nor_company_name) ... ok
test_find_domain_with_company_name (tests.zero_bounce_test_case.ZeroBounceTestCase.test_find_domain_with_company_name) ... ok
test_find_domain_with_domain (tests.zero_bounce_test_case.ZeroBounceTestCase.test_find_domain_with_domain) ... ok
test_find_email_format_both_domain_and_company_name (tests.zero_bounce_test_case.ZeroBounceTestCase.test_find_email_format_both_domain_and_company_name) ... ok
test_find_email_format_email_confidence_enum_conversion (tests.zero_bounce_test_case.ZeroBounceTestCase.test_find_email_format_email_confidence_enum_conversion) ... ok
test_find_email_format_neither_domain_nor_company_name (tests.zero_bounce_test_case.ZeroBounceTestCase.test_find_email_format_neither_domain_nor_company_name) ... ok
test_find_email_format_with_company_name (tests.zero_bounce_test_case.ZeroBounceTestCase.test_find_email_format_with_company_name) ... ok
test_find_email_format_with_domain (tests.zero_bounce_test_case.ZeroBounceTestCase.test_find_email_format_with_domain) ... ok
test_get_file_valid (tests.zero_bounce_test_case.ZeroBounceTestCase.test_get_file_valid) ... ok
test_guess_format_status_invalid (tests.zero_bounce_test_case.ZeroBounceTestCase.test_guess_format_status_invalid) ... /home/vlungu/workspace/zerobounce/sdk/python/tests/zero_bounce_test_case.py:287: DeprecationWarning: guess_format is deprecated and will be removed in future versions. Use find_email_format or find_domain instead.
response = self.zero_bounce_client.guess_format(
ok
test_guess_format_status_valid (tests.zero_bounce_test_case.ZeroBounceTestCase.test_guess_format_status_valid) ... /home/vlungu/workspace/zerobounce/sdk/python/tests/zero_bounce_test_case.py:316: DeprecationWarning: guess_format is deprecated and will be removed in future versions. Use find_email_format or find_domain instead.
response = self.zero_bounce_client.guess_format(
ok
test_init_blank_key (tests.zero_bounce_test_case.ZeroBounceTestCase.test_init_blank_key) ... ok
test_invalid_file_path (tests.zero_bounce_test_case.ZeroBounceTestCase.test_invalid_file_path) ... ok
test_response_contains_error (tests.zero_bounce_test_case.ZeroBounceTestCase.test_response_contains_error) ... ok
test_response_contains_errors (tests.zero_bounce_test_case.ZeroBounceTestCase.test_response_contains_errors) ... ok
test_response_contains_message (tests.zero_bounce_test_case.ZeroBounceTestCase.test_response_contains_message) ... ok
test_response_contains_message_list (tests.zero_bounce_test_case.ZeroBounceTestCase.test_response_contains_message_list) ... ok
test_response_sub_status_accept_all (tests.zero_bounce_test_case.ZeroBounceTestCase.test_response_sub_status_accept_all) ... ok
test_response_sub_status_role_based_accept_all (tests.zero_bounce_test_case.ZeroBounceTestCase.test_response_sub_status_role_based_accept_all) ... ok
test_send_file_valid (tests.zero_bounce_test_case.ZeroBounceTestCase.test_send_file_valid) ... ok
test_validate_batch_blank_email (tests.zero_bounce_test_case.ZeroBounceTestCase.test_validate_batch_blank_email) ... ok
test_validate_batch_no_emails (tests.zero_bounce_test_case.ZeroBounceTestCase.test_validate_batch_no_emails) ... ok
test_validate_batch_valid (tests.zero_bounce_test_case.ZeroBounceTestCase.test_validate_batch_valid) ... ok
test_validate_valid (tests.zero_bounce_test_case.ZeroBounceTestCase.test_validate_valid) ... ok
----------------------------------------------------------------------
Ran 43 tests in 0.015s
OK (skipped=10) # integration tests are skipped if no api key is present# to run integration tests export api key into the environment (valid api key required)
export ZEROBOUNCE_API_KEY=<apikey> && python -m tests -v
# output:
test_api_regions (tests.zero_bounce_integration_test.ZeroBounceIntegrationTestCase.test_api_regions)
Test that different API regions work. ... ok
test_error_handling_invalid_key (tests.zero_bounce_integration_test.ZeroBounceIntegrationTestCase.test_error_handling_invalid_key)
Test error handling with invalid API key. ... ok
test_find_domain_with_domain (tests.zero_bounce_integration_test.ZeroBounceIntegrationTestCase.test_find_domain_with_domain)
Test find_domain with domain parameter. ... ok
test_find_email_format_with_domain (tests.zero_bounce_integration_test.ZeroBounceIntegrationTestCase.test_find_email_format_with_domain)
Test find_email_format with domain parameter. ... ok
test_get_credits (tests.zero_bounce_integration_test.ZeroBounceIntegrationTestCase.test_get_credits)
Test getting credits from the API. ... ok
test_init_blank_key (tests.zero_bounce_integration_test.ZeroBounceIntegrationTestCase.test_init_blank_key)
Test that blank API key raises exception. ... ok
test_init_invalid_base_url_type (tests.zero_bounce_integration_test.ZeroBounceIntegrationTestCase.test_init_invalid_base_url_type)
Test that invalid base_url type raises exception. ... ok
test_init_with_custom_base_url_string (tests.zero_bounce_integration_test.ZeroBounceIntegrationTestCase.test_init_with_custom_base_url_string)
Test initializing with a custom base URL string. ... ok
test_init_with_enum_base_url (tests.zero_bounce_integration_test.ZeroBounceIntegrationTestCase.test_init_with_enum_base_url)
Test initializing with enum base URL. ... ok
test_validate_email (tests.zero_bounce_integration_test.ZeroBounceIntegrationTestCase.test_validate_email)
Test validating an email address. ... ok
test_activity_valid (tests.zero_bounce_test_case.ZeroBounceTestCase.test_activity_valid) ... ok
test_api_usage_valid (tests.zero_bounce_test_case.ZeroBounceTestCase.test_api_usage_valid) ... ok
test_blank_file_id (tests.zero_bounce_test_case.ZeroBounceTestCase.test_blank_file_id) ... ok
test_credits_invalid_key (tests.zero_bounce_test_case.ZeroBounceTestCase.test_credits_invalid_key) ... ok
test_credits_valid (tests.zero_bounce_test_case.ZeroBounceTestCase.test_credits_valid) ... ok
test_delete_file_valid (tests.zero_bounce_test_case.ZeroBounceTestCase.test_delete_file_valid) ... ok
test_file_status_valid (tests.zero_bounce_test_case.ZeroBounceTestCase.test_file_status_valid) ... ok
test_find_domain_both_domain_and_company_name (tests.zero_bounce_test_case.ZeroBounceTestCase.test_find_domain_both_domain_and_company_name) ... ok
test_find_domain_confidence_enum_conversion (tests.zero_bounce_test_case.ZeroBounceTestCase.test_find_domain_confidence_enum_conversion) ... ok
test_find_domain_neither_domain_nor_company_name (tests.zero_bounce_test_case.ZeroBounceTestCase.test_find_domain_neither_domain_nor_company_name) ... ok
test_find_domain_with_company_name (tests.zero_bounce_test_case.ZeroBounceTestCase.test_find_domain_with_company_name) ... ok
test_find_domain_with_domain (tests.zero_bounce_test_case.ZeroBounceTestCase.test_find_domain_with_domain) ... ok
test_find_email_format_both_domain_and_company_name (tests.zero_bounce_test_case.ZeroBounceTestCase.test_find_email_format_both_domain_and_company_name) ... ok
test_find_email_format_email_confidence_enum_conversion (tests.zero_bounce_test_case.ZeroBounceTestCase.test_find_email_format_email_confidence_enum_conversion) ... ok
test_find_email_format_neither_domain_nor_company_name (tests.zero_bounce_test_case.ZeroBounceTestCase.test_find_email_format_neither_domain_nor_company_name) ... ok
test_find_email_format_with_company_name (tests.zero_bounce_test_case.ZeroBounceTestCase.test_find_email_format_with_company_name) ... ok
test_find_email_format_with_domain (tests.zero_bounce_test_case.ZeroBounceTestCase.test_find_email_format_with_domain) ... ok
test_get_file_valid (tests.zero_bounce_test_case.ZeroBounceTestCase.test_get_file_valid) ... ok
test_guess_format_status_invalid (tests.zero_bounce_test_case.ZeroBounceTestCase.test_guess_format_status_invalid) ... /home/vlungu/workspace/zerobounce/sdk/python/tests/zero_bounce_test_case.py:287: DeprecationWarning: guess_format is deprecated and will be removed in future versions. Use find_email_format or find_domain instead.
response = self.zero_bounce_client.guess_format(
ok
test_guess_format_status_valid (tests.zero_bounce_test_case.ZeroBounceTestCase.test_guess_format_status_valid) ... /home/vlungu/workspace/zerobounce/sdk/python/tests/zero_bounce_test_case.py:316: DeprecationWarning: guess_format is deprecated and will be removed in future versions. Use find_email_format or find_domain instead.
response = self.zero_bounce_client.guess_format(
ok
test_init_blank_key (tests.zero_bounce_test_case.ZeroBounceTestCase.test_init_blank_key) ... ok
test_invalid_file_path (tests.zero_bounce_test_case.ZeroBounceTestCase.test_invalid_file_path) ... ok
test_response_contains_error (tests.zero_bounce_test_case.ZeroBounceTestCase.test_response_contains_error) ... ok
test_response_contains_errors (tests.zero_bounce_test_case.ZeroBounceTestCase.test_response_contains_errors) ... ok
test_response_contains_message (tests.zero_bounce_test_case.ZeroBounceTestCase.test_response_contains_message) ... ok
test_response_contains_message_list (tests.zero_bounce_test_case.ZeroBounceTestCase.test_response_contains_message_list) ... ok
test_response_sub_status_accept_all (tests.zero_bounce_test_case.ZeroBounceTestCase.test_response_sub_status_accept_all) ... ok
test_response_sub_status_role_based_accept_all (tests.zero_bounce_test_case.ZeroBounceTestCase.test_response_sub_status_role_based_accept_all) ... ok
test_send_file_valid (tests.zero_bounce_test_case.ZeroBounceTestCase.test_send_file_valid) ... ok
test_validate_batch_blank_email (tests.zero_bounce_test_case.ZeroBounceTestCase.test_validate_batch_blank_email) ... ok
test_validate_batch_no_emails (tests.zero_bounce_test_case.ZeroBounceTestCase.test_validate_batch_no_emails) ... ok
test_validate_batch_valid (tests.zero_bounce_test_case.ZeroBounceTestCase.test_validate_batch_valid) ... ok
test_validate_valid (tests.zero_bounce_test_case.ZeroBounceTestCase.test_validate_valid) ... ok
----------------------------------------------------------------------
Ran 43 tests in 1.682s
OK. venv/bin/activate
pip install build twine
vi pyproject.toml # update version
python -m build