Uploading contacts and add/removing them to a list, Python, API

SOLVE
BJacobson
Participant

Solution in my latest post.  Special thank you to @JBeatty 

 

The goal of my project is to import contacts, to make sure they exist in hubspot. Then to add the contact to a list.  Once the list is populated a HubSpot workflow will send an email.  After the email is sent, the list will need to be cleared for use again later.  

 

I have been able to successfully import the contacts using the provided python script.  I am having difficulty using the same .CSV file to add the contacts to the list in question.  

 

I have included the code below with comments.  

Any help is greatly appreciated!

 

 

# This file contains a list of contact records to import.

import requests
import json
import os

# insert your api key here

#List import to HubSpot
url = "https://api.hubapi.com/crm/v3/imports?hapikey=KEY"

data = {
    "name": "HubSpot import name",
    "files": [
        {
            "fileName": "FileName.csv",
            "fileFormat": "CSV",
            "fileImportPage": {
                "hasHeader": True,
                "columnMappings": [
                    {
                        "ignored": False,
                        "columnName": "Last Name",
                        "idColumnType": None,
                        "propertyName": "lastname",
                        "foreignKeyType": None,
                        "columnObjectType": "CONTACT",
                        "associationIdentifierColumn": False
                    },
                    {
                        "ignored": False,
                        "columnName": "First Name",
                        "idColumnType": None,
                        "propertyName": "firstname",
                        "foreignKeyType": None,
                        "columnObjectType": "CONTACT",
                        "associationIdentifierColumn": False
                    },
                    {
                        "ignored": False,
                        "columnName": "Primary Email",
                        "idColumnType": "HUBSPOT_ALTERNATE_ID",
                        "propertyName": "email",
                        "foreignKeyType": None,
                        "columnObjectType": "CONTACT",
                        "associationIdentifierColumn": False
                    },
                    {
                        "ignored": False,
                        "columnName": "Class",
                        "idColumnType": None,
                        "propertyName": "lms_class_full_name",
                        "foreignKeyType": None,
                        "columnObjectType": "CONTACT",
                        "associationIdentifierColumn": False
                    },
                    {
                        "ignored": True,
                        "columnName": "Enrollment",
                        "idColumnType": None,
                        "propertyName": None,
                        "foreignKeyType": None,
                        "columnObjectType": None,
                        "associationIdentifierColumn": False
                    },
                    {
                        "ignored": False,
                        "columnName": "Starts",
                        "idColumnType": None,
                        "propertyName": "lms_class_date_time",
                        "foreignKeyType": None,
                        "columnObjectType": "CONTACT",
                        "associationIdentifierColumn": False
                    },
                    {
                        "ignored": True,
                        "columnName": "Courseware Order Type",
                        "idColumnType": None,
                        "propertyName": None,
                        "foreignKeyType": None,
                        "columnObjectType": None,
                        "associationIdentifierColumn": False
                    },
                    {
                        "ignored": True,
                        "columnName": "Courseware Status",
                        "idColumnType": None,
                        "propertyName": None,
                        "foreignKeyType": None,
                        "columnObjectType": None,
                        "associationIdentifierColumn": False
                    },
                ]
            }
        }
    ]}

datastring = json.dumps(data)

payload = {"importRequest": datastring}

current_dir = os.path.dirname(__file__)
relative_path = "EnrollmentsT.csv"

absolute_file_path = os.path.join(current_dir, relative_path)

files = [
    ('files', open(absolute_file_path, 'r'))
]
print(files)


response = requests.request("POST", url, data=payload, files=files)

print(response.text.encode('utf8'))
print(response.status_code)

#Everything above this is working

#I tried to modify the import contacts script with no success
#The following is to add the contacts to a list and it is NOT working
AddList = "https://api.hubapi.com/contacts/v1/lists/ListID/add?hapikey=KEY"

