├── _config.yml ├── pyhunter ├── __init__.py ├── exceptions.py └── pyhunter.py ├── setup.cfg ├── .github └── dependabot.yml ├── Pipfile ├── requirements.txt ├── requirements-dev.txt ├── LICENSE ├── setup.py ├── .gitignore └── README.md /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-minimal -------------------------------------------------------------------------------- /pyhunter/__init__.py: -------------------------------------------------------------------------------- 1 | from .pyhunter import PyHunter 2 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | description-file = README.md 3 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: pip 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | time: "04:00" 8 | open-pull-requests-limit: 10 9 | -------------------------------------------------------------------------------- /Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | url = "https://pypi.python.org/simple" 3 | verify_ssl = true 4 | name = "pypi" 5 | 6 | [packages] 7 | requests = "*" 8 | 9 | [dev-packages] 10 | pipenv-to-requirements = "*" 11 | -------------------------------------------------------------------------------- /pyhunter/exceptions.py: -------------------------------------------------------------------------------- 1 | class PyhunterError(Exception): 2 | """ 3 | Generic exception class for the library 4 | """ 5 | pass 6 | 7 | 8 | class MissingCompanyError(PyhunterError): 9 | pass 10 | 11 | 12 | class MissingNameError(PyhunterError): 13 | pass 14 | 15 | 16 | class HunterApiError(PyhunterError): 17 | """ 18 | Represents something went wrong in the call to the Hunter API 19 | """ 20 | pass 21 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # This requirements file has been automatically generated from `Pipfile` with 3 | # `pipenv-to-requirements` 4 | # 5 | # 6 | # This has been done to maintain backward compatibility with tools and services 7 | # that do not support `Pipfile` yet. 8 | # 9 | # Do NOT edit it directly, use `pipenv install [-d]` to modify `Pipfile` and 10 | # `Pipfile.lock` and then regenerate `requirements*.txt`. 11 | ################################################################################ 12 | 13 | requests 14 | -------------------------------------------------------------------------------- /requirements-dev.txt: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # This requirements file has been automatically generated from `Pipfile` with 3 | # `pipenv-to-requirements` 4 | # 5 | # 6 | # This has been done to maintain backward compatibility with tools and services 7 | # that do not support `Pipfile` yet. 8 | # 9 | # Do NOT edit it directly, use `pipenv install [-d]` to modify `Pipfile` and 10 | # `Pipfile.lock` and then regenerate `requirements*.txt`. 11 | ################################################################################ 12 | 13 | pipenv-to-requirements 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Quentin Durantay 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | with open("README.md", "r") as fh: 4 | long_description = fh.read() 5 | 6 | setup( 7 | name='pyhunter', 8 | packages=['pyhunter'], 9 | version='1.7', 10 | description='An (unofficial) Python wrapper for the Hunter.io API', 11 | long_description=long_description, 12 | long_description_content_type='text/markdown', 13 | author='Quentin Durantay', 14 | author_email='quentin.durantay@gmail.com', 15 | url='https://github.com/VonStruddle/PyHunter', 16 | download_url='https://github.com/VonStruddle/PyHunter/archive/121.tar.gz', 17 | license='MIT', 18 | install_requires=['requests'], 19 | keywords=['hunter', 'hunter.io', 'lead generation', 'lead enrichment'], 20 | classifiers=[ 21 | 'Development Status :: 3 - Alpha', 22 | 'Natural Language :: English', 23 | 'License :: OSI Approved :: MIT License', 24 | 'Programming Language :: Python', 25 | 'Programming Language :: Python :: 3', 26 | 'Programming Language :: Python :: 3.3', 27 | 'Programming Language :: Python :: 3.4', 28 | 'Programming Language :: Python :: 3.5', 29 | 'Topic :: Utilities' 30 | ], 31 | ) 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | local_settings.py 56 | 57 | # Flask stuff: 58 | instance/ 59 | .webassets-cache 60 | 61 | # Scrapy stuff: 62 | .scrapy 63 | 64 | # Sphinx documentation 65 | docs/_build/ 66 | 67 | # PyBuilder 68 | target/ 69 | 70 | # Jupyter Notebook 71 | .ipynb_checkpoints 72 | 73 | # pyenv 74 | .python-version 75 | 76 | # celery beat schedule file 77 | celerybeat-schedule 78 | 79 | # SageMath parsed files 80 | *.sage.py 81 | 82 | # dotenv 83 | .env 84 | 85 | # virtualenv 86 | .venv 87 | venv/ 88 | ENV/ 89 | 90 | # Spyder project settings 91 | .spyderproject 92 | .spyproject 93 | 94 | # Rope project settings 95 | .ropeproject 96 | 97 | # mkdocs documentation 98 | /site 99 | 100 | # mypy 101 | .mypy_cache/ 102 | PyHunter.egg-info 103 | .idea 104 | 105 | # VS Code 106 | .vscode/* 107 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![PyPI version](https://badge.fury.io/py/pyhunter.svg)](https://badge.fury.io/py/pyhunter) 2 | 3 | # PyHunter 4 | 5 | ## A Python wrapper for the Hunter.io v2 API 6 | 7 | ### Installation 8 | 9 | Requirements: 10 | 11 | * Python 3 (no Python 2 version) 12 | 13 | 14 | To install: 15 | 16 | ```bash 17 | pip install pyhunter 18 | ``` 19 | 20 | ### Usage 21 | 22 | PyHunter supports all the methods from the [Hunter.io](https://hunter.io/api/v2/docs) v2 API: 23 | 24 | * `domain_search` 25 | * `email_finder` 26 | * `email_verifier` 27 | * `email_count` 28 | * `account_information` 29 | 30 | PyHunter also supports new methods from the `Leads` and `Leads Lists` APIs. 31 | 32 | ### How to use PyHunter 33 | 34 | Import the PyHunter and instantiate it: 35 | 36 | ```python 37 | from pyhunter import PyHunter 38 | ``` 39 | 40 | ```python 41 | hunter = PyHunter('my_hunter_api_key') 42 | ``` 43 | 44 | You can search all the email addresses for a given domain: 45 | 46 | ```python 47 | hunter.domain_search('instagram.com') 48 | ``` 49 | 50 | You can also pass the company name, along with optional parameters: 51 | 52 | ```python 53 | hunter.domain_search(company='Instagram', limit=5, offset=2, emails_type='personal') 54 | ``` 55 | 56 | You can find a specific email address: 57 | 58 | ```python 59 | email, confidence_score = hunter.email_finder('instagram.com', first_name='Kevin', last_name='Systrom') 60 | ``` 61 | 62 | You can also use the company name and the full name instead, along with raw to get the full response: 63 | 64 | ```python 65 | hunter.email_finder(company='Instagram', full_name='Kevin Systrom', raw=True) 66 | ``` 67 | 68 | You can check the deliverability of a given email address: 69 | 70 | ```python 71 | hunter.email_verifier('kevin@instagram.com') 72 | ``` 73 | 74 | You can check how many email addresses Hunter has for a given domain: 75 | 76 | ```python 77 | hunter.email_count('instagram.com') 78 | ``` 79 | 80 | You can also use a company name if the domain is unknown:: 81 | ```python 82 | hunter.email_count(company='Instagram') 83 | ``` 84 | 85 | When both domain and company are passed, the domain will be used: 86 | ```python 87 | hunter.email_count(domain='instagram.com', company='Instagram') 88 | ``` 89 | 90 | And you can finally check your account information (PyHunter adds the number of calls still available in the payload): 91 | 92 | ```python 93 | hunter.account_information() 94 | ``` 95 | 96 | 97 | **NOTE:** By default, all of the calls (except `email_verifier()`) return the 'data' element 98 | of the JSON response. You can get the "raw" response by passing `raw=True` to those calls. 99 | This gives access to the response headers, e.g. `X-RateLimit-Remaining` returned for the 100 | `domain_search()` call, and also the complete response body, including `meta`. 101 | 102 | 103 | ### But that's not all folks! As the v2 API added Leads and Leads Lists, these methods are also available on PyHunter 104 | 105 | #### Leads methods 106 | 107 | You can get all your leads: 108 | 109 | ```python 110 | hunter.get_leads() 111 | ``` 112 | 113 | Or filter the leads you want using these arguments: 114 | 115 | ```python 116 | hunter.get_leads(offset=2, limit=10, lead_list_id=1, first_name='Kevin', last_name='Systrom', email='kevin@instragram.com', company='Instagram', phone_number='0102030405', twitter='kevin') 117 | ``` 118 | 119 | You can also get a specific lead by giving its id: 120 | 121 | ```python 122 | hunter.get_lead(42) 123 | ``` 124 | 125 | You can create a lead: 126 | 127 | ```python 128 | hunter.create_lead('Quentin', 'Durantay', email='quentin.durantay@unicorn.io', position='CEO', company='Unicorn Consulting', company_size=10, confidence_score=100, website='unicornsaregreat.io', contry_code='FR', postal_code=75000, source='theinternet.com', linkedin_url='www.linkedin.com/in/masteroftheuniverse', phone_number='0102030405', twitter='quentindty', leads_list_id=1) 129 | ``` 130 | 131 | You can update a lead by giving its id and the parameters to change (same as creation): 132 | 133 | ```python 134 | hunter.update_lead(1, position='CEO in chief') 135 | ``` 136 | 137 | And you can also delete a lead by giving its id: 138 | 139 | ```python 140 | hunter.delete_lead(42) 141 | ``` 142 | 143 | #### Leads Lists methods 144 | 145 | You can get all your Leads Lists: 146 | 147 | ```python 148 | hunter.get_leads_lists() 149 | ``` 150 | 151 | And filter the results with these arguments: 152 | 153 | ```python 154 | hunter.get_leads_lists(offset=3, limit=2) 155 | ``` 156 | 157 | You can get a specific Leads List by giving its id: 158 | 159 | ```python 160 | hunter.get_leads_list(42) 161 | ``` 162 | 163 | You can create a Leads Lists: 164 | 165 | ```python 166 | hunter.create_leads_list('Ultra hot prospects', team_id=1) 167 | ``` 168 | 169 | You can update a Leads List by giving its id: 170 | 171 | ```python 172 | hunter.update_leads_list(42, 'Ultra mega hot prospects', team_id=2) 173 | ``` 174 | 175 | And you can finally delete a Leads Lists by giving its id: 176 | 177 | ```python 178 | hunter.delete_leads_list(42) 179 | ``` 180 | 181 | ### Information 182 | 183 | This library is still in development, don't hesitate to share bugs if you find some (normally it's good, but you never know :p ). 184 | 185 | ### Contribute 186 | 187 | It's my first (ever) open-source library! So it can be improved. Feel very welcome to fork it and ask for pull requests if you find something buggy or lacking ;) 188 | 189 | ### Have a nice day scraping B2B emails with PyHunter! 190 | -------------------------------------------------------------------------------- /pyhunter/pyhunter.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | from .exceptions import MissingCompanyError, MissingNameError, HunterApiError 4 | 5 | 6 | class PyHunter: 7 | def __init__(self, api_key): 8 | self.api_key = api_key 9 | self.base_params = {'api_key': api_key} 10 | self.base_endpoint = 'https://api.hunter.io/v2/{}' 11 | 12 | def _query_hunter(self, endpoint, params, request_type='get', 13 | payload=None, headers=None, raw=False): 14 | 15 | request_kwargs = dict(params=params, json=payload, headers=headers) 16 | res = getattr(requests, request_type)(endpoint, **request_kwargs) 17 | res.raise_for_status() 18 | 19 | if raw: 20 | return res 21 | 22 | try: 23 | data = res.json()['data'] 24 | except KeyError: 25 | raise HunterApiError(res.json()) 26 | 27 | return data 28 | 29 | def domain_search(self, domain=None, company=None, limit=None, offset=None, 30 | seniority=None, department=None, emails_type=None, raw=False): 31 | """ 32 | Return all the email addresses found for a given domain. 33 | 34 | :param domain: The domain on which to search for emails. Must be 35 | defined if company is not. 36 | 37 | :param company: The name of the company on which to search for emails. 38 | Must be defined if domain is not. 39 | 40 | :param limit: The maximum number of emails to give back. Default is 10. 41 | 42 | :param offset: The number of emails to skip. Default is 0. 43 | 44 | :param seniority: The seniority level of the owners of emails to give back. Can be 'junior', 'senior', 45 | 'executive' or a combination of them delimited by a comma. 46 | 47 | :param department: The department where the owners of the emails to give back work. Can be 'executive', 'it', 48 | 'finance', 'management', 'sales', 'legal', 'support', 'hr', 'marketing', 'communication' or a combination of 49 | them delimited by a comma. 50 | 51 | :param emails_type: The type of emails to give back. Can be one of 52 | 'personal' or 'generic'. 53 | 54 | :param raw: Gives back the entire response instead of just the 'data'. 55 | 56 | :return: Full payload of the query as a dict, with email addresses 57 | found. 58 | """ 59 | if domain: 60 | params = {'domain': domain, 'api_key': self.api_key} 61 | elif company: 62 | params = {'company': company, 'api_key': self.api_key} 63 | else: 64 | raise MissingCompanyError( 65 | 'You must supply at least a domain name or a company name' 66 | ) 67 | 68 | if limit: 69 | params['limit'] = limit 70 | 71 | if offset: 72 | params['offset'] = offset 73 | 74 | if seniority: 75 | params['seniority'] = seniority 76 | 77 | if department: 78 | params['department'] = department 79 | 80 | if emails_type: 81 | params['type'] = emails_type 82 | 83 | endpoint = self.base_endpoint.format('domain-search') 84 | 85 | return self._query_hunter(endpoint, params, raw=raw) 86 | 87 | def email_finder(self, domain=None, company=None, first_name=None, 88 | last_name=None, full_name=None, raw=False): 89 | """ 90 | Find the email address of a person given its name and company's domain. 91 | 92 | :param domain: The domain of the company where the person works. Must 93 | be defined if company is not. 94 | 95 | :param company: The name of the company where the person works. Must 96 | be defined if domain is not. 97 | 98 | :param first_name: The first name of the person. Must be defined if 99 | full_name is not. 100 | 101 | :param last_name: The last name of the person. Must be defined if 102 | full_name is not. 103 | 104 | :param full_name: The full name of the person. Must be defined if 105 | first_name AND last_name are not. 106 | 107 | :param raw: Gives back the entire response instead of just email and score. 108 | 109 | :return: email and score as a tuple. 110 | """ 111 | params = self.base_params 112 | 113 | if not domain and not company: 114 | raise MissingCompanyError( 115 | 'You must supply at least a domain name or a company name' 116 | ) 117 | 118 | if domain: 119 | params['domain'] = domain 120 | elif company: 121 | params['company'] = company 122 | 123 | if not(first_name and last_name) and not full_name: 124 | raise MissingNameError( 125 | 'You must supply a first name AND a last name OR a full name' 126 | ) 127 | 128 | if first_name and last_name: 129 | params['first_name'] = first_name 130 | params['last_name'] = last_name 131 | elif full_name: 132 | params['full_name'] = full_name 133 | 134 | endpoint = self.base_endpoint.format('email-finder') 135 | 136 | res = self._query_hunter(endpoint, params, raw=raw) 137 | if raw: 138 | return res 139 | 140 | email = res['email'] 141 | score = res['score'] 142 | 143 | return email, score 144 | 145 | def email_verifier(self, email, raw=False): 146 | """ 147 | Verify the deliverability of a given email address. 148 | 149 | :param email: The email address to check. 150 | 151 | :param raw: Gives back the entire response instead of just the 'data'. 152 | 153 | :return: Full payload of the query as a dict. 154 | """ 155 | params = {'email': email, 'api_key': self.api_key} 156 | 157 | endpoint = self.base_endpoint.format('email-verifier') 158 | 159 | return self._query_hunter(endpoint, params, raw=raw) 160 | 161 | def email_count(self, domain=None, company=None, raw=False): 162 | """ 163 | Give back the number of email addresses Hunter has for this domain/company. 164 | 165 | :param domain: The domain of the company where the person works. Must 166 | be defined if company is not. If both 'domain' and 'company' are given, 167 | the 'domain' will be used. 168 | 169 | :param company: The name of the company where the person works. Must 170 | be defined if domain is not. 171 | 172 | :param raw: Gives back the entire response instead of just the 'data'. 173 | 174 | :return: Full payload of the query as a dict. 175 | """ 176 | params = self.base_params 177 | 178 | if not domain and not company: 179 | raise MissingCompanyError( 180 | 'You must supply at least a domain name or a company name' 181 | ) 182 | 183 | if domain: 184 | params['domain'] = domain 185 | elif company: 186 | params['company'] = company 187 | 188 | endpoint = self.base_endpoint.format('email-count') 189 | 190 | return self._query_hunter(endpoint, params, raw=raw) 191 | 192 | def account_information(self, raw=False): 193 | """ 194 | Gives the information about the account associated with the api_key. 195 | 196 | :param raw: Gives back the entire response instead of just the 'data'. 197 | 198 | :return: Full payload of the query as a dict. 199 | """ 200 | params = self.base_params 201 | 202 | endpoint = self.base_endpoint.format('account') 203 | 204 | res = self._query_hunter(endpoint, params, raw=raw) 205 | if raw: 206 | return res 207 | 208 | res['calls']['left'] = res['calls']['available'] - res['calls']['used'] 209 | 210 | return res 211 | 212 | def get_leads(self, offset=None, limit=None, lead_list_id=None, 213 | first_name=None, last_name=None, email=None, company=None, 214 | phone_number=None, twitter=None): 215 | """ 216 | Gives back all the leads saved in your account. 217 | 218 | :param offset: Number of leads to skip. 219 | 220 | :param limit: Maximum number of leads to return. 221 | 222 | :param lead_list_id: Id of a lead list to query leads on. 223 | 224 | :param first_name: First name to filter on. 225 | 226 | :param last_name: Last name to filter on. 227 | 228 | :param email: Email to filter on. 229 | 230 | :param company: Company to filter on. 231 | 232 | :param phone_number: Phone number to filter on. 233 | 234 | :param twitter: Twitter account to filter on. 235 | 236 | :return: All leads found as a dict. 237 | """ 238 | args = locals() 239 | args_params = dict((key, value) for key, value in args.items() if value 240 | is not None) 241 | args_params.pop('self') 242 | 243 | params = self.base_params 244 | params.update(args_params) 245 | 246 | endpoint = self.base_endpoint.format('leads') 247 | 248 | return self._query_hunter(endpoint, params) 249 | 250 | def get_lead(self, lead_id): 251 | """ 252 | Get a specific lead saved on your account. 253 | 254 | :param lead_id: Id of the lead to search. Must be defined. 255 | 256 | :return: Lead found as a dict. 257 | """ 258 | params = self.base_params 259 | 260 | endpoint = self.base_endpoint.format('leads/' + str(lead_id)) 261 | 262 | return self._query_hunter(endpoint, params) 263 | 264 | def create_lead(self, first_name, last_name, email=None, position=None, 265 | company=None, company_industry=None, company_size=None, 266 | confidence_score=None, website=None, country_code=None, 267 | postal_code=None, source=None, linkedin_url=None, 268 | phone_number=None, twitter=None, leads_list_id=None): 269 | """ 270 | Create a lead on your account. 271 | 272 | :param first_name: The first name of the lead to create. Must be 273 | defined. 274 | 275 | :param last_name: The last name of the lead to create. Must be defined. 276 | 277 | :param email: The email of the lead to create. 278 | 279 | :param position: The professional position of the lead to create. 280 | 281 | :param company: The company of the lead to create. 282 | 283 | :param company_industry: The type of industry of the company where the 284 | lead works. 285 | 286 | :param company_size: The size of the company where the lead works. 287 | 288 | :param confidence_score: The confidence score of the lead's email. 289 | 290 | :param website: The website of the lead's company. 291 | 292 | :param country_code: The country code of the lead's company. 293 | 294 | :param postal_code: The postal code of the lead's company. 295 | 296 | :param source: The source of the lead's email. 297 | 298 | :param linkedin_url: The URL of the lead's LinkedIn profile. 299 | 300 | :param phone_number: The phone number of the lead to create. 301 | 302 | :param twitter: The lead's Twitter account. 303 | 304 | :param leads_list_id: The id of the leads list where to save the new 305 | lead. 306 | 307 | :return: The newly created lead as a dict. 308 | """ 309 | args = locals() 310 | payload = dict((key, value) for key, value in args.items() if value 311 | is not None) 312 | payload.pop('self') 313 | 314 | params = self.base_params 315 | 316 | endpoint = self.base_endpoint.format('leads') 317 | 318 | return self._query_hunter(endpoint, params, 'post', payload) 319 | 320 | def update_lead(self, lead_id, first_name=None, last_name=None, email=None, 321 | position=None, company=None, company_industry=None, 322 | company_size=None, confidence_score=None, website=None, 323 | country_code=None, postal_code=None, source=None, 324 | linkedin_url=None, phone_number=None, twitter=None, 325 | leads_list_id=None): 326 | """ 327 | Update a lead on your account. 328 | 329 | :param lead_id: The id of the lead to update. Must be defined. 330 | 331 | :param first_name: The first name of the lead to update. Must be 332 | defined. 333 | 334 | :param last_name: The last name of the lead to update. Must be defined. 335 | 336 | :param email: The email of the lead to update. 337 | 338 | :param position: The professional position of the lead to update. 339 | 340 | :param company: The company of the lead to update. 341 | 342 | :param company_industry: The type of industry of the company where the 343 | lead works. 344 | 345 | :param company_size: The size of the company where the lead works. 346 | 347 | :param confidence_score: The confidence score of the lead's email. 348 | 349 | :param website: The website of the lead's company. 350 | 351 | :param country_code: The country code of the lead's company. 352 | 353 | :param postal_code: The postal code of the lead's company. 354 | 355 | :param source: The source of the lead's email. 356 | 357 | :param linkedin_url: The URL of the lead's LinkedIn profile. 358 | 359 | :param phone_number: The phone number of the lead to update. 360 | 361 | :param twitter: The lead's Twitter account. 362 | 363 | :param leads_list_id: The id of the leads list where to save the new 364 | lead. 365 | 366 | :return: The newly updated lead as a dict. 367 | """ 368 | args = locals() 369 | payload = dict((key, value) for key, value in args.items() if value 370 | is not None) 371 | payload.pop('self') 372 | payload.pop('lead_id') 373 | 374 | params = self.base_params 375 | 376 | endpoint = self.base_endpoint.format('leads/' + str(lead_id)) 377 | 378 | return self._query_hunter(endpoint, params, 'put', payload) 379 | 380 | def delete_lead(self, lead_id): 381 | """ 382 | Delete a specific lead saved on your account. 383 | 384 | :param lead_id: Id of the lead to delete. Must be defined. 385 | 386 | :return: 204 Response. 387 | """ 388 | params = self.base_params 389 | 390 | endpoint = self.base_endpoint.format('leads/' + str(lead_id)) 391 | 392 | return self._query_hunter(endpoint, params, 'delete') 393 | 394 | def get_leads_lists(self, offset=None, limit=None): 395 | """ 396 | Gives back all the leads lists saved on your account. 397 | 398 | :param offset: Number of lists to skip. 399 | 400 | :param limit: Maximum number of lists to return. 401 | 402 | :return: Leads lists found as a dict. 403 | """ 404 | params = self.base_params 405 | 406 | if offset: 407 | params['offset'] = offset 408 | if limit: 409 | params['limit'] = limit 410 | 411 | endpoint = self.base_endpoint.format('leads_lists') 412 | 413 | return self._query_hunter(endpoint, params) 414 | 415 | def get_leads_list(self, leads_list_id): 416 | """ 417 | Gives back a specific leads list saved on your account. 418 | 419 | :param leads_list_id: The id of the list to return. 420 | 421 | :return: Leads list found as a dict. 422 | """ 423 | params = self.base_params 424 | 425 | endpoint = self.base_endpoint.format( 426 | 'leads_lists/' + 427 | str(leads_list_id) 428 | ) 429 | 430 | return self._query_hunter(endpoint, params) 431 | 432 | def create_leads_list(self, name, team_id=None): 433 | """ 434 | Create a leads list. 435 | 436 | :param name: Name of the list to create. Must be defined. 437 | 438 | :param team_id: The id of the list to share this list with. 439 | 440 | :return: The created leads list as a dict. 441 | """ 442 | params = self.base_params 443 | 444 | payload = {'name': name} 445 | if team_id: 446 | payload['team_id'] = team_id 447 | 448 | endpoint = self.base_endpoint.format('leads_lists') 449 | 450 | return self._query_hunter(endpoint, params, 'post', payload) 451 | 452 | def update_leads_list(self, leads_list_id, name, team_id=None): 453 | """ 454 | Update a leads list. 455 | 456 | :param leads_list_id: The id of the list to update. 457 | 458 | :param name: Name of the list to update. Must be defined. 459 | 460 | :param team_id: The id of the list to share this list with. 461 | 462 | :return: 204 Response. 463 | """ 464 | params = self.base_params 465 | 466 | payload = {'name': name} 467 | if team_id: 468 | payload['team_id'] = team_id 469 | 470 | endpoint = self.base_endpoint.format( 471 | 'leads_lists/' + str(leads_list_id)) 472 | 473 | return self._query_hunter(endpoint, params, 'put', payload) 474 | 475 | def delete_leads_list(self, leads_list_id): 476 | """ 477 | Delete a leads list. 478 | 479 | :param leads_list_id: The id of the list to delete. 480 | 481 | :return: 204 Response. 482 | """ 483 | params = self.base_params 484 | 485 | endpoint = self.base_endpoint.format( 486 | 'leads_lists/' + 487 | str(leads_list_id) 488 | ) 489 | 490 | return self._query_hunter(endpoint, params, 'delete') 491 | --------------------------------------------------------------------------------