Case-insensitive company matching while ignoring extra whitespace using the CRM Search API

Hello HubSpot Support,

We are implementing company synchronization between our platform and HubSpot using:

POST /crm/v3/objects/companies/search

Our matching sequence is:

  1. Search for an existing company using a unique company hash.

  2. If no hash match exists, search using the combined normalized values of:

    • Company Name
    • Address
    • Postal Code

Normalization means:

  • Removing leading and trailing whitespace.
  • Replacing multiple internal spaces with a single space.
  • Comparing values case-insensitively.

For example:

Platform:
Name: "  ACME    Bikes  "
Address: "Main   Street 12"
Postal Code: "1000"

HubSpot:
Name: "Acme Bikes"
Address: "Main Street 12"
Postal Code: "1000"

After normalization, these records should be considered a match.

Currently, we search by the HubSpot name property and then normalize and compare the returned company’s name, address, and zip values in our application.

However, HubSpot search does not appear to ignore multiple internal spaces. Searching for:

Acme Bikes

does not reliably return a company stored as:

Acme    Bikes

Because the existing company is not returned as a candidate, our application cannot perform the normalized comparison. This may result in duplicate companies being created.

Could you please clarify:

  1. Is there a CRM Search API operator that supports case-insensitive exact matching while ignoring leading, trailing, and repeated internal whitespace?
  2. Does the EQ operator perform any case or whitespace normalization?
  3. Would CONTAINS_TOKEN be suitable for retrieving candidates in this scenario?
  4. Can we search using name, address, and zip filters in the same filterGroup and apply normalized matching?
  5. When multiple filters are placed in the same filterGroup, are they combined using AND, and is each value normalized before comparison?
  6. What is the recommended approach for avoiding missed matches when whitespace differs?
  7. Is maintaining a separate normalized custom property, such as normalized_company_match_key, the recommended solution?

The property would contain a value such as:

acme bikes|main street 12|1000

We would appreciate your recommended approach for implementing this matching reliably through the HubSpot CRM Search API.

Hello,

I want to be up front and say I’m not affiliated with HubSpot. I’m just another community member who’s spent a fair bit of hands-on time with the CRM Search API on my own projects. So take this as practical experience rather than an official answer.

That said, my read is you’ve actually diagnosed the Search API correctly. You’re not missing an operator, and your instinct in question 7 is the right one. Going through your questions:

1 & 2 — EQ normalization: HubSpot indexes string properties case-insensitively, so EQ ignores case. But it does not normalize whitespace. EQ compares against the stored value, so "Acme Bikes" will not match a record stored as "Acme Bikes". Leading, trailing, and repeated internal spaces all cause misses. There is no operator that trims or collapses whitespace for you.

3 — CONTAINS_TOKEN: This is your best option for retrieving candidates. Search tokenizes on whitespace, so a search for acme bikes (or acme* bikes*) matches a record stored with multiple internal spaces, because it matches word tokens rather than the exact string. The trade-off is that it over-returns - any company containing those tokens comes back - so treat it as a candidate net and do your normalized comparison in your application on the returned set. That’s essentially what you’re doing today, just with a more forgiving retrieval operator than name EQ.

4 & 5 — combining name, address, zip: You can place all three in one filterGroup, and filters within a group are combined with AND (separate groups are OR’d; the limit is up to 6 filters per group, up to 5 filterGroups, with a maximum of 18 filters in total), see CRM search - HubSpot docs . But HubSpot does not normalize any of these values before comparison beyond case-folding. Combining three exact filters actually makes misses more likely, since every field must match exactly.

6 & 7 — recommended approach: Yes, a dedicated normalized match-key property (your normalized_company_match_key) is the standard and most reliable dedup pattern. Store the fully normalized composite exactly as you described:

acme bikes|main street 12|1000

Then a single filter does an exact, deterministic lookup:

{
  "filterGroups": [
    { "filters": [
        { "propertyName": "normalized_company_match_key",
          "operator": "EQ",
          "value": "acme bikes|main street 12|1000" }
    ]}
  ],
  "properties": ["name", "address", "zip", "normalized_company_match_key"],
  "limit": 1
}

A few things worth planning for with the match-key approach:

  • Indexing lag. A newly written property value isn’t instantly searchable. If you create and then immediately search in a tight loop you can still produce duplicates. I’d recommend a short retry / read-your-write buffer to solve for this.
  • Concurrency. Two simultaneous syncs of the same company can both miss and both create. If your volume is high, consider a lock or queue keyed on the match key.

So the short version: your hash-first approach plus a normalized match-key property is the way to go. Use CONTAINS_TOKEN as an interim candidate-retrieval method until the match-key property is built and backfilled.

If you want this confirmed officially, you can reach HubSpot support from inside your portal via the “?” icon in the top-right nav.
Note that the available channels depend on your subscription tier; here’s the breakdown: Get help with HubSpot . If you’re on a free plan, this community forum is actually the official channel, so you’re already in the right place.

Cheers!

Moderator Note: this post was reviewed for accuracy on July 15, 2026. Thank you for your contributions to the HubSpot Community!

For this kind of dedupe flow I would not depend on CRM Search to do the normalization for you.

The more reliable pattern is:

  1. Keep your unique company hash as the first lookup.
  2. Add a custom property such as normalized_company_match_key.
  3. Populate it from your integration for every create/update, using the exact same normalization code on both sides.
  4. Backfill it for existing HubSpot companies before enabling the dedupe rule.
  5. Search that property with EQ, then do a final in-app comparison before deciding to create.

I would treat CONTAINS_TOKEN as candidate discovery only, not as the final dedupe check. It can help widen a search, but it is not a stable substitute for a deterministic key built from name + address + postal code.

Also yes: filters in the same filterGroup are effectively AND conditions. Separate filterGroups are OR. That helps narrow results, but it still does not solve whitespace normalization unless the values you search on are already normalized.

Thanks everyone.

It would be good if i also get a reply from the official hubspot support team. Thanks

Hi @VGupta39, I hope that you are well!

Great questions, thanks for asking the HubSpot Community!

To reply to your questions:

Is there an operator for case-insensitive matching that also ignores internal whitespace?
Currently, the Search API does not offer an operator that normalizes internal whitespace, so “fuzzy whitespace” matching isn’t available yet.

Does EQ perform case or whitespace normalization?
The EQ operator is case-insensitive for string properties, except for enumerations and the IN/NOT_IN operators. However, EQ doesn’t normalize internal spaces, they’re matched exactly as entered.

Is CONTAINS_TOKEN suitable here?
CONTAINS_TOKEN helps match individual tokens (words) and supports wildcards (*), which can be handy for broader searches (like looking for “acme” and “bikes” separately). However, it’s not intended for exact-match deduplication and may bring in extra results.

Can you combine name, address, and zip filters in one filterGroup?
Yes! Filters within the same filterGroup use AND logic, allowing you to refine your search across multiple fields.

Are filter values normalized before comparison?
Property values are matched in a case-insensitive way for strings, but there’s no mention of whitespace normalization in the documentation.

6 & 7. Recommended approach for reliable matching?
Given the current API limitations, your suggested approach is a strong one: keep a custom normalized property (like normalized_company_match_key, storing values such as “acme bikes|main street 12|1000”). Regularly normalize and update this property on every create or update, and then use an EQ search on this field with your pre-normalized value for consistent results.

If you have other questions, feel free to ask!

Have a lovely day!
Bérangère

This post was created with the assistance of AI tools

Hi, the normalized key approach makes sense. I’d just make sure you store which version of the matching rule was used. Otherwise, a small change to the logic can suddenly stop older records from matching and start creating duplicates. I’d also avoid creating a new company when the match is unclear. Send those cases for review instead. After any change, keep an eye on how many records are being created versus updated. A sudden increase in new companies usually means the matching logic is missing existing records.