08 Oct 2023
Been struggling with making this work for the last couple days - adding contacts to a sequence in Apollo via API.
As my contacts originate from my own database, I understand I need first to create them in Apollo, to get the contact ID, and then add them to the sequence.
doc
Using this doc:
https://apolloio.github.io/apollo-api-docs/?python#add-contacts-to-sequence
code
Here is my code:
email_account_id = "642**********ed86" # verified as correct
class Lead:
def __init__(self, first=None, last=None, email=None, title=None, company=None, linkedin=None):
self.first = first
self.last = last
self.email = email
self.title = title
self.company = company
self.linkedin = linkedin
def __str__(self):
attributes = ["first", "last", "email", "title", "company", "linkedin"]
output = ""
for attr in attributes:
value = getattr(self, attr, None)
if value is not None:
output += f"{attr}: {value}\n"
return output
# FUNCTIONS
# Get all campaigns IDs and names
def get_dict_campaigns():
url = "https://api.apollo.io/v1/emailer_campaigns/search"
data = {
"api_key": APOLLO_API_KEY,
# "q_name": "Nic Test 1"
}
headers = {
'Cache-Control': 'no-cache',
'Content-Type': 'application/json'
}
response = requests.request("POST", url, headers=headers, json=data)
data = response.json()
campaigns = data['emailer_campaigns']
dict_campaign_name_id = {
campaign['name']: campaign['id']
for campaign in campaigns
}
# pp.pprint(dict_campaign_name_id)
return dict_campaign_name_id
campaigns = get_dict_campaigns()
# Create a contact in Apollo
def create_contact(lead):
url = "https://api.apollo.io/v1/contacts"
data = {
"api_key": APOLLO_API_KEY,
"first_name": lead.first,
"last_name": lead.last,
"title": lead.title,
"organization_name": lead.company,
"email": lead.email,
}
headers = {
'Cache-Control': 'no-cache',
'Content-Type': 'application/json'
}
response = requests.request("POST", url, headers=headers, json=data)
contact = response.json()['contact']
contact_id = contact['id']
if verbose:
print(f"\nℹ️ apollo.create_contact: {lead.email} created with ID {contact_id}\n")
return contact_id
# Add contact to sequence
def add_contact_to_sequence(apollo_sequence_name, lead_list): # lead_list is a list of Apollo Contact IDs
global campaigns
global email_account_id
try:
campaign_id = campaigns[apollo_sequence_name]
except KeyError:
print(f"\n\n❌ ERROR with campaign name '{apollo_sequence_name.upper()}' does not exist.\n\n")
campaign_id = None
return False
if campaign_id:
if verbose:
print(f"\nℹ️ apollo.add_contact_to_sequence: adding {len(lead_list)} leads ({lead_list}) to {apollo_sequence_name} (ID: {campaign_id})\n")
url = f"https://api.apollo.io/v1/emailer_campaigns/{campaign_id}/add_contact_ids"
data = {
"api_key": APOLLO_API_KEY,
"async": False,
"contact_ids": lead_list,
"emailer_campaign_id": campaign_id,
"send_email_from_email_account_id": email_account_id,
"sequence_active_in_other_campaigns": False, # Whether to still sequence the contact if he/she is active or paused in another sequence
"sequence_no_email": False, # Whether to still sequence the contact if he/she does not have an email address
"sequence_finished_in_other_campaigns": False # Whether to still sequence the contact if he/she already finished another sequence
}
headers = {
'Cache-Control': 'no-cache',
'Content-Type': 'application/json'
}
response = requests.request("POST", url, headers=headers, json=data)
pp.pprint(response.json())
return True
## TEST 1
# new_lead = create_contact(Lead
# (
# first="Paul",
# last=f"Atreides {ts_db}",
# email="test@test.com",
# title="Mua'dib",
# company="The Sand Company",
# )
# )
# # add_contact_to_sequence("Nic Test 1", [new_lead,])
## TEST 2
add_contact_to_sequence("Nic Test 1", ["651d8c8ee7b4d10001d37d23",]) # https://app.apollo.io/#/contacts/651d8c8ee7b4d10001d37d23
output
response.json():
ℹ️ apollo.add_contact_to_sequence: adding 1 leads (['651d8c8ee7b4d10001d37d23']) to Nic Test 1 (ID: 651c699754e81b00a3f85017)
{ 'contacts': [],
'emailer_campaign': { 'ab_test_step_ids': [],
'active': True,
'archived': False,
'bcc_emails': '',
'bounce_rate': 0.0,
'cc_emails': '',
'click_rate': 0.0,
'contact_email_event_to_stage_mapping': {},
'contact_statuses': { 'active': 0,
'bounced': 0,
'failed': 0,
'finished': 0,
'hard_bounced': 0,
'not_sent': 0,
'paused': 0,
'spam_blocked': 0},
'create_task_if_email_open': False,
'created_at': '2023-10-03T19:20:55.045Z',
'creation_type': 'ai_generated',
'days_to_wait_before_mark_as_response': 40,
'demo_rate': 0.0,
'email_open_trigger_task_threshold': 3,
'emailer_schedule_id': '6426675b43030601320ded88',
'excluded_account_stage_ids': [ '6426675a43030601320ded16',
'6426675a43030601320ded17',
'6426675a43030601320ded18',
'6426675a43030601320ded19'],
'excluded_contact_stage_ids': [ '6426675a43030601320ded13',
'6426675a43030601320ded12',
'6426675a43030601320ded0e',
'6426675a43030601320ded0f'],
'folder_id': None,
'hard_bounce_rate': 0.0,
'id': '651c699754e81b00a3f85017',
'is_performing_poorly': False,
'label_ids': [],
'last_used_at': '2023-10-08T08:05:46.777+00:00',
'loaded_stats': True,
'mark_finished_if_click': False,
'mark_finished_if_interested': True,
'mark_finished_if_reply': True,
'mark_paused_if_ooo': True,
'max_emails_per_day': None,
'name': 'Nic Test 1',
'num_contacts_email_status_extrapolated': 0,
'open_rate': 0.0,
'opt_out_rate': 0.0,
'permissions': 'team_can_use',
'prioritized_by_user': None,
'remind_ab_test_results': False,
'reply_rate': 0.0,
'same_account_reply_delay_days': 30,
'same_account_reply_policy_cd': None,
'sequence_by_exact_daytime': None,
'sequence_ruleset_id': None,
'spam_block_rate': 0.0,
'starred_by_user_ids': [],
'underperforming_touches_count': 0,
'unique_bounced': 0,
'unique_clicked': 0,
'unique_delivered': 0,
'unique_demoed': 0,
'unique_hard_bounced': 0,
'unique_opened': 0,
'unique_replied': 0,
'unique_scheduled': 0,
'unique_spam_blocked': 0,
'unique_unsubscribed': 0,
'user_id': '6426675b43030601320ded86'},
'emailer_steps': [ { 'ab_test_details': {},
'auto_skip_in_x_days': None,
'counts': { 'active': 0,
'bounced': 0,
'finished': 0,
'hard_bounced': 0,
'not_sent': 0,
'paused': 0,
'spam_blocked': 0},
'emailer_campaign_id': '651c699754e81b00a3f85017',
'exact_datetime': None,
'id': '651c699754e81b00a3f8501b',
'max_emails_per_day': None,
'note': '',
'position': 1,
'priority': None,
'type': 'auto_email',
'wait_mode': 'day',
'wait_time': 100},
{ 'ab_test_details': {},
'auto_skip_in_x_days': None,
'counts': { 'active': 0,
'bounced': 0,
'finished': 0,
'hard_bounced': 0,
'not_sent': 0,
'paused': 0,
'spam_blocked': 0},
'emailer_campaign_id': '651c699754e81b00a3f85017',
'exact_datetime': None,
'id': '651c699754e81b00a3f85020',
'max_emails_per_day': None,
'note': '',
'position': 2,
'priority': None,
'type': 'auto_email',
'wait_mode': 'day',
'wait_time': 3},
{ 'ab_test_details': {},
'auto_skip_in_x_days': None,
'counts': { 'active': 0,
'bounced': 0,
'finished': 0,
'hard_bounced': 0,
'not_sent': 0,
'paused': 0,
'spam_blocked': 0},
'emailer_campaign_id': '651c699754e81b00a3f85017',
'exact_datetime': None,
'id': '651c699754e81b00a3f85025',
'max_emails_per_day': None,
'note': '',
'position': 3,
'priority': None,
'type': 'auto_email',
'wait_mode': 'day',
'wait_time': 3}],
'emailer_touches': [ { 'bounce_rate': None,
'click_rate': None,
'demo_rate': None,
'emailer_step_id': '651c699754e81b00a3f8501b',
'emailer_template_id': '651c699754e81b00a3f8501d',
'generic_personalized_opener': None,
'hard_bounce_rate': None,
'has_personalized_opener': None,
'id': '651c699754e81b00a3f8501e',
'include_signature': False,
'open_rate': None,
'opt_out_rate': None,
'personalized_opener_fallback_option': None,
'reply_rate': None,
'spam_block_rate': None,
'status': 'approved',
'type': 'new_thread',
'unique_bounced': 0,
'unique_clicked': 0,
'unique_delivered': 0,
'unique_demoed': 0,
'unique_hard_bounced': 0,
'unique_opened': 0,
'unique_replied': 0,
'unique_scheduled': 1,
'unique_spam_blocked': 0,
'unique_unsubscribed': 0},
{ 'bounce_rate': None,
'click_rate': None,
'demo_rate': None,
'emailer_step_id': '651c699754e81b00a3f85020',
'emailer_template_id': '651c699754e81b00a3f85022',
'generic_personalized_opener': None,
'hard_bounce_rate': None,
'has_personalized_opener': None,
'id': '651c699754e81b00a3f85023',
'include_signature': True,
'open_rate': None,
'opt_out_rate': None,
'personalized_opener_fallback_option': 'send_without_opener',
'reply_rate': None,
'spam_block_rate': None,
'status': 'approved',
'type': 'reply_to_thread',
'unique_bounced': 0,
'unique_clicked': 0,
'unique_delivered': 0,
'unique_demoed': 0,
'unique_hard_bounced': 0,
'unique_opened': 0,
'unique_replied': 0,
'unique_scheduled': 0,
'unique_spam_blocked': 0,
'unique_unsubscribed': 0},
{ 'bounce_rate': None,
'click_rate': None,
'demo_rate': None,
'emailer_step_id': '651c699754e81b00a3f85025',
'emailer_template_id': '651c699854e81b00a3f85027',
'generic_personalized_opener': None,
'hard_bounce_rate': None,
'has_personalized_opener': None,
'id': '651c699854e81b00a3f85028',
'include_signature': True,
'open_rate': None,
'opt_out_rate': None,
'personalized_opener_fallback_option': 'send_without_opener',
'reply_rate': None,
'spam_block_rate': None,
'status': 'approved',
'type': 'reply_to_thread',
'unique_bounced': 0,
'unique_clicked': 0,
'unique_delivered': 0,
'unique_demoed': 0,
'unique_hard_bounced': 0,
'unique_opened': 0,
'unique_replied': 0,
'unique_scheduled': 0,
'unique_spam_blocked': 0,
'unique_unsubscribed': 0},
{ 'bounce_rate': None,
'click_rate': None,
'demo_rate': None,
'emailer_step_id': '651c699754e81b00a3f8501b',
'emailer_template_id': '651d887f6ef23700bc309a13',
'generic_personalized_opener': None,
'hard_bounce_rate': None,
'has_personalized_opener': None,
'id': '651d887f6ef23700bc309a15',
'include_signature': True,
'open_rate': None,
'opt_out_rate': None,
'personalized_opener_fallback_option': 'send_without_opener',
'reply_rate': None,
'spam_block_rate': None,
'status': 'to_be_reviewed',
'type': 'new_thread',
'unique_bounced': 0,
'unique_clicked': 0,
'unique_delivered': 0,
'unique_demoed': 0,
'unique_hard_bounced': 0,
'unique_opened': 0,
'unique_replied': 0,
'unique_scheduled': 0,
'unique_spam_blocked': 0,
'unique_unsubscribed': 0}],
'signals_hash': None,
'team': {'id': '6426675a43030601320ded0a', 'sequences_finder_empty': False}}
Issue is that an empty list is returned as contacts
, and the contact is not added to the sequence.
tried
- creating a new Contact, getting its ID, and adding it to the sequence AND passing the ID of an existing Contact in Apollo
- with & without passing
async
,sequence_active_in_other_campaigns
,sequence_no_email
&sequence_finished_in_other_campaigns
- with & without sequence being active
- adding to different sequences