Bulk Import CRM Data help

I’m currently not receiving any data back at all while trying to use the CRM api.

I’ve created a python script based on the documentation provided (Accounts Dashboard | HubSpot), but I’m not having any luck.

According to the documentation, it should be able to take in a CSV or Excel Spreadsheet based on the fileFormat value. I’ve tried both and didn’t get a response back from either.

When I checked the endpoints tab to find an example of it being used, there is unfortunately not one provided.

The purpose of this script was to automate the input of excel/csv files that contain contact data for our group. It pulls the provided csv file in this case and submits a post request. I’ve verified that all the column names are correct, as well as their associated propertyNames. The file it’s fetching it in the current directory, it has the correct matching data.

import requests
import json

post_url = 'http://api.hubapi.com/crm/v3/imports?hapikey=c76....901'

latest_file = "thisIsTestData.csv"

headers = {'accept': 'application/json'}

data = {
 "name": "import_contacts",
 "files": [
 {
 "fileName": latest_file,
 "fileFormat": "CSV",
 "fileImportPage": {
 "hasHeader": True,
 "columnMappings": [
 {
 "ignored": False,
 "columnName": "FirstName",
 "idColumnType": None,
 "propertName": "firstname",
 "foreignKeyType": None,
 "columnObjectType": "CONTACT",
 "associationIdentifiedColumn": False
 },
 {
 "ignored": False,
 "columnName": "Web Site URL",
 "idColumnType": None,
 "propertyName": "website",
 "foreignKeyType": None,
 "columnObjectType": "CONTACT",
 "associationIdentifiedColumn": False
 },
 {
 "ignored": False,
 "columnName": "Ad_Age",
 "idColumnType": None,
 "propertyName": "ad_age",
 "foreignKeyType": None,
 "columnObjectType": "CONTACT",
 "associationIdentifiedColumn": False
 },
 {
 "ignored": False,
 "columnName": "City",
 "idColumnType": None,
 "propertyName": "city",
 "foreignKeyType": None,
 "columnObjectType": "CONTACT",
 "associationIdentifiedColumn": False
 },
 {
 "ignored": False,
 "columnName": "Mobile Phone Number",
 "idColumnType": None,
 "propertyName": "mobilephone",
 "foreignKeyType": None,
 "columnObjectType": "CONTACT",
 "associationIdentifiedColumn": False
 },
 ]
 }
 }
 ]
}

r = requests.post(url=post_url, data=data, headers=headers)

print(r.text)

Is there something I’m missing here? Any help would be appreciated on this.

Hey @JakeBreaksStuff,

Taking a look at your code, I believe the issue is because you’re using:

application/json as the content type header.

Could you change that to content-type: multipart/form-data and see if it works?

Hey @WendyGoh ,

I’d tried fiddling with the header value since I posted this question. Changing the header value to multipart/form-data, and in response I get a ERROR 400 return.

Hey @JakeBreaksStuff,

In order for me to furthre troubleshoot this, could you share with me the full error message?

Absolutely. To start, I’ve added the following print statements.

print(r.text)
print(r.json)
print(r.status_code)

When I run my program, I receive the following error lines.

<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
<title>Error 400 Bad Request</title>
</head>
<body><h2>HTTP ERROR 400</h2>
<p>Problem accessing /crm/v3/imports. Reason:
<pre> Bad Request</pre></p>
</body>
</html>

<bound method Response.json of <Response [400]>>

400

As you can see, this is an error 400, indicating that the server could not process my request. To assist further, I will include my updates to this point.

import requests
import json

post_url = 'http://api.hubapi.com/crm/v3/imports?hapikey=c76....901'

latest_file = "thisIsTestData.csv"

headers = {"Content-Type": "multipart/form-data"}

data = {
 "name": "import_contacts",
 "files": [
 {
 "fileName": latest_file,
 "fileFormat": "CSV",
 "fileImportPage": {
 "hasHeader": True,
 "columnMappings": [
 {
 "ignored": False,
 "columnName": "FirstName",
 "idColumnType": None,
 "propertName": "firstname",
 "foreignKeyType": None,
 "columnObjectType": "CONTACT",
 "associationIdentifiedColumn": False
 },
 {
 "ignored": False,
 "columnName": "Web Site URL",
 "idColumnType": None,
 "propertyName": "website",
 "foreignKeyType": None,
 "columnObjectType": "CONTACT",
 "associationIdentifiedColumn": False
 },
 {
 "ignored": False,
 "columnName": "Ad_Age",
 "idColumnType": None,
 "propertyName": "ad_age",
 "foreignKeyType": None,
 "columnObjectType": "CONTACT",
 "associationIdentifiedColumn": False
 },
 {
 "ignored": False,
 "columnName": "City",
 "idColumnType": None,
 "propertyName": "city",
 "foreignKeyType": None,
 "columnObjectType": "CONTACT",
 "associationIdentifiedColumn": False
 },
 {
 "ignored": False,
 "columnName": "Mobile Phone Number",
 "idColumnType": None,
 "propertyName": "mobilephone",
 "foreignKeyType": None,
 "columnObjectType": "CONTACT",
 "associationIdentifiedColumn": False
 },
 ]
 }
 }
 ]
}

