The BatchData Property Search API enables developers to integrate the means for searching and retrieving detailed information on real-estate properties. The API implements a very robust search grammar designed to support a wide-range of use cases.
For example, search criteria can be defined to:
Return a list of properties located in a particular geographic location. This is accomplished using a simple syntax where the location is specified using a single text string. For example, to search for properties in a city, simply set the search criteria query string to "Phoenix, AZ".
Return a list of properties that are comparable to a subject property. The comparables search grammar supports the ability apply search criteria that are relative to the subject property. For example, returning comparable properties whose living area square footage is within a certain percentage of the subject property, or within a certain radius of the subject property.
Return a list of properties that match certain characteristics of the property owner. For example, returning a list of properties where the total number of properties owned by the property owner is greater than a certain value.
Setting up the search criteria
The Property Search API accepts two primary JSON objects as input parameters, searchCriteria and options.
The searchCriteria object is used to specify what properties should be returned in the search results. The options object is used to further refine the API response and to control how many records to retrieve in each request.
searchCriteria has multiple properties to define the filtering criteria, out of which query is the most commonly used property. This property expects a US location such as a city, county, zip code, or State.
To help you understand better, let's look at some of the examples.
Retrieving a list of the properties in Phoenix, Arizona can be done by setting the query parameter to specify the location:
{
"searchCriteria": {
"query": "Phoenix, AZ"
}
}
Searching for properties in Phoenix whose last sale price was between $50,000 and $100,000 can be achieved by adding additional search conditions:
{
"searchCriteria": {
"query": "Phoenix, AZ",
"sale": {
"lastSalePrice": {
"min": 50000,
"max": 100000
}
}
}
}
The search results can be further refined to return a list of properties whose last sale prices fall within the $50,000 - $100,000 range and with lot sizes ranging between 0.1 - 0.5 acres:
{
"searchCriteria": {
"query": "Phoenix, AZ",
"sale": {
"lastSalePrice": {
"min": 50000,
"max": 100000
}
},
"lot": {
"lotSizeAcres": {
"min": 0.1,
"max": 0.5
}
}
}
}
Note: All search conditions are AND'd together and have the net effect of further reducing the number of properties that are returned in the search results.
Search criteria with numeric values
The two examples just shown used additional search criteria that accept numeric values. The search grammar for numeric search attributes allows for a minimum and a maximum value to be specified. The min and max operators can be combined to specify a range of values, or they can be used on their own. For example, to match properties that have a lot size of at least 2 acres, just specify the min value:
{
"searchCriteria": {
"query": "Phoenix, AZ",
"sale": {
"lastSalePrice": {
"min": 50000,
"max": 100000
}
},
"lot": {
"lotSizeAcres": {
"min": 2
}
}
}
}
The following search criteria matches properties whose last sale price did not exceed $100k. No minimum value is specified. This search is more concisely expressed as "Properties in Phoenix, AZ with a last sale price <= $100k and a minimum lot size of 2 acres":
{
"searchCriteria": {
"query": "Phoenix, AZ",
"sale": {
"lastSalePrice": {
"max": 100000
}
},
"lot": {
"lotSizeAcres": {
"min": 2
}
}
}
}
Search criteria with date values
Date based search criteria have a similar search grammar to numeric values. The minDate and maxDate operators can be used in combination or by themselves.
Here is an example of a search for properties with auction dates that are scheduled to occur in the next 7 days. The example assumes that the current date is December 8, 2024:
{
"searchCriteria": {
"query": "Phoenix, AZ",
"foreclosure": {
"auctionDate": {
"minDate": "2024-12-09",
"maxDate": "2024-12-15"
}
}
}
}
This example returns properties that were sold in the past 12 months assuming the current date is December 8, 2024. Note that we are using the intel.lastSold attribute. intel.lastSold is a composite attribute that considers both the sale.lastSaleDate and listing.soldDate when applying the search criteria:
{
"searchCriteria": {
"query": "Phoenix, AZ",
"intel": {
"lastSoldDate": {
"minDate": "2023-12-08"
}
}
}
}
Search criteria with text values
Now let's introduce some search conditions that make use of text values. The BatchData search grammar supports a robust set of operators for applying text based search conditions.
Consider the following search for residential properties:
{
"searchCriteria": {
"query": "Phoenix, AZ",
"general": {
"propertyTypeCategory": {
"equals": "Residential"
}
}
}
}
The equals operator is used to match properties where general.propertyTypeCategory = "Residential".
We can further refine our search results to only return certain types of residential properties by making use of the inList operator to specify a list of values that the propertyTypeDetail attribute must match:
{
"searchCriteria": {
"query": "Phoenix, AZ",
"general": {
"propertyTypeCategory": {
"equals": "Residential"
},
"propertyTypeDetail": {
"inList": [
"Single Family",
"Multi-Family"
]
}
}
}
}
The inList operator is effectively OR'ing the values in the array. The previous search is more concisely expressed as properties where the propertyTypeCategory = "Residential" AND the propertyTypeDetail is "Single Family" OR "Multi-Family".
The complete list of values that can be applied to general.propertyTypeCategory and general.propertyTypeDetail criteria are documented in the "Property Search/Property Lookup API, List Builder codes and descriptions" data dictionary.
Advanced location searches
While the query property provides the easiest way to search by location, it is limited to searching a single location. We can take advantage of the robust text search grammar to create more advanced location searches.
Searching multiple locations
A common use case is to identify properties located in a specific list of cities, counties, or zip codes. This can be accomplished by using the address property in conjunction with the inList operator.
For example, let's say you want to match properties in 3 cities. We omit the query property and instead make use of the address.cityState property:
{
"searchCriteria": {
"address": {
"cityState": {
"inList": [
"Phoenix, AZ",
"Tempe, AZ",
"Chandler, AZ"
]
}
}
}
}
The same search results can be accomplished using address.city and address.state:
{
"searchCriteria": {
"address": {
"city": {
"inList": [
"Phoenix",
"Tempe",
"Chandler"
]
},
"state": {
"equals": "AZ"
}
}
}
}
Let's say the desired outcome is to search for properties in a couple of counties while excluding properties in certain zip codes. Here we can make use of the notInList operator:
{
"searchCriteria": {
"address": {
"county": {
"inList": [
"Maricopa",
"Pima"
]
},
"state": {
"equals": "AZ"
},
"zip": {
"notInList": [
"85337",
"85390"
]
}
}
}
}
The contains operator is a way to apply a partial text match. The contains operator can be combined with the address.locality property to find properties in any neighborhoods that include "village" in their name:
{
"searchCriteria": {
"address": {
"county": {
"inList": [
"Maricopa",
"Pima"
]
},
"state": {
"equals": "AZ"
},
"locality": {
"contains": "village"
}
}
}
}
The complete list of text operators is as follows:
equals- search for an exact matchcontains- search for results that contain the valuestartsWith- search for results that start with the valueendsWith- search for results that end with the valueinList- search for results that match any of the strings in the arraynotInList- search for results that match none of the strings in the arraymatches- search for results that match all of the strings in the array
Array Size Limit: Arrays passed to inList and notInList should not exceed 10,000 values. Requests with arrays larger than 10,000 values may silently return incorrect results — the API will not return an error, but matching behavior becomes unreliable. Always monitor your result counts when working with large exclusion lists to verify the filter is being applied correctly.
Quick Lists
As you can see, the search grammar implemented by the Property Search API provides a lot of flexibility and can be used to define very complex search queries. However BatchData goes one step further by making it easy to implement search conditions for the most common real-estate investor use cases by predefining a large set of search queries. These predefined search queries are called Quick Lists and are accessed using the quickList, quickLists, and orQuickLists search criteria.
For example, a common use case for real-estate investors is to identify properties that are owned by individuals that live outside of the state where the property is located. We can easily implement that search by adding just one additional condition using the out-of-state-absentee-owner quick list:
{
"searchCriteria": {
"query": "Phoenix, AZ",
"quickList": "out-of-state-absentee-owner"
}
}
Multiple quick lists can be combined using either the quickLists search criteria which AND's the lists, or the orQuickLists search criteria which OR's the lists.
For example, to return properties where the owner is both out of state and is in default of their loan:
{
"searchCriteria": {
"query": "Phoenix, AZ",
"quickLists": [
"out-of-state-absentee-owner",
"notice-of-default"
]
}
}
Use the orQuickLists search criteria to return properties where the owner is out of state or is in default of their loan:
{
"searchCriteria": {
"query": "Phoenix, AZ",
"orQuickLists": [
"out-of-state-absentee-owner",
"notice-of-default"
]
}
}
The not- operator can be applied to quick lists to negate them. For example, to search for properties where the owner is not an out of state owner but is in default of their loan we simply prefix the quick list with "not-":
{
"searchCriteria": {
"query": "Phoenix, AZ",
"quickLists": [
"not-out-of-state-absentee-owner",
"notice-of-default"
]
}
}
The complete list of quick lists and their descriptions is documented in Property Search/Property Lookup API, List Builder datasets.
Structuring the result
If certain sections of the API response, such as building, listing, sale, or valuation data, are missing, it may indicate that your API token lacks the necessary datasets. Ensure your token is provisioned correctly to include all required datasets.
The options object enables the customization of search results. It is optional and contains various properties with useful customization functions.
Here are some of the important options:
skip - denotes the number of records to skip from the results. Used when retrieving paginated results, typically in combination with take. Default value is 0.
take - denotes the number of property results to return per response. Default value is 25. The minimum value is 1 and the maximum value is 500.
Pagination type: The Property Search API uses offset-based pagination via skip and take. Cursor-based pagination is not supported on this endpoint.
Total count: Every response includes a resultsFound field in the results.meta object. This value represents the total number of properties that match your search criteria, regardless of pagination settings. Use resultsFound to determine how many pages of results are available.
Paginating through results
A Property Search request can potentially return thousands of matched properties. Use skip and take to retrieve results in pages.
First request — retrieve the first 100 properties and check the total count:
{
"searchCriteria": {
"query": "Phoenix, AZ"
},
"options": {
"skip": 0,
"take": 100
}
}
The response will include results.meta.resultsFound (e.g., 5,432). Use this value to determine how many subsequent requests are needed.
Second request — retrieve the next 100 properties by advancing skip:
{
"searchCriteria": {
"query": "Phoenix, AZ"
},
"options": {
"skip": 100,
"take": 100
}
}
Continue incrementing skip by the take value until you have retrieved all results or reached your desired count.
Billing note: Each property returned in a response counts toward your usage, whether you are on a PAYG or subscription plan.
Avoiding duplicates with offset drifting
When paginating through large result sets using skip and take, the underlying dataset may change between requests (new records added, existing records updated). This can occasionally cause duplicate or missing properties across pages — a behavior known as offset drifting. For most use cases, simple skip/take pagination is sufficient and offset drifting has minimal impact.
However, if your application requires that no duplicate properties are returned across paginated requests — for example, when building a unique lead list or performing incremental data pulls — you can use the ids.propertyId.notInList filter to exclude previously retrieved property IDs from subsequent requests:
{
"searchCriteria": {
"query": "Phoenix, AZ",
"ids": {
"propertyId": {
"notInList": [
"prop-id-1",
"prop-id-2",
"prop-id-3"
]
}
}
},
"options": {
"skip": 0,
"take": 100
}
}
When using this approach, set skip back to 0 on each subsequent request since the exclusion list itself advances your position in the result set.
You can also use ids.addressHash.notInList to exclude properties by their address hash instead of property ID. Both approaches are valid — use whichever identifier your application stores.
Keep exclusion lists under the 10,000-value limit described in the text operators section above.
quicklistCounts - indicates whether or not you want to see quicklist counts in the response.
dateFormat - a flag indicating whether the date format should be datetime or date. Possible values are "iso-date-time" and "iso-date". When not specified it defaults to "iso-date-time".
{
"searchCriteria": {
"query": "Phoenix, AZ"
},
"options": {
"quicklistCounts": true,
"dateFormat": "iso-date"
}
}
sort - use to order the response by a particular field. Takes two inputs: sortOrder and field. sortOrder defines whether you want results in ascending, descending, or random order (denoted as "asc", "desc", or "random"). field denotes the column by which to order the response.
Supported sort fields: bathroomCount, bedroomCount, city, daysOnMarket, equityCurrentEstimatedBalance, estimatedValue, foreclosureStatus, imageCount, lastSalePrice, lastSaleDate, lastSoldDate, lastSoldPrice, livingAreaSquareFeet, mlsStatus, ownerOccupied, price, propertyType, propertyTypeCategory, soldDate, state, totalBuildingAreaSquareFeet, yearBuilt, zip.
{
"searchCriteria": {
"query": "Phoenix, AZ"
},
"options": {
"sort": {
"sortOrder": "asc",
"field": "city"
}
}
}
Billing and Dataset Costs
Understanding the billing model for Property Search and Property Lookup endpoints is crucial for cost-effective API usage and integration planning.
Token-Based Billing: Property Search and Property Lookup API costs are determined by your authentication token's provisioned dataset access, not by the specific data requested in individual API calls.
Why This Billing Model?: This token-based per-record billing approach provides predictable cost estimation and transparent usage reporting. You can accurately forecast API expenses based on expected property record volumes and your token's dataset provisioning. Usage reports clearly correlate charges to property records retrieved, making it simple to validate billing against actual usage and understand cost breakdowns.
Dataset Provisioning: Each API token is configured during setup with access to specific datasets. This provisioning determines your per-record billing rate for Property Search and Property Lookup requests.
Per-Record Costs: Every property record returned from Property Search and Property Lookup endpoints costs the full per-record rate for all datasets your token is provisioned to access. For example:
Token provisioned for multiple datasets: Each property costs the combined rate for all provisioned datasets
Runtime request for subset of data: Still charged the full rate for all provisioned datasets
Runtime Parameters Don't Affect Billing: The datasets and customProjection parameters in Property Search and Property Lookup requests filter which data is returned but do not reduce costs:
datasetsparameter filters which data categories are returned but you're charged for all provisioned datasetscustomProjectionspecifies which fields to return but doesn't change per-record costs
Cost Optimization Strategies:
Provision tokens with only datasets needed for your specific use case
Use
takeparameter to limit the number of records returned per searchImplement appropriate search criteria to get relevant results without over-fetching
Use sandbox tokens for development and testing to avoid charges
Billing Transparency: Your account dashboard shows detailed usage and costs broken down by dataset and time period for complete visibility into API expenses.
Production Planning: Before deploying to production, work with your account manager to optimize token provisioning based on your application's actual data needs.
For comprehensive dataset information and pricing details, see Datasets and Custom Projections.
More information on all the available properties within searchCriteria and options can be found in the Property Search API Reference.
