Skip to content

Using the CRM Imports API to Import Records in Bulk into a Dad-abase

Customer Relationship Management (CRM) systems, like HubSpot, use and extract insights from business data to aid interactions with customers and streamline the business's operations. CRMs store contact and customer information, but they often also store important information specific to the business. For example: an organization with summer camps may have an object for each camp so they can associate contacts and equipment to individual camps. This helps them with communicating to their contacts as well as managing their business operations. 

If you didn't know, dad jokes are part of HubSpot's culture. Not everyone loves them, but enough do that there's a thriving internal chat channel dedicated to them. A group of HubSpotters, in their off-work hours, banded together to collect dad jokes from our internal chat to build a Dad-abase. We've collected over 1,100 jokes. If you've used ChatSpot.ai you've probably read some of them. Now, what do punny jokes have to do with CRM? What if you had a handy place to store all of your jokes? What if you could avoid telling someone a joke they've already heard before? How would you keep track of all that?  Well, while it's a silly use-case, a CRM is perfectly equipped for this. 

Where do dads and HubSpotters store their jokes?

In a Dad-abase 

🥁

Look, I've got two goals here, show you how easy it is to automate getting your data into HubSpot and to make you laugh/groan. By the end of this article, if I didn't help you Grow Better, I'll be darned if I didn't at least help you groan better.

Getting your data into HubSpot

Your business probably doesn't need to store jokes - but you can easily substitute jokes with an object type that is relevant to your specific business and its operations. The steps for importing are the same. With that many jokes to enter into HubSpot, manual record creation just won't do. We're gonna need to break out the APIs. 

For one-off or smaller data syncing operations, the APIs for creating and updating individual records may be fine; however, if you're updating more than 100 records or are nearing your API limits, it might be a good idea to use the HubSpot CRM Imports API instead. This API enables you to bulk create records with fewer requests. Using the CRM Imports API you can import up to 80,000,000 rows per day total (Individual import files are limited to 1,048,576 rows or 512 MB, whichever is reached first).

The CRM Imports API allows you to import a very high quantity of records with only one API call by sending a CSV or Excel file to HubSpot for processing. This is better than making an API call per record you wish to add/update; Which increases your odds of hitting your rate limit, and might be a high load on your app.

I have 150 of the dad jokes collected by HubSpotters ready to upload. The dad jokes are in a CSV with two columns "setup" and "punchline". We're going to use the Imports API instead of making 150 separate API calls. The Imports API works very similarly to the in-app import tool. The advantage of using an API is that you can automate away the need for a human to manually upload the data and prepare the data for import.

Cop: I'm arresting you for illegally downloading the entire Wikipedia database.

Arrested person: Wait, I can explain everything!

🥁

Create the object type if it doesn't already exist in HubSpot

In order to import data, HubSpot needs to have a schema for that object type. I've gone ahead and created a custom object type called Dad Jokes. You can create a custom object schema in-app, using the CLI, or using the HubSpot APIs.  Using the APIs provides more granular control, but the data model was simple for our dad-abase. We only have 2 properties: a setup, and a punchline, which I’ve created in-app.

If it's your first time creating a custom object, I recommend reading How to think like an architect, by building scalable custom objects first. You'll learn a lot of best practices.

dadabase1-1

I boiled a funny bone once. It turned into a laughing stock.

For those not in the medical profession, that's a humerus joke.

🥁

How to use the CRM Imports API

We're going to use Postman to speed things up, the end result is that we're going to write Node JavaScript to make a POST request to the Imports API

Create/Use a private app or OAuth token

To use the Imports API, we need to have a private app or OAuth token with the crm.import scope.

For importing dad jokes, I'm going to use a private app.  I don't see dad jokes being something we need in multiple HubSpot accounts, so a private app which is specific to just one account is perfect. If you'd like to learn more about the differences and similarities between private apps and OAuth tokens, David Adams has written an excellent explainer.

If you ever get locked out of your house, just talk to the lock calmly.

Communication is key.

🥁

Get it? A joke about keys, because we're talking about authentication. You get it.

In our request, we're going to include two parameters. The first is an object called importRequest, which will include metadata for the import.

Inside of the importRequest, we'll provide two properties: 

  • A name property - which will be used to identify this import. This will show inside the app and will also help you identify this import using the API if you need to check an import's status.
  • A files property - which is an array of objects. 

There are  two other optional properties:

  • marketableContactImport - you'd include if importing contacts, but you don't need it for other types of data.
  • importOperations - which is an optional field for indicating whether the import should ONLY create, or ONLY update records for a certain object or activity. You would include the objectTypeId for the object/activity and whether you want to only "CREATE" or "UPDATE" records. In today's example, we are importing data for the first time and we want to create records, but if records already existed in our CRM, we'd be okay with them being updated, too so we will leave out this property. That said, when working with information like contacts, it can be really helpful to avoid accidentally making changes you don't intend.

Example importRequest:

{ "name": "January - Dad Jokes", "files": [{ "fileName": "dadjokes.csv", "fileFormat": "CSV", "fileImportPage": { "hasHeader": true, "columnMappings": [{ "columnObjectTypeId": "2-11080335", "columnName": "setup", "propertyName": "setup", "idColumnType": null }, { "columnObjectTypeId": "2-11080335", "columnName": "punchline", "propertyName": "punchline", "idColumnType": null } ] } }] }