r = requests.post(url=post_url, data=data, headers=headers)

print(r.text)
print(r.json)
print(r.status_code)

Which returns a 400 response code.

Maybe you can assist further in fixing the documentation present on the developers page.

The python example for this api is incomplete, as it never uses any form of requests to submit the data to the server. This is not an issue present with the PHP and Shell Script examples, however, when they both take the files in, they’re “important_file.csv”, but when the json config line occurs, the file name is different “final_emails.csv” from the file that was read in.

Hey @JakeBreaksStuff,

Upon digging further, I was able to get it working with the following python code

import requests
import json

url = "https://api.hubapi.com/crm/v3/imports?hapikey={{hapikey}}"

data = {
 "name": "test_import",
 "files": [
 {
 "fileName": "final_emails.csv",
 "fileFormat": "CSV",
 "fileImportPage": {
 "hasHeader": True,
 "columnMappings": [
 {
 "ignored": False,
 "columnName": "First Name",
 "idColumnType": None,
 "propertyName": "firstname",
 "foreignKeyType": None,
 "columnObjectType": "CONTACT",
 "associationIdentifierColumn": False
 },
 {
 "ignored": False,
 "columnName": "Email",
 "idColumnType": "HUBSPOT_ALTERNATE_ID",
 "propertyName": "email",
 "foreignKeyType": None,
 "columnObjectType": "CONTACT",
 "associationIdentifierColumn": False
 }
		]
	}
}
]}

datastring = json.dumps(data)

payload = {"importRequest": datastring}

files = [
 ('files', open('file path','rb'))
]

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

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

Could you try and see if it works?

Additionally, I do agree that there’s opportunity to update the documentation. Thank you for the feedback!

Gave it a shot with your changes, I still unfortunately received a 400 Bad Request error.

import requests

import json

post_url = 'http://api.hubapi.com/crm/v3/imports?hapikey={hapikeyHere}'

latest_file = "test_zone/TestData.csv"
# print("Regular file with extension: "+latest_file)

data = {
 "name": "import_contacts",
 "files": [
 {
 "fileName": latest_file,
 "fileFormat": "CSV",
 "fileImportPage": {
 "hasHeader": True,
 "columnMappings": [
 {
 "ignored": False,
 "columnName": "FirstName",
 "idColumnType": None,
 "propertName": "firstname",
 "foreignKeyType": None,
 "columnObjectType": "CONTACT",
 "associationIdentifiedColumn": False
 },
 {
 "ignored": False,
 "columnName": "Web Site URL",
 "idColumnType": None,
 "propertyName": "website",
 "foreignKeyType": None,
 "columnObjectType": "CONTACT",
 "associationIdentifiedColumn": False
 },
 {
 "ignored": False,
 "columnName": "Ad_Age",
 "idColumnType": None,
 "propertyName": "ad_age",
 "foreignKeyType": None,
 "columnObjectType": "CONTACT",
 "associationIdentifiedColumn": False
 },
 {
 "ignored": False,
 "columnName": "City",
 "idColumnType": None,
 "propertyName": "city",
 "foreignKeyType": None,
 "columnObjectType": "CONTACT",
 "associationIdentifiedColumn": False
 },
 {
 "ignored": False,
 "columnName": "Mobile Phone Number",
 "idColumnType": None,
 "propertyName": "mobilephone",
 "foreignKeyType": None,
 "columnObjectType": "CONTACT",
 "associationIdentifiedColumn": False
 },
 ]
 }
 }
 ]
}

datastring = json.dumps(data)

payload = {"importRequest": datastring}

files = [
 ('files', open('test_zone/TestData.csv', 'rb'))
]

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

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

In response to this I receive the following error message.

b'<html>\n<head>\n<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>\n<title>Error 400 Bad Request</title>\n</head>\n<body><h2>HTTP ERROR 400</h2>\n<p>Problem accessing /crm/v3/imports. Reason:\n<pre> Bad Request</pre></p>\n</body>\n</html>\n'

400

The test data csv is sitting one directory lower than the python code. I’ve also tried with having the python code and the testdata.csv file sitting in the same directory. Both resulted in the above 400 Bad request error.

I’ve been doing a bit more thinking. If this worked on your end, it might be the data formatting. I’ll include an image of what my csv of test data looks like.

Could you please give it a go with my test data, and updated code please?

29565iD5422B88B0F5DFB1.png

Hey @JakeBreaksStuff,

A couple of things here that jumps out to me:

1. The excel file consists of 7 columns in which only 5 column was mapped. Namely: website url, firstname, ad ge, mobile phone number and city.

2. The filename key value should match with the file csv file name

As such, what I did was:

1. Removed the create date and stock photo column on the TestData.csv excel sheet:

