When you build a Property Search request, each text field accepts a small grammar of operators that tell the API how to match your value. Choosing the right operator is the single biggest factor in how fast your search returns and how relevant your results are. This guide walks through every operator, when to use it, and the patterns that produce predictable performance.
The Most Important Rule: Prefer equals When You Can
When you know the exact value you are looking for, use equals. It is the fastest operator and the most precise.
Historically, many slow searches came from customers reaching for contains when equals would have returned the same result; today most text fields reject contains outright. contains is a wildcard search: it asks the API to scan every value in the index for a match anywhere inside the string. equals asks the index a single, direct question.
Rule of thumb:
If you have a full, known value (a complete city name, a complete subdivision name, an exact owner name) → use
equals.If you only have a fragment of the value → first recover the complete value, then search with
equalsorinListusing complete values (a fragment insideinListmatches nothing). For a subdivision name, use the Property Lookup approach in Subdivision Searches; for owner names, usecontainsWords, the one operator that matches on partial names. The wildcard operators (contains,startsWith,endsWith) are restricted on most text fields (see Field-Specific Restrictions) and are available only on low-cardinality enumerated fields.
Subdivision Searches: When and How to Use legal.subdivisionName
legal.subdivisionName supports two related use cases, and both are exact-match by design, which is why the field accepts only equals, inList, notInList, and matches, all of which require the complete stored name, not the wildcard operators (contains, startsWith, endsWith).
Use case 1: Property search restricted to a specific subdivision
When you have one or more known subdivision names and want every property in them, use legal.subdivisionName as a filter on a standard Property Search call. This is the right pattern for marketing list build-outs, portfolio audits, and any workflow where the subdivision is part of the search criteria — independent of any subject property.
A Property Search payload filtered to a single subdivision:
{
"searchCriteria": {
"address": {
"city": { "equals": "Phoenix" },
"state": { "equals": "AZ" }
},
"legal": {
"subdivisionName": { "equals": "Briarwood Estates" }
}
},
"options": {
"skip": 0,
"take": 25
}
}
Use inList to span a set of subdivisions, or notInList to exclude specific ones:
{
"searchCriteria": {
"legal": {
"subdivisionName": {
"inList": [
"Briarwood Estates",
"Briarwood North",
"Briarwood Heights"
]
}
}
}
}
If you only have a fragment of a subdivision name and need to discover the full value, identify any property you know to be in the subdivision — an address you have on file, an existing customer property, or any other known parcel — and call Property Lookup on it. The response's legal.subdivisionName is the exact string to use in your equals or inList filter.
Use case 2: Comparable property search constrained to the subject's subdivision
A property comparable search returns properties similar to a subject property — matched on distance from the subject's address (compAddress), property characteristics (bedrooms, bathrooms, square footage, year built), and recent sale activity. For valuation and investment workflows, "similar" almost always has to mean "in the same subdivision." Subdivisions are stable, market-meaningful boundaries: two homes a few hundred feet apart but in different subdivisions can sit in different school zones, be served by different HOAs, and trade at materially different prices.
A comparable search has two parts that go in different places in the request body:
searchCriteriaholds the subject property (compAddress) plus any explicit filters you want to apply to the result set, such asgeneral.propertyTypeCategory.optionsholds the comparable-algorithm tuning knobs:useDistance+distanceMiles,useBedrooms+minBedrooms/maxBedrooms,useBathrooms,useArea,useYearBuilt,useLotSize,useSubdivision. Themin*/max*values are deltas from the subject (for example, the defaultminBedrooms: -1andmaxBedrooms: 1returns comparables with one fewer to one more bedroom than the subject).
The simplest way to constrain comparables to the subject's own subdivision is options.useSubdivision: true. The comparable algorithm derives the subdivision from compAddress automatically — no separate Property Lookup step needed:
{
"searchCriteria": {
"compAddress": {
"street": "8421 N Briar Ridge Way",
"city": "Phoenix",
"state": "AZ",
"zip": "85020"
},
"general": {
"propertyTypeCategory": {
"inList": ["Residential"]
}
}
},
"options": {
"aggComparablesMetrics": true,
"dateFormat": "iso-date",
"skip": 0,
"take": 25,
"useDistance": true,
"distanceMiles": 1,
"useBedrooms": true,
"useBathrooms": true,
"useSubdivision": true
}
}
If you need comparables in a different subdivision than the subject's — or want inList to span multiple adjacent subdivisions — replace useSubdivision: true with an explicit legal.subdivisionName clause in searchCriteria. The two controls AND together: setting useSubdivision: true alongside a conflicting explicit equals returns zero results, because the algorithm requires the subject's subdivision and the filter requires the other one.
{
"searchCriteria": {
"compAddress": {
"street": "8421 N Briar Ridge Way",
"city": "Phoenix",
"state": "AZ",
"zip": "85020"
},
"legal": {
"subdivisionName": {
"inList": ["Briarwood North", "Briarwood Heights"]
}
},
"general": {
"propertyTypeCategory": {
"inList": ["Residential"]
}
}
},
"options": {
"useDistance": true,
"distanceMiles": 1,
"useBedrooms": true
}
}
Why wildcard operators are not supported on subdivisionName
contains would seem useful if you only remember part of a subdivision name. In practice, it is the wrong tool for this field:
Subdivision names are stable identifiers, not keywords. Two subdivisions might both contain the word "Briar" but represent entirely different developments with no shared market behavior. Substring matching conflates them.
Wildcard scans on this field force index-wide reads. A
containsquery is implemented as*value*, which the search index cannot satisfy with a direct lookup. The cost grows with the size of the dataset, not with the size of the result set.
If you only know part of a subdivision name, use the Property Lookup approach from Use Case 1 to retrieve the full name first, then use it in equals or inList.
The Full Text-Search Grammar
Property Search supports eight text operators. Seven are general-purpose and fall into three groups: single-value operators, list operators, and the complete-value operator. The eighth, containsWords, is specific to current-owner name search on owner.firstName and owner.lastName; it is described under Owner-Name Operator below, with the full workflow in Searching by owner name. It is not available on any other name field, including previous-owner search.
Single-Value Operators
equals (string)
Exact match against the field value. Fast and precise. Use this whenever you know the complete value.
{ "address": { "city": { "equals": "Phoenix" } } }
contains (string)
Wildcard match that returns results where the field contains the value anywhere inside it. This is a substring scan and is significantly slower than equals.
contains is not supported on the high and medium cardinality Property Search text fields (owner names and owner mailing fields, address and location fields, subdivision name, APN, tax id, property id, address hashes, zoning code, permit tags, and the demographics text field). See Field-Specific Restrictions for the exact list. Sending it on one of those fields returns an HTTP 400 error naming the field and operator, with guidance on the supported operators for that field. It remains available only on the low-cardinality enumerated fields (for example address.state, address.zip, general.propertyTypeCategory, general.propertyTypeDetail, and most other status and type enums).
{ "address": { "zip": { "contains": "021" } } }
startsWith (string)
Prefix match that returns results where the field begins with the value. Faster than contains because the API can use the index to jump directly to candidates.
Like contains, startsWith returns an HTTP 400 error on the restricted text fields listed above and is available only on the low-cardinality enumerated fields.
{ "address": { "zip": { "startsWith": "850" } } }
endsWith (string)
Suffix match that returns results where the field ends with the value. Like contains, this requires scanning every value in the index and is slower than equals or startsWith.
As with contains and startsWith, endsWith returns an HTTP 400 error on the restricted text fields and is available only on the low-cardinality enumerated fields.
{ "address": { "zip": { "endsWith": "20" } } }
List Operators
inList (array of strings)
Returns results that match any value in the list. This is the right tool when you have a set of known values — much more efficient than chaining many equals calls and clearer than a contains against a partial string.
{ "address": { "city": { "inList": ["Phoenix", "Mesa", "Tempe"] } } }
notInList (array of strings)
Returns results that match none of the values in the list. Useful for excluding known categories (for example, excluding specific property types or specific cities from a regional search).
{ "general": { "propertyTypeDetail": { "notInList": ["Vacant Land", "Mobile/Manufactured Home"] } } }
Complete-Value Operator
matches (array of strings)
Each value must be a complete stored value. matches is not a keyword, prefix, or partial search: a fragment of a longer stored value returns nothing.
Use matches with a single value, where it behaves like equals, and use inList to match any of several values.
What supplying more than one value to matches does varies by field, so do not rely on it. Matching is case-insensitive. matches is not supported for property loan type (the openLien loan-type fields).
{ "permit": { "allTags": { "matches": ["ROOFING"] } } }
Owner-Name Operator
containsWords (string, current-owner name fields only)
Tokenized, order-insensitive, case-insensitive word match, available on owner.firstName and owner.lastName only. It is not accepted on previousOwner.fullName; see Searching by previous owner. Unlike contains (a substring scan), containsWords matches whole name tokens in any order, so you can find a property by its owner's name without knowing the stored name format or the order its parts appear in. When you supply both owner.firstName and owner.lastName with containsWords, both names must appear on the same owner record. A supplied name part is matched against the whole of the owner's stored name rather than against one name component, so owner.firstName and owner.lastName are interchangeable when both are supplied. This is the supported way to search by owner name. If you use containsWords for one name and equals or inList for the other, the API checks each name separately, so the two names can match two different owners. See Searching by owner name for the full workflow.
{ "owner": { "lastName": { "containsWords": "Smith" } } }
One Operator Per Field
Supply exactly one operator per field. If a field carries more than one operator, the API applies one of them and silently discards the rest. The request still returns HTTP 200, and the response reports no error and no warning, so a query that ignored half of what you asked for looks like a query that worked. That silent discard applies when the field accepts every operator you supplied. If any supplied operator is not accepted on the field, the request fails with an error instead, so a wildcard paired with equals on a restricted field still returns its HTTP 400.
Of the eight text operators, matches ranks last. On a field that accepts matches, pairing it with any other operator means matches is the clause that gets dropped. The property loan type fields do not accept matches at all, so supplying it there makes the request fail instead. These are the openLien loan-type fields: allLoanTypes, juniorLoanTypes, and firstLoanType through fourthLoanType.
Two different operators on the same field cannot be combined in one request. To match any of several values, use inList. For two genuinely different match rules, send one request per rule and merge or filter the results in your own application.
Performance Quick Reference
Operator | Index-friendly | Notes |
| Yes | Fastest. Always prefer when the full value is known. |
| Yes | Restricted: returns HTTP 400 on the high and medium cardinality fields. Available only on low-cardinality enumerated fields, where it is a fast prefix scan. |
| Yes | Fast — equivalent to many |
| Yes | Fast — negation of a list. |
| Yes | Each value must be a complete stored value. Use with a single value. Not supported for property loan type. |
| Yes | Tokenized owner-name match. Available on |
| No | Restricted: returns HTTP 400 on the high and medium cardinality fields. Available only on low-cardinality enumerated fields. Slower, requires scanning all values. |
| No | Restricted: returns HTTP 400 on the high and medium cardinality fields. Available only on low-cardinality enumerated fields. Slowest, a wildcard scan over every value. |
The wildcard operators (contains, startsWith, endsWith) are supported only on the low-cardinality enumerated fields (for example address.state, address.zip, general.propertyTypeCategory, general.propertyTypeDetail, and most other status and type enums). On the high and medium cardinality text fields they return an HTTP 400 error. See Field-Specific Restrictions for the full list.
Field-Specific Restrictions
Not every operator is available on every field. The wildcard operators (contains, startsWith, endsWith) are not supported on the high and medium cardinality Property Search text fields below, grouped by object. Sending a wildcard operator on one of these fields returns an HTTP 400 error naming the field and operator, with guidance on the operators that are supported there.
owner:firstName,lastName,middleName,fullName,mailingStreet,mailingCity,mailingAddressHashpreviousOwner:fullNameaddress:street,streetNoUnit,formattedStreet,city,locality,county,cbsaTitle,csaTitleids:apn,taxId,addressHash,mailingAddressHash,propertyIdlot:zoningCodedemographics:demographicspermit:allTagslegal:subdivisionName(supportsequals,inList,notInList, andmatches; see subdivision searches above for the full rationale and the comparable-search workflow this restriction is designed around)sale:lastSaleTransactionTypeCode,priorSaleTransactionTypeCode(low-cardinality code fields that are restricted as well)
On these fields, use the supported operators instead: equals for an exact value, inList and notInList for a known set of values, and matches with a single complete stored value, as on the tag field permit.allTags. owner.firstName and owner.lastName additionally support containsWords for owner-name search (see Searching by owner name).
The wildcard operators remain available on the low-cardinality enumerated fields, for example address.state, address.zip, general.propertyTypeCategory, general.propertyTypeDetail, the numeric geography codes (address.countyFipsCode, address.cbsaCode, address.csaCode), and most other status and type enums. Note that the restriction applies to the text title fields (address.cbsaTitle, address.csaTitle), not to their corresponding numeric code fields.
A second restriction worth knowing about is previousOwner.fullName, which is exact-match-only: it supports equals, inList, notInList, and matches, and it does not accept containsWords, contains, startsWith, or endsWith. matches is no exception: it also requires the complete stored name. See Searching by previous owner.
The Property Search API Reference carries worked request-body recipes for building filters, property types, quicklists, and request options. If you send a wildcard operator on a restricted field, the API returns an HTTP 400 naming the field and operator. The loan-type fields are the exception: supplying matches there fails without that guidance.
Searching by owner name
To find a property by its owner's name, use the containsWords operator on owner.firstName and owner.lastName. containsWords performs a tokenized, order-insensitive, case-insensitive word match, so a first or last name matches regardless of how the owner's name is stored or the order its parts appear in. When you supply both owner.firstName and owner.lastName, a property matches only if both names appear on the same owner record. When both names use containsWords, satisfying one name through one owner and the other through a second owner is not a match. Supplying a single name part matches it against either owner. A supplied name part is matched against the whole of the owner's stored name rather than against one name component, so owner.firstName and owner.lastName are interchangeable when both are supplied. This is the supported way to search by owner name, and it replaces wildcard matching on owner-name fields. The same-owner rule applies when both names use containsWords or both names use equals or inList. If you use containsWords for one name and equals or inList for the other, the API checks each name separately. The two names can then match two different owners on the same property.
equals, inList, and matches are also accepted on owner.firstName and owner.lastName. They compare a value against the stored name component itself: the stored first name on owner.firstName, or the stored last name on owner.lastName. A value that is not the complete stored component, such as a full name supplied to owner.lastName, returns no results. With a single value, matches behaves like equals. When you do not know the stored format, use containsWords.
A search that uses containsWords on either name requires a location, such as a city and state, a ZIP code, or a full address. Without a location, the request returns HTTP 400. An owner-name search that uses only equals or inList does not require a location. For a containsWords search, the options.ownerNameAddressMatchMode request option selects how the location is handled:
standard(default): normal Property Search address handling, returning the closest matching properties at the location with owner-name matching applied. Accepts a full address or a location query such as a city, county, or ZIP.exact: resolves the structured address you supply to the single parcel at that address, with no widening. An address that does not resolve returns no match. This mode requires a structured address and an owner-namecontainsWordscriterion.
Use exact mode when you need to confirm whether a specific person is on the owner record at a known address (for example, probate and estate research). In exact mode the response adds an ownerNameMatch object under meta.results carrying matchedParcelCount (the total, and the value to use for the zero / one / more-than-one decision), ownerNameMatchCount and trustMatchCount (attributions explaining how those parcels were reached, which overlap rather than dividing the total between them), and matchSource, which indicates which address the match came from. ownerNameMatchCount counts parcels carrying every supplied name part on the current owner record, and trustMatchCount counts parcels reached through the trust deed look-back. For a trust-owned property that look-back also matches recent deed parties, and both names must describe the same deed party. standard mode omits the ownerNameMatch object.
Confirm whether John Smith is on the owner record at a specific address:
{
"searchCriteria": {
"owner": {
"firstName": { "containsWords": "John" },
"lastName": { "containsWords": "Smith" }
},
"address": {
"street": { "equals": "123 Main St" },
"city": { "equals": "Phoenix" },
"state": { "equals": "AZ" },
"zip": { "equals": "85020" }
}
},
"options": {
"ownerNameAddressMatchMode": "exact",
"take": 1
}
}
This capability is announced in the Owner-Name Property Search release note in our Release Notes collection.
Searching by previous owner
Owner-name search finds properties a party owns today. To find properties a party previously sold, use previousOwner.fullName. This is the criterion to reach for when you are researching a fix-and-flip investor, a builder, or any party whose activity you track by what they have offloaded rather than what they currently hold.
previousOwner.fullName is exact-match-only:
Supported:
equals,inList,notInList,matches. Matching is case-insensitive, and all four require the complete stored name: a single word from a longer name returns nothing. Supplying more than one name tomatchesreturns nothing, so useinListto match any of several names.Not supported:
containsWords,contains,startsWith, andendsWith. Sending one of these returns an HTTP 400 error naming the field and operator.
Because there is no tokenized or partial matching here, supply the party's name as it appears on the transaction. If you are unsure of the exact spelling or the entity suffix, pass several variants with inList rather than guessing a single form.
One important limitation: this criterion matches the seller of record on a property's two most recent sales. It does not search a property's full ownership or transaction history, so a flip that happened three or more transactions ago will not match. If you need everything a party has ever transacted, use Investor Profile instead.
{
"searchCriteria": {
"query": "Phoenix, AZ",
"previousOwner": {
"fullName": { "equals": "Sunrise Property Group LLC" }
}
},
"options": {
"take": 10
}
}
This capability is announced in the Previous-Owner Property Search release note in our Release Notes collection.
Practical Patterns
Looking up properties in a known set of cities:
"address": { "city": { "inList": ["Phoenix", "Tempe"] } }
Looking up properties in a specific subdivision:
"legal": { "subdivisionName": { "equals": "Briarwood Estates" } }
Excluding land and mobile-home property types from a regional search:
"general": { "propertyTypeDetail": { "notInList": ["Vacant Land", "Mobile/Manufactured Home"] } }
Searching for properties whose ZIP code begins with a prefix:
"address": { "zip": { "startsWith": "850" } }
Summary
Reach for
equalsfirst. It is faster and more precise.Use
inListandnotInListfor known sets of values instead of stringing multiple operators together.The wildcard operators (
contains,startsWith,endsWith) are restricted: on most text fields they return an HTTP 400 error, and they remain available only on low-cardinality enumerated fields. Where they are allowed, treat them as last-resort operators, since they scan the full index and are noticeably slower at scale.Check the Property Search API reference for the operators allowed on each field. Not every operator is supported everywhere.
Following these patterns will keep your searches fast, your results predictable, and your integrations resilient as your data volume grows.
Related Resources
Property Search API Reference: worked request-body recipes for building filters, property types, quicklists, and request options.
