This is a library for the Freshsales Suite CRM for Python 3.6+.
It includes the following features from the Freshsales Suite CRM API:
- Contacts
- Marketing Lists
- Accounts
- Deals
- Notes
- Tasks
- Appointments
- Sales Activities
- Products
- Documents
- Selectors
The easiest way to install is from PyPi inside a virtualenv:
-
Create the virtualenv (Python 3.6+ supported) and activate it:
virtualenv env source env/bin/activate -
Install from PyPi:
pip install python-freshworks-crm -
Optionally, run the test suite:
pip install python-freshworks-crm[test] pytest
Please note the domain and API key are not real and the example will not work without changing these.
>>> from freshsales.api import API
>>> a = API('company.myfreshworks.com/crm/sales', 'fsdajfl323kj423rj2')To find your API key, follow Freshworks CRM step-by-step solution article How to find your API key.
The API class provides access to all the methods exposed by the Freshsales Suite CRM API.
Attributes are automatically converted to native Python objects where appropriate:
>>> a.contacts.list_contacts()[0].created_at
datetime.datetime(2023, 12, 5, 14, 7, 44)The Contacts API is accessed by using the methods assigned to the a.contacts instance. Contacts are loaded as instances of the freshsales.models.Contact class.
>>> a.contacts.create_contact(first_name='Jane',
last_name='Sampleton',
emails='[email protected]',
custom_field=custom_field)
<Contact 'Jane Sampleton'>With custom fields:
>>> custom_field = {'custom_field_1': 'custom_value_1',
'custom_field_2': 'custom_value_2'}
>>> a.contacts.create_contact(first_name='Jane',
last_name='Sampleton',
emails='[email protected]',
custom_field=custom_field)
<Contact 'Jane Sampleton'>To access any attribute of the contact, use the dot notation:
>>> contact.first_name
'Jane'>>> a.contacts.view_contact(1)
<Contact 'John Doe'>>>> a.contacts.list_views()
[<View 'All Contacts'>, <View 'My Contacts'>]>>> a.contacts.list_contacts()
[<Contact 'John Doe'>, <Contact 'Jane Sampleton'>]Get specific page of contacts:
>>> a.contacts.list_contacts(page=2, per_page=5)
[<Contact 'John Doe'>, <Contact 'Jane Sampleton'>,...]>>> a.contacts.update_contact(1,
first_name='Jane',
last_name='Updated')
<Contact 'Jane Updated'>>>> a.contacts.upsert_contact(unique_identifier=('emails', '[email protected]'),
first_name='Jane',
last_name='Upserted')
<Contact 'Jane Upserted'>>>> a.contacts.delete_contact(1)
True>>> a.contacts.forget_contact(1)
True>>> a.contacts.list_fields()
[<Field 'first_name'>, <Field 'last_name'>, <Field 'emails'>, ...]>>> a.contacts.list_activities(1)
[<Activity 'Call'>, <Activity 'Email'>, <Activity 'Meeting'>, ...]The Marketing Lists API is accessed by using the methods assigned to the a.lists instance. Marketing Lists are loaded as instances of the freshsales.models.List class.
>>> a.lists.create_list(name='My List')
<List 'My List'>>>> a.lists.view_list(1)
<List 'My List'>>>> a.lists.fetch_all_lists()
[<List 'My List'>, <List 'My Other List'>]>>> a.lists.update_list(1,
name='My Updated List')
<List 'My Updated List'>>>> a.lists.fetch_contacts_from_list(1)
[<Contact 'John Doe'>, <Contact 'Jane Sampleton'>]>>> a.lists.add_contacts_to_list(1, [1, 2])
'2 contacts updated.'>>> a.lists.remove_contacts_from_list(1, [1, 2])
'2 contacts updated.'>>> a.lists.move_contacts_to_list(2, [1, 2])
'2 contacts updated.'The Accounts API is accessed by using the methods assigned to the a.accounts instance. Accounts are loaded as instances of the freshsales.models.Account class.
>>> a.accounts.create_account(name='My Account')
<Account 'My Account'>>>> a.accounts.view_account(1)
<Account 'My Account'>>>> a.accounts.list_views()
[<View 'My View'>, <View 'My Other View'>]>>> a.accounts.list_accounts()
[<Account 'My Account'>, <Account 'My Other Account'>]Note: supports pagination similar to contacts.
>>> a.accounts.update_account(1,
name='My Updated Account')
<Account 'My Updated Account'>>>> a.accounts.upsert_account(unique_identifier=('name', 'My Account'),
name='My Upserted Account')
<Account 'My Upserted Account'>>>> a.accounts.clone_account(1)
<Account 'My Cloned Account'>>>> a.accounts.delete_account(1)
True>>> a.accounts.forget_account(1)
True>>> a.accounts.list_fields()
[<Field 'name'>, <Field 'website'>, <Field 'industry'>, ...]The Deals API is accessed by using the methods assigned to the a.deals instance. Deals are loaded as instances of the freshsales.models.Deal class.
>>> a.deals.create_deal(name='My Deal')
<Deal 'My Deal'>>>> a.deals.view_deal(1)
<Deal 'My Deal'>>>> a.deals.list_views()
[<View 'My View'>, <View 'My Other View'>]>>> a.deals.list_deals()
[<Deal 'My Deal'>, <Deal 'My Other Deal'>]Note: supports pagination similar to contacts.
>>> a.deals.update_deal(1,
name='My Updated Deal')
<Deal 'My Updated Deal'>>>> a.deals.upsert_deal(unique_identifier=('name', 'My Deal'),
name='My Upserted Deal')
<Deal 'My Upserted Deal'>>>> a.deals.clone_deal(1)
<Deal 'My Cloned Deal'>>>> a.deals.delete_deal(1)
True>>> a.deals.forget_deal(1)
True>>> a.deals.list_fields()
[<Field 'name'>, <Field 'amount'>, <Field 'stage'>, ...]The Notes API is accessed by using the methods assigned to the a.notes instance. Notes are loaded as instances of the freshsales.models.Note class.
>>> a.notes.create_note(description='My Note',
targetable_type='Contact',
targetable_id=1)
<Note 'My Note'>>>> a.notes.update_note(1,
description='My Updated Note')
<Note 'My Updated Note'>>>> a.notes.delete_note(1)
TrueThe Tasks API is accessed by using the methods assigned to the a.tasks instance. Tasks are loaded as instances of the freshsales.models.Task class.
>>> a.tasks.create_task(title='Sample Task',
description='This is just a sample task.',
due_date='Tue Jun 21 2099 11:00:00 GMT+0000',
owner_id=1,
targetable_id=1,
targetable_type='Contact')
<Task 'Sample Task'>`>>> a.tasks.view_task(1)
<Task 'Sample Task'>>>> a.tasks.list_tasks()
[<Task 'Sample Task'>, <Task 'My Other Task'>]Note: supports pagination similar to contacts.
>>> a.tasks.update_task(1,
title='Updated Task')
<sk 'Updated Task'>>>> a.tasks.mark_task_as_done(1)
<Task 'Updated Task'>>>> a.tasks.delete_task(1)
TrueThe Appointments API is accessed by using the methods assigned to the a.appointments instance. Appointments are loaded as instances of the freshsales.models.Appointment class.
>>> a.appointments.appointments.create_appointment(
title='Sample Appointment',
description='This is just a sample Appointment.',
from_date='Mon Jun 20 2016 10:30:00 GMT+0530 (IST)',
end_date='Mon Jun 20 2016 11:30:00 GMT+0530 (IST)',
creater_id=1,
time_zone='Chennai',
location='Chennai, TN, India',
targetable_id=1,
targetable_type='Contact')
<Appointment 'Sample Appointment'>>>> a.appointments.view_appointment(1)
<Appointment 'Sample Appointment'>>>> a.appointments.list_appointments()
[<Appointment 'Sample Appointment'>, <Appointment 'My Other Appointment'>]Note: supports pagination similar to contacts.
>>> a.appointments.update_appointment(1,
title='Updated Appointment')
< 'Updated Appointment'>>>> a.appointments.delete_appointment(1)
TrueThe Sales Activities API is accessed by using the methods assigned to the a.sales_activities instance. Sales Activities are loaded as instances of the freshsales.models.SalesActivity class.
>>> a.sales_activities.sales_activities.create_activity(
title='My Activity',
notes='sample',
targetable_id=1,
targetable_type='Contact',
start_date='2017-12-04T17:00:00+05:30',
end_date='2017-12-04T17:30:00+05:30',
owner_id=1,
sales_activity_type_id=1)
<SalesActivity 'My Activity'>>>> a.sales_activities.view_activity(1)
<SalesActivity 'My Activity'>>>> a.sales_activities.list_activities()
[<SalesActivity 'My Activity'>, <SalesActivity 'My Other Activity'>]Note: supports pagination similar to contacts.
>>> a.sales_activities.list_fields()
[<Field 'title'>, <Field 'notes'>, <Field 'start_date'>, ...]>>> a.sales_activities.update_activity(1,
title='Updated Activity')
<Activity 'Updated Activity'>>>> a.sales_activities.delete_activity(1)
TrueThe Products API is accessed by using the methods assigned to the a.products instance. Products are loaded as instances of the freshsales.models.Product class.
>>> a.products.create_product(
name='My Product',
description='This is a sample product',
category='Software',
is_active=True,
product_code='sample_product',
sku_number='sample_sku',
)
<Product 'My Product'>>>> a.products.view_product(1)
<Product 'My Product'>>>> a.products.update_product(1,
name='Updated Product')
<Product 'Updated Product'>>>> a.products.delete_product(1)
True>>> a.products.restore_product(1)
<Product 'Updated Product'>>>> product_pricings = [
{"currency_code": "USD", "unit_price": 2000}
]
product = api.products.edit_product_prices(
1,
product_pricings=product_pricings
)
<Product 'Updated Product'>>>> product = api.products.delete_product_prices(1,
[100, 101])
<Product 'Updated Product'>>>> products = [{"id": 100}]
deal = api.products.add_products_to_deal(
1,
products=products
)
<Deal 'Updated Deal'>>>> products = [{"id": 100, "quantity": 2}]
deal = api.products.edit_deal_products(
1,
products=products
)
<Deal 'Updated Deal'>>>> deal = api.products.delete_all_deal_products(1)
<Deal 'Updated Deal'>The Documents API is accessed by using the methods assigned to the a.documents instance. Documents are loaded as instances of the freshsales.models.Document class.
>>> a.documents.create_document(
deal_id=1,
contact_id=1,
display_name='Sample Document',
document_type='Quote',
cpq_document_template_name='Sample Template',
currency_code='USD'
)
<Document 'Sample Document'>>>> a.documents.view_document(1)
<Document 'Sample Document'>>>> a.documents.update_document(1,
display_name='Updated Document')
<Document 'Updated Document'>>>> a.documents.delete_document(1)
True>>> a.documents.restore_document(1)
<Document 'Updated Document'>>>> a.documents.forget_document(1)
True>>> products = [
{"id": 100, "quantity": 2}
]
document = api.documents.add_products_to_document(
1,
products=products
)
<Document 'Updated Document'>>>> products = [
{"id": 100, "quantity": 3}
]
document = api.documents.edit_products_of_document(
1,
products=products
)
<Document 'Updated Document'>>>> document = api.documents.delete_products_from_document(1)
<Document 'Updated Document'>The Selectors API is accessed by using the methods assigned to the a.selectors instance. Selectors are loaded as instances of the freshsales.models.Selector class.
>>> a.selectors.get_users()
[<User 'John Doe'>, <User 'Jane Doe'>]>>> a.selectors.get_territories()
[<Territory 'North America'>, <Territory 'South America'>]>>> a.selectors.get_deal_stages()
[<DealStage 'Prospecting'>, <DealStage 'Qualification'>]>>> a.selectors.get_currencies()
[<Currency 'USD'>, <Currency 'EUR'>]>>> a.selectors.get_deal_reasons()
[<DealReason 'New Business'>, <DealReason 'Renewal'>]>>> a.selectors.get_deal_types()
[<DealType 'New Business'>, <DealType 'Renewal'>]>>> a.selectors.get_lead_sources()
[<LeadSource 'Website'>, <LeadSource 'Email'>]>>> a.selectors.get_industry_types()
[<IndustryType 'Agriculture'>, <IndustryType 'Construction'>]>>> a.selectors.get_business_types()
[<BusinessType 'Customer'>, <BusinessType 'Competitor'>]>>> a.selectors.get_deal_payment_statuses()
[<DealPaymentStatus 'Paid'>, <DealPaymentStatus 'Unpaid'>]>>> a.selectors.get_deal_products()
[<DealProduct 'Product 1'>, <DealProduct 'Product 2'>]>>> a.selectors.get_deal_pipelines()
[<DealPipeline 'Sales Pipeline'>, <DealPipeline 'Renewal Pipeline'>]>>> a.selectors.get_contact_statuses()
[<ContactStatus 'Open'>, <ContactStatus 'Closed'>]>>> a.selectors.get_sales_activity_types()
[<SalesActivityType 'Call'>, <SalesActivityType 'Email'>]>>> a.selectors.get_sales_activity_entity_types()
[<SalesActivityEntityType 'Contact'>, <SalesActivityEntityType 'Deal'>]>>> a.selectors.get_sales_activity_outcomes()
[<SalesActivityOutcome 'Success'>, <SalesActivityOutcome 'Failure'>]>>> a.selectors.get_lifecycle_stages()
[<LifecycleStage 'Lead'>, <LifecycleStage 'Customer'>]You can efficiently filter through your contacts using the dynamic Filter API. Here's a quick guide on how to utilize it:
The filter accepts a dictionary with a key named filter_rule. This key holds a list of filtering criteria, and each criterion is a dictionary with the following keys:
-
attribute: Specifies the attribute of a contact you want to filter on.
-
operator: Describes the kind of operation you'd like to perform. Examples include:
is_inis_not_inis_beforeis_afteris_in_the_range- ... (more operators can be added as needed)
-
value: The value you want the attribute to be compared against.
You can use sort and sort_type to sort the results. This method also returns a second boolean value that indicates whether there are more results to be fetched. This must be done manually with sorting as the API endpoint does not support pagination and shows only the first 100 results.
Here's a simple example that filters contacts based on their email address:
filter_data = [
{
"attribute": "contact_email.email",
"operator": "is_in",
"value": "[email protected]"
}
]
results = a.search.filter_contacts(filter_data)
print(results) # [<Contact 'James Sampleton'>]To run the tests, you'll need to install the development dependencies:
pip install python-freshworks-crm[test]Then, you can run the tests with:
pytestTravis CI will run the tests against Python 3.6, 3.7, 3.8, 3.9., 3.10, and 3.11
You can also use Tox to run the tests against all supported versions of Python:
toxContributions are welcome! Bulk APIs as well as endpoints such as Files, Search (partially complete), Phone, .. are not implemented yet. Feel free to open a pull request.