How to create a HubSpot private app, and how to scope it properly
A HubSpot private app is created in Settings under Integrations, and it issues an access token scoped to specific objects and permissions. Grant read only scopes wherever the integration does not write, use one app per integration so a leaked token has limited blast radius, and store the token in a secret manager rather than in code.
By HubReven
HubSpot retired API keys. Private apps replaced them, and the replacement is better in one important way: the token carries scopes, so it can only do what you allowed.
Most teams create one and grant everything. Here is how to do it properly.
Creating it
- Settings (the gear icon), then Integrations, then Private apps.
- Create a private app.
- Name it after the integration, not after the person who made it. "ERP order sync" tells the next admin what breaks if they delete it. "Dev test" does not.
- Open the Scopes tab and select only what the integration needs.
- Create, then copy the access token once. It is shown in full only at that moment.
You need super admin permissions to create one. If the option is missing, that is why.
Scoping, which is the part that matters
The scope list is long and it is tempting to tick everything. Do not.
The principle: read where you read, write only where you write. An integration that pulls deals
into a warehouse needs crm.objects.deals.read and nothing else. Granting write access to an
integration that never writes means a bug or a leaked token can destroy data it was never supposed
to touch.
Scopes are organised per object, with read and write separate. Work through the field mapping and grant object by object.
One app per integration. Not one shared app for everything. Separate tokens mean you can rotate one without breaking the others, revoke one without an outage everywhere, and read the logs to see which integration did what.
Review the scopes when the integration changes. Adding a new object to the sync means adding a scope. Removing one should mean removing a scope, and nobody ever does.
Using the token
It goes in the Authorization header as a bearer token:
Authorization: Bearer pat-na1-xxxxxxxx-xxxx-xxxx
Three rules about where it lives.
Not in code. Not in the repository, not in a config file that gets committed, not in a comment. Use environment variables backed by a secret manager.
Not in a browser. A private app token is a server side credential. Anything shipped to a client is public, regardless of how it is obfuscated.
Not in a shared document. The place teams most often leak these is a wiki page titled "integration credentials".
Rate limits, briefly
Private apps are subject to per portal limits that depend on your subscription tier. The practical consequences are the same regardless of the exact numbers:
- Batch your writes. The batch endpoints handle up to 100 records per call. Using the single record endpoint in a loop is how you burn a limit for no reason.
- Handle 429 responses. Respect the retry header, back off exponentially, and add jitter so parallel workers do not retry in unison.
- Use search rather than pagination through everything. Fetching all records to find the changed ones wastes most of your budget.
An integration without 429 handling works in testing and fails on the first real backfill.
Rotating the token without an outage
Tokens should be rotated on a schedule and immediately if one is exposed.
The sequence that avoids downtime:
- Generate a new token in the private app.
- Deploy the new token to your secret manager and restart the service.
- Verify a real sync runs successfully on the new token.
- Only then rotate away the old one.
Doing step 4 first is a self inflicted outage, and it is the most common way this goes wrong.
What to log, and what not to
Log the request, the object type, the record identifier and the response status. That is what makes a failure diagnosable.
Never log the token, and never log full record payloads containing personal data. A searchable log full of contact records is a data protection problem wearing an operations costume.
When a private app is not the answer
If you are building something for other companies to install, that is a public app with OAuth, not a private app. Private apps are for your own portal only.
And if a marketplace app already covers your systems and mapping, use it. The comparison between the three options is here, and what a serious custom build actually contains is the six stage sequence.
Frequently asked questions
Are HubSpot API keys still supported?
No. API keys were retired and private apps replaced them. The practical improvement is that a private app token is scoped, so it can only access what you explicitly granted.
What scopes should I grant a private app?
Only the objects the integration touches, with write access only where it actually writes. Work through the field mapping and grant object by object rather than ticking the whole list.
Can I have more than one private app?
Yes, and you should. One per integration means you can rotate or revoke a token without taking every other integration down with it.
What happens when a private app hits a rate limit?
The API returns a 429 with a retry header. Your service should respect it, back off exponentially and add jitter. Batch endpoints handling 100 records per call are the main way to stay well under the limit.
Get the next one