data = {
#    "name": "6A_import",  '''I dont think this is needed to add contacts to a list'''
    "files": [  #I am trying to add to the list using the contacts email address
        {
            "fileName": "EnrollmentsT.csv",
            "fileFormat": "CSV",
            "fileImportPage": {
                "hasHeader": True,
                "columnMappings": [
                    {
                        "ignored": True,
                        "columnName": "Last Name",
                        "idColumnType": None,
                        "propertyName": "lastname",
                        "foreignKeyType": None,
                        "columnObjectType": "CONTACT",
                        "associationIdentifierColumn": False
                    },
                    {
                        "ignored": True,
                        "columnName": "First Name",
                        "idColumnType": None,
                        "propertyName": "firstname",
                        "foreignKeyType": None,
                        "columnObjectType": "CONTACT",
                        "associationIdentifierColumn": False
                    },
                    {
                        "ignored": False,
                        "columnName": "Primary Email",
                        "idColumnType": "HUBSPOT_ALTERNATE_ID",
                        "propertyName": "email",
                        "foreignKeyType": None,
                        "columnObjectType": "CONTACT",
                        "associationIdentifierColumn": False
                    },
                    {
                        "ignored": True,
                        "columnName": "Class",
                        "idColumnType": None,
                        "propertyName": "lms_class_full_name",
                        "foreignKeyType": None,
                        "columnObjectType": "CONTACT",
                        "associationIdentifierColumn": False
                    },
                    {
                        "ignored": True,
                        "columnName": "Enrollment",
                        "idColumnType": None,
                        "propertyName": None,
                        "foreignKeyType": None,
                        "columnObjectType": None,
                        "associationIdentifierColumn": False
                    },
                    {
                        "ignored": True,
                        "columnName": "Starts",
                        "idColumnType": None,
                        "propertyName": "lms_class_date_time",
                        "foreignKeyType": None,
                        "columnObjectType": "CONTACT",
                        "associationIdentifierColumn": False
                    },
                    {
                        "ignored": True,
                        "columnName": "Courseware Order Type",
                        "idColumnType": None,
                        "propertyName": None,
                        "foreignKeyType": None,
                        "columnObjectType": None,
                        "associationIdentifierColumn": False
                    },
                    {
                        "ignored": True,
                        "columnName": "Courseware Status",
                        "idColumnType": None,
                        "propertyName": None,
                        "foreignKeyType": None,
                        "columnObjectType": None,
                        "associationIdentifierColumn": False
                    },
                ]
            }
        }
    ]}

datastring = json.dumps(data)

payload = {datastring}

current_dir = os.path.dirname(__file__)
relative_path = "EnrollmentsT.csv"

absolute_file_path = os.path.join(current_dir, relative_path)

files = [
    ('files', open(absolute_file_path, 'r'))
]
print(files)


response = requests.request("POST", AddList, data=payload, files=files)

print(response.text.encode('utf8'))
print(response.status_code)

 

 

1 Accepted solution

Accepted Solutions
JBeatty
Solution
Top Contributor | Platinum Partner

@BJacobson 

Here is some example code to get you started. Rember that the add to list API can only work with a maximum of 500 contacts.

 

 

 

import csv
#Example section of my test csv
'''
first_name,last_name,email
Dolli,Drakes,ddrakes0@printfriendly.com
Jyoti,Richie,jrichie1@purevolume.com
Burt,Weekly,bweekly2@disqus.com
Aron,Pembery,apembery3@example.com
Bink,Tomkys,btomkys4@nbcnews.com
Nettle,Wolstenholme,nwolstenholme5@merriam-webster.com
Val,Casol,vcasol6@google.de
Gabby,Ragot,gragot7@rambler.ru
Locke,Lancley,llancley8@ow.ly
'''
#declaring vairable
emailList = ""
with open('contacts.csv') as csv_file: #opening the file
    csv_reader = csv.reader(csv_file, delimiter=',')
    line_count = 0
    for row in csv_reader: 
        if line_count == 0:   #ignoring the headers
            line_count += 1
            pass
        else:
            emailList += "\""+row[2] + "\",\n" #youll need to change this to the correct row
            line_count += 1

#format it as the correct JSON body
#the "[:-2]" is to remove the final new line and comma from the email list
mydata = """{
  "vids": [],
  "emails": [
    """+emailList[:-2]+""" 
  ]
}
"""

print(mydata)

 

 

✔️ Was I able to help answer your question? Help the community by marking it as a solution.

Joshua Beatty
Software Developer with Pearagon

Still have questions? Let's Talk

View solution in original post

8 Replies 8
JBeatty
Top Contributor | Platinum Partner

@BJacobson 

Is it a requirement of the project to add the contacts to the list through the API?

 

If it is not, depending on how you generate your CSV file, you could add a property to the Contacts that the workflow could trigger off instead, or have a workflow trigger off that property and add the Contact to the list.

For example, add a new column to your CSV file that has some Dummy Property with a set value. Then a workflow with an enrollment trigger of if that Dummy Property is equal to that set value. Then the workflow would either send the email, and clear the property, or add them to the list, where another workflow could trigger.

 

If the contacts must be added to the list through an API call, the issue is here the differences between the two API calls you are making. Looking at the imports API, the endpoint that you are calling, "POST /crm/v3/imports/", has a parameter "files" where you pass in the binary file that is imported. The lists API, at the endpoint you are calling "POST /contacts/v1/lists/:list_id/add" does not have a parameter "files" which you are attempting to use. 

Instead, you will need to parse your CSV file into a request that matches the schema the API wants. 

 

From the API Page:

 

 

 

Example URL:
https://api.hubapi.com/contacts/v1/lists/226468/add?hapikey=demo