2. Modified the code to match “fileName”: “TestData.csv”

import requests
import json

url = "https://api.hubapi.com/crm/v3/imports?hapikey={{hapikey}}"

data = {
 "name": "test_import",
 "files": [
 {
 "fileName": "TestData.csv",
 "fileFormat": "CSV",
 "fileImportPage": {
 "hasHeader": True,
 "columnMappings": [
 {
 "ignored": False,
 "columnName": "FirstName",
 "idColumnType": None,
 "propertyName": "firstname",
 "foreignKeyType": None,
 "columnObjectType": "CONTACT",
 "associationIdentifierColumn": False
 },
 {
 "ignored": False,
 "columnName": "Web Site URL",
 "idColumnType": None,
 "propertyName": "website",
 "foreignKeyType": None,
 "columnObjectType": "CONTACT",
 "associationIdentifiedColumn": False
 },
 {
 "ignored": False,
 "columnName": "Ad_Age",
 "idColumnType": None,
 "propertyName": "ad_age",
 "foreignKeyType": None,
 "columnObjectType": "CONTACT",
 "associationIdentifiedColumn": False
 },
 {
 "ignored": False,
 "columnName": "City",
 "idColumnType": None,
 "propertyName": "city",
 "foreignKeyType": None,
 "columnObjectType": "CONTACT",
 "associationIdentifiedColumn": False
 },
 {
 "ignored": False,
 "columnName": "Mobile Phone Number",
 "idColumnType": None,
 "propertyName": "mobilephone",
 "foreignKeyType": None,
 "columnObjectType": "CONTACT",
 "associationIdentifiedColumn": False
 }
		]
	}
}
]}

datastring = json.dumps(data)

payload = {"importRequest": datastring}

files = [
 ('files', open('/Users/wgoh/Documents/TestData.csv','rb'))
]

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

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

and it works. Could you try and see if it works for you?

Also, I copied the full file path of the csv file.

Hey @WendyGoh ,

You have been extremely helpful. It looks like it’s finally been fixed on my end. Thank you very much! My issues had been the following:

1) When I was making the request, I’d been using the relative path, instead of the absolute path. (Changing to absolute fixed the 400 error)

2) The data portion formatting, they need to be the same as they’re listed in that order as the csv file. I’d swapped First Name and website accidentally, and the two were merged on the submission.

3) Any columns that I couldnt map to were not skipped by default (as I’d foolishly assumed), once I had added in

"ignored": True,
"columnName": "InsertColumnNameToBeSkippedHere"

it worked.

Thank you very much for your time on this.

Hi Wendy,

I have been having the same issue as the OP but the accepted solution is not working for me.

I am receiving the error:

b’<html>\n<head>\n<meta http-equiv=“Content-Type” content=“text/html;charset=utf-8”/>\n<title>Error 400 Bad Request</title>\n</head>\n<body><h2>HTTP ERROR 400</h2>\n<p>Problem accessing /crm/v3/imports. Reason:\n<pre> Bad Request</pre></p>\n</body>\n</html>\n’ 400

My code is:

import requests
import json
import os

url = "https://api.hubapi.com/crm/v3/imports?hapikey=######"

data = {
 "name": "test_import",
 "files": [
 {
 "fileName": "TestData.csv",
 "fileFormat": "CSV",
 "fileImportPage": {
 "hasHeader": True,
 "columnMappings": [
 {
 "ignored": False,
 "columnName": "customer_id",
 "idColumnType": None,
 "propertyName": "CUSTOMER ID - V",
 "foreignKeyType": None,
 "columnObjectType": "CONTACT",
 "associationIdentifierColumn": False
 },
 {
 "ignored": False,
 "columnName": "email",
 "idColumnType": "HUBSPOT_ALTERNATE_ID",
 "propertyName": "EMAIL",
 "foreignKeyType": None,
 "columnObjectType": "CONTACT",
 "associationIdentifierColumn": False
 },
 {
 "ignored": False,
 "columnName": "full_name",
 "idColumnType": None,
 "propertyName": "NAME",
 "foreignKeyType": None,
 "columnObjectType": "CONTACT",
 "associationIdentifierColumn": False
 }
 ]
 }
 }
 ]}

datastring = json.dumps(data)

payload = {"importRequest": datastring}

files = [
 ('files', open('Somm\REPORTS\TestData.csv','rb'))
]

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

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

and my data is:

customer_id email full_name
1234 test1@hs.com test1
2345 test2@cs.com test2
3456 test3@hs.com test3

Can you see what I’m missing here?

Thanks!

@KEntwistle ,

Confirm that you are using the propertyName and not the property label.

The name will have no spaces which leads me to believe that this specific one is using the label:

"propertyName": "CUSTOMER ID - V"

You can find the property name in the portal settings.

Click settings

On left sidebar, scroll to properties

Search for the property and click on it.

You should see </> next to it. Clicking it will provide the propertyName

Yes!! You absolute legend, it now works using the internal property name.

Thank You :slightly_smiling_face: