Every HubSpot portal accumulates properties faster than it sheds them. A campaign manager adds a custom field for a one-quarter push and forgets to delete it. Marketing imports a vendor list and the import wizard creates 30 new fields on the way in. Operations duplicates a field with slightly different semantics because nobody could find the canonical one. A migration leaves behind columns nobody reads from. Five years of this and a contact object can carry 600+ properties, of which maybe 80 are still actively maintained. If you have not benchmarked your portal against tier-typical counts before, the Bloat Score is a free read across properties, workflows, lists, and asset density per contact.
The cost is invisible until something breaks. Reports pull from frozen fields and quietly misreport pipeline. Workflow conditions reference properties whose dropdown options drifted from the current taxonomy. New hires open a record, see four fields that all look like they should hold the same value, and have no way to tell which one is current. Integrations write to the wrong field because the field name in the third-party config matched the internal name of a property that got deprecated two years ago.
We see this on every portal we audit. The fix is not heroic. It is just patient work that nobody wants to do, and a repeatable shape so the work does not get heroic the next time.
What "stale" actually means
Three definitions. Each is useful on its own. A property is fully stale when all three apply.
Fill-rate stale. Less than 5% of records in the relevant scope have the property set. A custom contact property at 2% fill rate is either dead or scoped to such a narrow segment that it should probably live on a different object. The 5% threshold is not magic; pick whatever your portal can defend.
Activity stale. Nobody has written to the property in 90+ days, regardless of fill rate. This is the harder signal to get out of native HubSpot reporting, but it is the most diagnostic. A 60% fill rate property nobody has updated since 2024 is a frozen historical artifact, not a working data point. Treat it accordingly.
Reference stale. The property is not used by any workflow, list, report, form, sequence, integration, or custom code module. HubSpot exposes this on the property's edit screen under the "Used in" tab. If "Used in" is empty across every category, nothing in your portal depends on this property continuing to exist.
The interesting cases are the partial ones. High fill rate with no recent writes is a frozen dataset that may still matter for historical reporting, but the property should be renamed (something like archived_ or legacy_) and locked from future writes. Low fill rate with an active workflow reference is a property somebody is still trying to use, often badly. Active workflow reference with zero recent writes is a workflow that fires on a condition that no longer happens, which means the workflow itself is the stale thing, not the property.
A seven-step audit
Here is what we run, in order. The whole pass takes two to four hours for a mid-sized portal once you have the inputs assembled.
Step 1: Export the property list. Settings, Properties, pick the object type, click Export. HubSpot mails you a CSV with property name, internal name, group, type, field type, and a flag for whether the property is hubspot-defined or custom. Repeat for contacts, companies, deals, and tickets. Custom objects get the same export from their respective settings panels.
Step 2: Filter to custom only. HubSpot-defined properties are not your problem. They are versioned by HubSpot and they exist whether you want them or not. The audit is about properties your team or your vendors created.
Step 3: Pull "Used in" data for each property. This is the most laborious step in native HubSpot because there is no bulk export of the "Used in" tab. You have to click into each property and read the count. For a 600-property contact object that is 600 clicks. We have written internal scripts that pull this through the CRM Properties API plus the Workflows, Lists, and Reports APIs. If you do not write code, batch this work over a week and budget two minutes per property.
Step 4: Pull fill rate. Custom Report Builder, single-property report, set the metric to "% of records with property set." Save the report. Repeat for every custom property on the object, or build one report per group. For a faster pass, export 10,000 records via CRM list export and count nulls in a spreadsheet.
Step 5: Sample last-write timestamps. HubSpot does not expose per-property last-write timestamps at the schema level. You have to derive them from property history on individual records via the Property History endpoint. Sample 50 records that have the property set, pull the most recent timestamp from each property's history, take the max across the sample. Anything older than 90 days is activity-stale by our definition.
Step 6: Bucket each property. Five buckets: keep as-is, archive, hide-from-forms, merge, delete. We define each in the next section. Every custom property goes into exactly one bucket. Document the decision and the reason in a shared sheet so the audit is auditable.
Step 7: Execute in batches. Archive is the safest reversible action. Run all archives first, watch for two weeks, look for any reports or workflows that suddenly broke. If nothing broke, the archive was correct. Then run the deletes. Then run the merges, which are more involved because they require a workflow to copy values from the source field to the canonical destination before the source field is archived.
Tools and queries
What we actually use:
- HubSpot Settings, Properties. The native UI. Free. The "Used in" tab on each property is the highest-signal datapoint in the whole audit. Read it, every time.
- Custom Report Builder. For fill rate. Single-property reports. Build them once, save them, re-run them on every audit cycle so the second pass is cheap.
- CRM Properties API.
GET /crm/v3/properties/{objectType}returns the full schema in one call. Useful for programmatic passes, especially when the property count gets above 200 and clicking through the UI stops being reasonable. - CRM Search API plus Property History endpoint. For deriving last-write timestamps. Sample records, pull history per property, take the max. There is no shortcut endpoint for this at the schema level; it is a derived metric, always.
- CSV export plus spreadsheet. For fill rate when Custom Report Builder feels too slow. List export, count nulls, divide. Crude but fast.
What we do not use: any third-party "property auditor" app that promises a one-click report. (Our own Property Audit Checklist is a self-assessment, not a scanner: it scores your hygiene 0 to 100 across the same questions this guide covers, but you have to know your portal to answer.) We have evaluated several. They surface fill rate well, "Used in" coverage poorly, and last-write timestamps not at all. The signal you actually want for the keep-or-kill decision is the one those tools cannot deliver, because HubSpot does not expose it at the schema level and the apps cannot derive it cheaply at scale.
What to do with the stale ones
Five actions. Pick one per property.
Keep. The property is current, in use, has a clear owner, and nothing about the audit changed your view of it. Document why you kept it so the next auditor does not reopen the question and run through the same chain of reasoning.
Archive. Reversible soft-delete. The property hides from the UI and from new records, but historical values stay on records that already had them, and any report or workflow that explicitly references the property by internal name keeps working. This is the right action for most stale properties. If you are unsure between keep and delete, archive is the safe middle. Unarchiving is one click if you change your mind.
Hide from forms. A property-level setting under Settings, Properties, Edit, Form options. Useful for properties you want to keep in the schema but no longer surface to contacts via marketing forms. Common case: an internal scoring field that got accidentally added to a public form, where contacts can now see and edit it. Hide it from forms; do not delete it.
Merge. Two duplicate properties holding the same concept get collapsed into one canonical. Build a workflow that fires on record-update with the source property as a trigger, copies the source value into the canonical destination if the destination is empty, and clears the source. Run the workflow once, verify the migration on a sample of records, then archive the source. Merges are the most labor-intensive action because they require careful workflow design and a verification step. They are also the most valuable because every merged duplicate removes a future point of confusion for new hires and integrations.
Delete. Hard delete. Gone forever. Breaks anything pointing at it. Reserve this for properties with zero historical data, no references, and zero risk of someone asking "where did that field go" six months from now. For everything else, archive is the better choice.
The rough split we see across audited portals: archive 70%, merge 15%, delete 10%, hide-from-forms 5%. Numbers vary by portal, but the shape of the distribution does not. Most stale properties are not actively dangerous; they are just clutter that should be hidden, not destroyed.
Prevention
A clean audit lasts about 18 months before drift returns. The audit cycle is necessary either way, but the drift rate is something you can slow down so the audits stay shallow instead of becoming structural rebuilds.
What works:
- Naming convention. Custom properties get a short namespace prefix indicating the team or domain that owns them:
ops_,mkt_,sales_,cs_. The prefix shows up in the property list view and instantly tells anyone reading the schema who to ask before changing the field. Properties without a namespace become orphans within a quarter. - Description field is mandatory. The description on every custom property answers "what reads this?" If the description is empty, the property has no documented consumer and is one rotation away from being stale. Make the answer to that question a precondition for property creation, not an afterthought.
- Quarterly mini-audit. Run steps 3, 4, and 5 of the audit (Used-in, fill rate, last-write) every 90 days as a partial pass. Full audits with bucketing and execution happen once a year. The quarterly pass catches new drift before it becomes structural.
- Sunset clause on campaign properties. When a property is created for a one-off campaign or vendor integration, the creator sets a calendar reminder for 90 days out to review and archive it. Most one-off properties never get that review on their own.
- Document the canonical property. For every common concept (lead source, last touch channel, NPS score, account tier), write down which property is the source of truth. Pin the doc somewhere people will find it. The most common cause of duplicate properties is somebody creating a new one because they could not find the old one.
None of these are hard rules. They are friction surfaces that keep the rate of new stale properties below the rate of old stale properties getting cleaned up. As long as the second number is bigger than the first, the portal trends toward clean.
How Property Pulse fits
Disclosure: we make Property Pulse. It is a HubSpot marketplace app that records every property change on every CRM record and renders a filterable change log card on the record itself.
For the audit, two things in Property Pulse are useful. First, the change log gives you a fast read on which properties are actively being written to and which have not been touched in months, without sampling individual records by hand for last-write timestamps. Second, the per-property history lets you see at a glance whether changes come from humans, integrations, or workflows, which is often the difference between "delete safely" and "this property is load-bearing for a vendor sync I forgot about."
Property Pulse does not do the bucketing for you, and it is not meant to. The keep-or-kill decision still belongs to a person who knows the business. What it does is shorten the time you spend gathering the inputs to that decision, which is most of the work in any audit.
It is in marketplace beta and free during the beta. If you are about to run an audit and you do not already have it installed, it is the cheap version of answering "is anyone still writing to this field, and who is it" without writing a script that hits the Property History API for every record on the object.
The take
Stale property cleanup is unglamorous work that every portal needs and almost none of them get. The cost of putting it off is invisible until somebody runs a report that has been silently broken for 18 months, or a new hire spends a week trying to figure out which of four similarly-named fields holds the actual value, or a vendor integration writes to a field that nothing reads.
Run the audit, or schedule the audit. Either is better than the third option, which is what most portals are doing right now.