The JSON sample below represents the contact IDs (VID) or emails that 
you need to pass in the body of your POST request to add the 
contact to your list:

{
  "vids": [
    3057124,
	5524274
  ],
  "emails": [
    "testingapis@hubspot.com",
    "testingapisSecondEmail@hubspot.com"
  ]
}

 

 

 

 

Though keep in mind that even though the vids is a required parameter you can just keep in empty.

 

For parsing CSV files I recommend using the inbuilt python CSV library and grabbing all of the emails, then separating them with a comma, then formatting them as JSON like above. Here is a useful article on how to use that library. In the end, your request would look something more like this, where "mydata" is formatted like the JSON above:

 

 

 

 

response = requests.request("POST", AddList, data=mydata)

 

 

 

✔️ Was I able to help answer your question? Help the community by marking it as a solution.

Joshua Beatty
Software Developer with Pearagon

Still have questions? Let's Talk

BJacobson
Participant

Thank you for the advice!  I'll be giving it a try.  I'm still pretty new to python and even greener with hubspot.  When I parse the csv file and extract the email addresses, will I want to create a dictionary or just a list?

0 Upvotes
JBeatty
Solution
Top Contributor | Platinum Partner

@BJacobson 

Here is some example code to get you started. Rember that the add to list API can only work with a maximum of 500 contacts.

 

 

 

import csv
#Example section of my test csv
'''
first_name,last_name,email
Dolli,Drakes,ddrakes0@printfriendly.com
Jyoti,Richie,jrichie1@purevolume.com
Burt,Weekly,bweekly2@disqus.com
Aron,Pembery,apembery3@example.com
Bink,Tomkys,btomkys4@nbcnews.com
Nettle,Wolstenholme,nwolstenholme5@merriam-webster.com
Val,Casol,vcasol6@google.de
Gabby,Ragot,gragot7@rambler.ru
Locke,Lancley,llancley8@ow.ly
'''
#declaring vairable
emailList = ""
with open('contacts.csv') as csv_file: #opening the file
    csv_reader = csv.reader(csv_file, delimiter=',')
    line_count = 0
    for row in csv_reader: 
        if line_count == 0:   #ignoring the headers
            line_count += 1
            pass
        else:
            emailList += "\""+row[2] + "\",\n" #youll need to change this to the correct row
            line_count += 1

#format it as the correct JSON body
#the "[:-2]" is to remove the final new line and comma from the email list
mydata = """{
  "vids": [],
  "emails": [
    """+emailList[:-2]+""" 
  ]
}
"""

print(mydata)

 

 

✔️ Was I able to help answer your question? Help the community by marking it as a solution.

Joshua Beatty
Software Developer with Pearagon

Still have questions? Let's Talk

View solution in original post

BJacobson
Participant

Everything seems to be working perfectly with the exception of turning the list into a JSON body.  I've tried passing emaiList, mydata, datastring and payload with no success; recieving code 415 (unsupported media type) or "TypeError: memoryview: a bytes-like object is required, not 'str'"

 

emailList = ""

with open("EnrollmentsT.csv") as csv_file:
    csv_reader = csv.reader(csv_file, delimiter=',')
    line_count = 0
    for row in csv_reader:
        if line_count == 0:
            line_count += 1
            pass
        else:
            emailList += "\""+row[2] + "\",\n"
            line_count += 1

mydata = """{
  "vids": [
  ],
  "emails": [
    """+emailList[:-2]+"""
  ]
}"""

datastring = json.dumps(emailList)  #I got the same results using mydata

payload = {datastring}   #i was trying to pass each of the variables here with no success

 

0 Upvotes
JBeatty
Top Contributor | Platinum Partner

The mydata variable is already a JSON string. the python function json.dumps() turns a JSON object into a JSON string. You are attempting to turn a JSON string into a JSON string again. You should be able to just replace your last two lines with:

 

 

 

response = requests.request("POST", AddList, data=mydata)

 

 

 

AddList is the URL you have already set. "data" is the parameter of the python function that excepts a Dictionary, list of tuples, bytes, or file-like object (aka a string). The string mydata is already a json string we have generated and should be able to pass it directly the to request function.

✔️ Was I able to help answer your question? Help the community by marking it as a solution.

Joshua Beatty
Software Developer with Pearagon

Still have questions? Let's Talk

BJacobson
Participant

The list I am trying to add and remove contacts to and from is a static list.  I was told by HubSpot to use the static over dynamic because I will be manually adding/removing the contacts.  Below is the result I get when I run the script.  

 

b'<html>\n<head>\n<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>\n<title>Error 415 Unsupported Media Type</title>\n</head>\n<body><h2>HTTP ERROR 415</h2>\n<p>Problem accessing /contactslistseg/v1/lists/5820/add. Reason:\n<pre> Unsupported Media Type</pre></p>\n</body>\n</html>\n'   #Printing the Response.text.encode(utf8)
415                                                                       #Printing the Response status code
emailList "myem@il.com",                               #Printing the email list
"yourem@il.com",
"flast@here.com",

