Skip to main content

Hubspot Connector (Technical Documentation)

Written by Philippe Trussart

Overview

The HubSpot connector provides read-only access to a focused set of HubSpot CRM and marketing datasets:

  • contacts

  • lists

  • email_events

  • marketing_emails

  • email_campaigns

The connector does not create, update, or delete HubSpot records. Available tables depend on the scopes granted to the supplied access token.


Authentication & Permissions

The connector requires a HubSpot private-app:

access_token

API requests use:

Authorization: Bearer {access_token}

against:

https://api.hubapi.com/

The connector checks the scopes associated with the token and only exposes tables for which the required scope is available:

Table

Required scope

contacts

crm.objects.contacts.read

lists

crm.lists.read

email_events

content

marketing_emails

content

email_campaigns

content

The connection test uses:

POST /oauth/v2/private-apps/get/access-token-info

to verify that HubSpot recognizes the access token. A successful test does not necessarily guarantee access to every table if the required scopes are missing.


API Requests

The connector uses both GET and POST requests.

Dataset

Method

Endpoint

Contacts

POST

/crm/v3/objects/contacts/search

Lists

POST

/crm/v3/lists/search

Email events

GET

/email/public/v1/events

Marketing emails

GET

/marketing/v3/emails

Email campaigns

GET

/email/public/v1/campaigns

Email campaign detail

GET

/email/public/v1/campaigns/{id}

There are no PUT, PATCH, or DELETE requests.


Sync Behavior

Most HubSpot datasets use the previous:

last_started

bookmark to limit what is requested on the next run. After each successful table sync, the bookmark is moved to the timestamp captured at the beginning of the current connector run.

This creates a small amount of intentional overlap between runs, so recently retrieved records may occasionally be returned again.

Contacts

Contacts are retrieved using HubSpot's search endpoint and filtered by:

lastmodifieddate >= previous bookmark

They are sorted by hs_object_id and retrieved in pages of 100.

HubSpot search results are limited to 10,000 records per query. The connector works around this by restarting the search after every 10,000 records and continuing from the last hs_object_id retrieved.

Email events

Email events use a timestamp range:

startTimestamp={previous bookmark} endTimestamp={current time}

and retrieve up to 500 records per request.

Marketing emails

Marketing emails use:

createdAfter={previous bookmark}

with up to 300 records per request.

On the initial load, the connector changes the effective earliest v3 date from 1970 to:

2000-01-01

because the endpoint does not handle the earlier value correctly.

Email campaigns

Campaigns are retrieved through the campaign-list endpoint. The connector then performs an additional GET request for each campaign and stores the detailed response with that campaign record.

Lists

Lists are different from the other datasets. They use offset-based pagination but do not apply the stored bookmark as a filter.

As a result, lists is effectively fully reread on every sync.


Schema & Data Behavior

Contact columns are based on the properties currently defined in HubSpot. The connector retrieves HubSpot property metadata and uses those properties to determine available contact fields and their types.

For the other datasets, columns are initially inferred from a sample API record. If additional fields appear later during the sync, the connector can expand the schema to include those new fields.

Complex arrays or objects are stored as JSON values rather than being fully flattened.


Deletes

The connector does not explicitly synchronize deletes.

It does not:

  • Query an archived/deleted-record feed

  • Generate downstream delete events

  • Compare the current dataset with the previous source snapshot

  • Issue DELETE requests

A HubSpot record that is deleted or archived upstream should therefore not automatically be expected to disappear downstream.


Retries & Rate Limits

Normal request failures are retried with a 15-second delay, with up to approximately five attempts.

For HTTP 429 rate-limit responses, the connector reads HubSpot's:

X-HubSpot-RateLimit-Interval-Milliseconds

header and waits for that interval before retrying. If the header is unavailable, it defaults to:

120 seconds

Known Limitations & Notable Behavior

  • Not every table uses the bookmark the same way. Contacts use lastmodifieddate, email events use timestamp boundaries, marketing emails use createdAfter, and lists are fully reread.

  • Marketing email updates can potentially be missed. The endpoint is filtered by createdAfter, not a last-modified field. An older marketing email that is changed after its original creation date may therefore fall outside a later incremental window.

  • Lists are full rereads. Although a bookmark is stored for the table, that bookmark is not actually included in list-search requests.

  • Email campaigns require an extra request per campaign. Large campaign histories can therefore generate significantly more API traffic than the number of campaign rows suggests.

  • Deletes and archived objects are not explicitly propagated.

  • Schema can change over time. HubSpot properties and newly returned API fields can introduce new columns, so downstream models should tolerate schema evolution.

  • Empty values are normalized to NULL. The current cleaning logic treats values considered empty—including boolean false, numeric 0, and string "0"—as null. Consumers should be cautious when distinguishing a genuine zero/false value from missing data.

  • Large contact updates are more expensive. Although the connector works around HubSpot's 10,000-result search limitation, syncing more than 10,000 modified contacts requires multiple search windows.

  • Repeated 429 responses may cause a long-running sync. The rate-limit branch waits and retries based on HubSpot's rate-limit interval rather than immediately failing.

Did this answer your question?