For each file you upload, you'll add it to your files array as an object. Each file's object will contain the following properties:

  • fileName - the name of the file you're importing.
  • fileFormat - the file's format. For CSV files, the value is "CSV". For Excel, use a value of "SPREADSHEET". In our case, we have CSV.
  • dateFormat - this communicates the format in which dates in the imported file are: MONTH_DAY_YEAR, DAY_MONTH_YEAR or YEAR_MONTH_DAY. Our import in this case doesn't include dates because dad jokes are timeless in my opinion.
  • fileImportPage - this denotes the structure of the CSV or Excel file; declaring if it has a header and also contains a ColumnMappings array.

Mapping your columns

In order for HubSpot to know which columns in your file correlate to which properties in your CRM, you need to pass that information inside of your fileImportPage object, using "columnMappings" which is an array of objects. Each object corresponds to a column in your file that represents a CRM property. 

"fileImportPage": { "hasHeader": true, "columnMappings": [{ "columnObjectTypeId": "2-11080335", "columnName": "setup", "propertyName": "setup", "idColumnType": null }, { "columnObjectTypeId": "2-11080335", "columnName": "punchline", "propertyName": "punchline", "idColumnType": null } ] }
  • columnObjectTypeId - The CRM object type that this property is for. Because we are only importing one object type, the value will be the same for all of your columns.  If you were importing multiple objects, then they would change accordingly. In our case the Object Type Id is for our dad joke custom object. There are two ways to get this ID, you can use the CRM object Schema list API endpoint, or when opening the listing page for that object, the object type id is in the URL.
    https://app.hubspot.com/contacts/{YourAccountId}/objects/{YourObjectTypeId}/views/all/list

    In my account the id is: 2-11080335
  • columnName - The text that appears naming this column in the file itself.
  • propertyName - The CRM property's internal name which this data maps to.
  • idColumnType - If a column contains a unique identifier property, you pass either "HUBSPOT_OBJECT_ID" (if the id in this column is the HubSpot object record id) or "HUBSPOT_ALTERNATE_ID" (If it's a unique identifier other than the record id like an email address or domain name). In our case, none of the columns are forced to be unique so we're going to leave this set to null.

So we know we have two columns, their properties are setup and punchline. To import that data the custom object must have two matching properties.

{ "name": "January - Dad Jokes", "files": [ { "fileName": "dadjokes.csv", "fileFormat": "CSV", "fileImportPage": { "hasHeader": true, "columnMappings": [ { "columnObjectTypeId": "2-11080335", "columnName": "setup", "propertyName": "setup", "idColumnType": null }, { "columnObjectTypeId": "2-11080335", "columnName": "punchline", "propertyName": "punchline", "idColumnType": null } ] } } ] }

We'll now paste this JSON into postman in a body field named importRequest.

In Postman, if uploading files, you hover over the key and click the dropdown next to "Text", and choose "File".

Postman screen. User has opened a dropdown on the "files" property. Dropdown shows two options, Text and File, File is selected. 

This changes the value field to a file picker. Select your file.

Postman interface showing files property checked, and next to it a "select files" button.

Then hit send.

We received a 200 OK response and a response body.

The response body includes more metadata about the file we uploaded, along with a "state" property. This property will likely read as "started" as it did for me. The import itself, depending on how much data you're importing, may take time to process.

If you have a complicated object with different data types or a lot of properties, you're more prone to potentially enter something wrong in your importRequest JSON. Review any errors returned in Postman. I also suggest looking at the logs for your private app. Often it will point out a line number where the issue lies making it easier to debug.

True fact: Spiders are the only web developers that are happy to find bugs.

🥁

If you received a 200, like I did, you can see the status of the import by looking at your imports screen. You can get there from the listing view for your object and then clicking the "import" button.

CRM imports screen showing a past import "January - Dad Jokes" 150 records.

If you need to check the status of your import programmatically, you can make a GET request to the same URL we used to do the import. https://api.hubapi.com/crm/v3/imports/. Leave the body set to none.

Optionally, provide parameters for paging using limit and after.

Postman request with limit property. The resulting response includes a results array.

The response will be a results array containing an object for each import you've done and the status of the import. Included is the importRequestJson which is what you passed when starting the import, making it easy to identify which import is which.

Why did the programmer quit his job?

He just couldn't get arrays.

🥁

When you check the listing page for the object, you should also now see the glorious new records you've added.

CRM interface opened to the Dad Jokes object's listing page, you can see numerous dad jokes.

Congratulations! You've successfully imported your first dad jokes…or maybe something more useful!

Now having done this once, you probably want to automate the import so next time you can just run a script to do it, or better yet, automate it on a recurring basis.

Postman has a really cool feature built in where it can generate a code snippet for you to make the request in your preferred language using a library you prefer. On the right side of the Postman interface is a </> icon for "Code Snippet". Click that and choose the language you want your code in.

Postman interface with code snippets section expanded, and you can see an example request.

Pretty cool huh? You can now pop that code into a HubSpot Serverless Function to enable users to trigger an import from a logged-in CMS page. Even better, place that code in a HubSpot custom code action and have it recur based on a date. On that note, I recently published a post on creating cron jobs on HubSpot which shows you exactly how to do that.

I hope you found this helpful and you'll think of using the imports API versus importing records individually in the future. If you'd like to see more HubSpot developer content from me, I'm on LinkedIn. Additionally, I enjoy a good dad joke or two so feel free to share yours.