Can you schedule a recurring CSV import in HubSpot?
HubSpot cannot schedule a recurring CSV import from its import tool. Repeat a past import copies the mappings but still needs someone to upload the file. To automate it, call the CRM Imports API on a schedule, use an automation platform that reads the file, or build a pipeline that validates rows and upserts them through the CRM API.
By HubReven
The question usually arrives in this form: "We get the same export every Monday. Can HubSpot just pick it up?" The honest answer is no, not on its own. The useful answer is that there are three ways to get there, and they differ mainly in what happens when the file is wrong.
What HubSpot gives you natively
The import tool. Upload a file, map the columns, run it. Good for one off loads. Nothing about it runs without a person.
Repeat a past import. This copies the settings and column mappings from an earlier import, so the second upload is fewer clicks. The file has to have the same columns, and imports older than six months are not available as templates. It saves setup time. It does not schedule anything.
Scheduled backups exist on Enterprise, but they are exports, not imports. They are the opposite direction.
So the scheduling has to live outside the import tool. Here are the options, cheapest first.
Option 1: the Imports API on a schedule
HubSpot exposes the same import engine through the CRM Imports API. You send the file and the mapping as a multipart request, HubSpot processes it, and you poll for the result. Put that call in a scheduled job and you have a recurring import.
const importRequest = {
name: `Weekly orders ${new Date().toISOString().slice(0, 10)}`,
files: [
{
fileName: 'orders.csv',
fileFormat: 'CSV',
fileImportPage: {
hasHeader: true,
columnMappings: [
{
columnName: 'Email',
columnObjectTypeId: '0-1',
propertyName: 'email',
idColumnType: 'HUBSPOT_ALTERNATE_ID',
},
{ columnName: 'First Name', columnObjectTypeId: '0-1', propertyName: 'firstname' },
{ columnName: 'Plan', columnObjectTypeId: '0-1', propertyName: 'plan_tier' },
],
},
},
],
}
const form = new FormData()
form.append('importRequest', JSON.stringify(importRequest))
form.append('files', new Blob([csv], { type: 'text/csv' }), 'orders.csv')
const res = await fetch('https://api.hubapi.com/crm/v3/imports', {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.HUBSPOT_TOKEN}` },
body: form,
})
const { id } = await res.json()
// Poll GET /crm/v3/imports/{id} until state is DONE or FAILEDThe token comes from a private app with the imports scope. Imports default to upsert, so rows that match an existing record update it instead of creating a new one, as long as the file carries an identifier HubSpot can match on. Files can be up to 1,048,576 rows or 512 MB.
What it is good at: large files, very little code, and the result shows up in HubSpot's import history like any manual import.
What it does not do: check the file before it goes in. If the source system renames a column or changes a date format, HubSpot will do its best with what it gets, and you find out from the import errors afterwards. The matching rules are also HubSpot's, which means rows without an email create duplicates unless you map a unique value property.
Option 2: an automation platform
Zapier, Make and similar tools can watch a folder, an inbox or an SFTP server, parse the CSV, and create or update records row by row.
What it is good at: no code, quick to set up, fine for a few hundred rows.
What it does not do: scale gracefully. Row by row means one task per row, which gets expensive at a few thousand rows a week and slow against HubSpot's rate limits. Error handling is whatever the platform's retry setting is, and a failed row tends to disappear into a task history nobody reads.
Option 3: a validating pipeline
This is the version where the file is treated as an integration interface rather than a chore: pick it up, validate every row against an expected schema, upsert the valid rows through the batch API on a stable external ID, and quarantine the invalid ones into an exception report someone can act on. It also alerts when the file does not arrive at all, which is the failure the other two options never notice.
We have written up the full architecture in how to sync a system to HubSpot when it only exports a CSV.
What it is good at: anything that matters. Revenue data, orders, account status, anything a sales rep will act on.
What it costs: real engineering time, typically two to three weeks for one source.
How to choose
| Imports API | Automation platform | Pipeline | |
|---|---|---|---|
| Volume | Very large | Small | Large |
| Validates before writing | No | Partly | Yes |
| Tells you which rows failed and why | In HubSpot's error file | In task history | In an exception report |
| Notices a missing file | No | No | Yes |
| Build effort | A day | An afternoon | Weeks |
A reasonable rule: if a bad file would only cost you a cleanup afternoon, the Imports API on a cron is enough. If a bad file would put wrong numbers in front of sales or finance, build the pipeline.
Whichever you pick, give every record a stable external ID from the source system and match on it. That single decision is what makes a recurring import safe to rerun.
Frequently asked questions
Can HubSpot schedule a recurring import?
No. The import tool and Repeat a past import both need a person to upload the file each time. To run an import on a schedule, call the CRM Imports API from a scheduled job, use an automation platform, or build a pipeline that writes through the CRM API.
What does Repeat a past import do in HubSpot?
It reuses an earlier import's settings and column mappings as a template, so a file with the same columns needs fewer steps to import. Imports older than six months cannot be used as templates, and it does not run automatically.
Does the HubSpot Imports API update existing records?
Yes. The default operation is upsert, which updates records it can match and creates the rest. You can restrict it to create only or update only per object. Matching uses the record ID, email, company domain or a unique value property mapped in the request.
How do I stop a recurring import from creating duplicates?
Match on a stable identifier rather than on names. Store the source system's ID in a unique value property in HubSpot and map that column as the identifier, so importing the same row twice updates the record instead of creating a second one.
Get the next one