mydata {                                                              #printing the "mydata" variable
"vids": [
],
"emails": [
"myem@il.com",
"yourem@il.com",
"flast@here.com"
]
}

datastring "{\n \"vids\": [\n ],\n \"emails\": [\n \"myem@il.com\",\n\"yourem@il.com\",\n\"flast@here.com\"\n ]\n}\n"  #Printing the datastring just to see

0 Upvotes
JBeatty
Top Contributor | Platinum Partner

From this post, have you made sure your header contains: Content-Type: application/json

 

 

✔️ Was I able to help answer your question? Help the community by marking it as a solution.

Joshua Beatty
Software Developer with Pearagon

Still have questions? Let's Talk

BJacobson
Participant

That was the winner, thank you so much for all of the help!  For those who are curious the following is the working code to do a CRM import, add contacts to a list and remove contacts from a list using email addresses.  Please read the imbedded comments.

 

import requests
import json
import os
import csv

#CRM Import

# insert your api key here
url = "https://api.hubapi.com/crm/v3/imports?hapikey=APIkey"

data = {
    "name": "Import_Name",   #this should be named the same as the target import
    "files": [
        {
            "fileName": "File Name",   #Name of the file you want to import; I had the best luck when the file was in the same directory as the script
            "fileFormat": "CSV",
            "fileImportPage": {
                "hasHeader": True,
                "columnMappings": [
                    {
                        "ignored": False,
                        "columnName": "Last Name",  #This matches the file
                        "idColumnType": None,
                        "propertyName": "lastname",  #This matches the import field property name
                        "foreignKeyType": None,
                        "columnObjectType": "CONTACT",
                        "associationIdentifierColumn": False
                    },
                    {
                        "ignored": False,
                        "columnName": "First Name",
                        "idColumnType": None,
                        "propertyName": "firstname",
                        "foreignKeyType": None,
                        "columnObjectType": "CONTACT",
                        "associationIdentifierColumn": False
                    },
                    {
                        "ignored": False,
                        "columnName": "Primary Email",
                        "idColumnType": "HUBSPOT_ALTERNATE_ID",
                        "propertyName": "email",
                        "foreignKeyType": None,
                        "columnObjectType": "CONTACT",
                        "associationIdentifierColumn": False
                    },
                ]
            }
        }
    ]}

datastring = json.dumps(data)

payload = {"importRequest": datastring}

current_dir = os.path.dirname(__file__)
relative_path = "File Name"   #File name goes here too

absolute_file_path = os.path.join(current_dir, relative_path)

files = [
    ('files', open(absolute_file_path, 'r'))
]
print(files)


response = requests.request("POST", url, data=payload, files=files)

print(response.text.encode('utf8'))
print(response.status_code)

#Adding contacts to a list using email addresses

#I changed the URL of the list and named the sections with numbers in them
#https://app.hubspot.com/contacts/HUB ID NUMBER/lists/LIST ID NUMBER

AddList = "https://api.hubapi.com/contacts/v1/lists/LIST ID NUMBER/add?hapikey=APIkey"

emailList = ""

with open("File Name") as csv_file:
    csv_reader = csv.reader(csv_file, delimiter=',')
    line_count = 0
    for row in csv_reader:
        if line_count == 0:  #This will skip the header row
            line_count += 1
            pass
        else:
            emailList += "\""+row[2] + "\",\n"  #My emails happen to be in column c, you may need to change this.  If the csv file is open in excel column A=0, B=1, C=2, D=3...
            line_count += 1

mydata = """{
  "vids": [
  ],
  "emails": [
    """+emailList[:-2]+"""   #This will remove the extra spaces and characters at the end
  ]
}
"""

headers = {'content-type': 'application/json'}

response = requests.request("POST", AddList, headers=headers, data=mydata)

print(response.text.encode('utf8'))
print(response.status_code)

#Remove contacts from a list using email addresses

RemList = "https://api.hubapi.com/contacts/v1/lists/LIST ID NUMBER/remove?hapikey=APIkey"

emailList = ""

with open("File Name") as csv_file:
    csv_reader = csv.reader(csv_file, delimiter=',')
    line_count = 0
    for row in csv_reader:
        if line_count == 0:
            line_count += 1
            pass
        else:
            emailList += "\""+row[2] + "\",\n"
            line_count += 1

mydata = """{
  "vids": [
  ],
  "emails": [
    """+emailList[:-2]+"""
  ]
}
"""

headers = {'content-type': 'application/json'}

response = requests.request("POST", RemList, headers=headers, data=mydata)

print(response.text.encode('utf8'))
print(response.status_code)

 

0 Upvotes