Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ['3.7', '3.8', '3.9', '3.10', '3.11', '3.12']
python-version: ['3.9', '3.10', '3.11', '3.12', '3.13']

steps:
- uses: actions/checkout@v4
Expand Down
22 changes: 17 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,26 @@ pip install emailable

## Usage

The library needs to be configured with your account's API key which is available in your [Emailable Dashboard](https://app.emailable.com/api).
### Authentication

### Setup
The Emailable API requires either an API key or an access token for
authentication. API keys can be created and managed in the
[Emailable Dashboard](https://app.emailable.com/api).

An API key can be set globally for the Emailable client:

```python
client = emailable.Client('your_api_key')
```

Or, you can specify an `api_key` or an `access_token` with each request:

```python
import emailable
# set api_key at request time
client.verify(api_key='your_api_key')

client = emailable.Client('live_...')
# set access_token at request time
client.verify(access_token='your_access_token')
```

### Verification
Expand All @@ -36,7 +48,7 @@ response = client.verify('evan@emailable.com')
response.state
=> 'deliverable'

# additional parameters are available. see API docs for additional info.
# additional parameters are available. see API docs for more info.
client.verify('evan@emailable.com', smtp=False, accept_all=True, timeout=25)
```

Expand Down
42 changes: 23 additions & 19 deletions emailable/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,19 @@

class Client:

def __init__(self, api_key):
def __init__(self, api_key=None):
self.api_key = api_key
self.base_url = 'https://api.emailable.com/v1/'

def verify(self, email, smtp=True, accept_all=False, timeout=None):
def verify(self,
email,
smtp=True,
accept_all=False,
timeout=None,
api_key=None,
access_token=None):
options = {
'params': {
'api_key': self.api_key,
'email': email,
'smtp': str(smtp).lower(),
'accept_all': str(accept_all).lower(),
Expand All @@ -22,45 +27,44 @@ def verify(self, email, smtp=True, accept_all=False, timeout=None):
}

url = self.base_url + 'verify'
return self.__request('get', url, options)
return self.__request('get', url, options, api_key or access_token)

def batch(self, emails, params={}):
def batch(self, emails, params={}, api_key=None, access_token=None):
options = {
'params': {
**{'api_key': self.api_key},
**params
},
'json': {
'emails': emails
}
}
url = self.base_url + 'batch'
return self.__request('post', url, options)
return self.__request('post', url, options, api_key or access_token)

def batch_status(self, batch_id, simulate=None):
def batch_status(self,
batch_id,
simulate=None,
api_key=None,
access_token=None):
options = {
'params': {
'api_key': self.api_key,
'id': batch_id,
'simulate': simulate
}
}

url = self.base_url + 'batch'
return self.__request('get', url, options)

def account(self):
options = {
'params': {
'api_key': self.api_key
}
}
return self.__request('get', url, options, api_key or access_token)

def account(self, api_key=None, access_token=None):
url = self.base_url + 'account'
return self.__request('get', url, options)
return self.__request('get', url, {}, api_key or access_token)

def __request(self, method, url, options):
def __request(self, method, url, options, key_or_token):
response = None
options['headers'] = {
'Authorization': f'Bearer {key_or_token or self.api_key}'
}
try:
response = requests.request(method, url, **options)
response.raise_for_status()
Expand Down
41 changes: 41 additions & 0 deletions tests/test_authentication.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
from unittest import TestCase
import emailable

class TestAuthentication(TestCase):

def setUp(self):
self.api_key = 'test_7aff7fc0142c65f86a00'
self.email = 'evan@emailable.com'
self.emails = ['evan@emailable.com', 'jarrett@emailable.com']

def test_invalid_api_key_authentication(self):
client = emailable.Client('test_7aff7fc0141c65f86a00')
self.assertRaises(
emailable.AuthError,
client.verify,
'evan@emailable.com'
)

def test_missing_api_key_authentication(self):
client = emailable.Client()
self.assertRaises(
emailable.AuthError,
client.verify,
'evan@emailable.com'
)

def test_global_api_key_authentication(self):
client = emailable.Client(self.api_key)
self.assertIsNotNone(client.verify(self.email).domain)
batch_id = client.batch(self.emails).id
self.assertIsNotNone(batch_id)
self.assertIsNotNone(client.batch_status(batch_id).id)
self.assertIsNotNone(client.account().available_credits)

def test_request_time_api_key_authentication(self):
client = emailable.Client()
self.assertIsNotNone(client.verify(self.email, api_key=self.api_key).domain)
batch_id = client.batch(self.emails, api_key=self.api_key).id
self.assertIsNotNone(batch_id)
self.assertIsNotNone(client.batch_status(batch_id, api_key=self.api_key).id)
self.assertIsNotNone(client.account(api_key=self.api_key).available_credits)
16 changes: 0 additions & 16 deletions tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,6 @@ def setUp(self):
self.client = emailable.Client('test_7aff7fc0142c65f86a00')
time.sleep(0.5)

def test_invalid_api_key(self):
client = emailable.Client('test_7aff7fc0141c65f86a00')
self.assertRaises(
emailable.AuthError,
client.verify,
'evan@emailable.com'
)

def test_missing_api_key(self):
self.client.api_key = None
self.assertRaises(
emailable.AuthError,
self.client.verify,
'evan@emailable.com'
)

def test_verify_returns_response(self):
response = self.client.verify('johndoe+tag@emailable.com')
self.assertIsInstance(response, emailable.Response)
Expand Down