Overview
The HubSpot connector provides read-only access to a focused set of HubSpot CRM and marketing datasets:
contactslistsemail_eventsmarketing_emailsemail_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 |
|
|
|
|
|
|
|
|
|
|
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 |
|
Lists | POST |
|
Email events | GET |
|
Marketing emails | GET |
|
Email campaigns | GET |
|
Email campaign detail | GET |
|
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 usecreatedAfter, 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 booleanfalse, numeric0, 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
429responses may cause a long-running sync. The rate-limit branch waits and retries based on HubSpot's rate-limit interval rather than immediately failing.
