CayIQ MCP Data API help
A code-accurate reference for search_listings. Feed statistics and examples below were verified against the production feed on 2026-08-28: about 16,800 listings, including 2,986 Active listings.
Using search_listings
Pass an object of optional filters to search_listings. CSV fields accept comma-separated values with optional surrounding spaces. Filters combine as AND conditions; multiple values in one CSV field match any listed value. Every response includes mls_attribution and idx_disclaimer; retain both when presenting listing data.
Your API key carries a county entitlement: the counties it is authorized for. Every tool response is limited to those counties on every call, whether or not you send the county parameter.
Regions vs counties
region is the MLS's MLSAreaMajor vocabulary, not a county field. Its roughly 65 values mix neighborhoods, towns, and some counties. It is useful for MLS area selection, but it is not a reliable county filter: most Beaufort County listings are tagged with a neighborhood rather than Beaufort.
County-level scoping belongs to CountyOrParish, exposed through the live county parameter. The production feed has the following Active counts:
| CountyOrParish value | Active listings | Use |
|---|---|---|
| Beaufort | 2,329 | Valid value for the live county parameter. |
| Jasper | 565 | Valid value for the live county parameter. |
| Hampton | 27 | Valid value for the live county parameter. |
| Colleton | 9 | Valid value for the live county parameter. |
Live The county parameter narrows results within your key's authorized counties; matching is case-insensitive. It can never widen your scope: counties outside your entitlement are ignored, and a request only for out-of-entitlement counties returns zero rows rather than an error. Your key sees only its authorized subset of the table above.
High-volume verified MLSAreaMajor examples
| region value | Listings in feed | What it illustrates |
|---|---|---|
| Bluffton/General | 422 | Town/general MLS area |
| Hilton Head/General | 342 | Town/general MLS area |
| Jasper County | 267 | County-named MLS area |
| Sea Pines | 102 | Neighborhood MLS area |
| City of Beaufort | 59 | Municipality MLS area |
These are the production values and counts verified for this reference. Fetch the current complete vocabulary with get_area_taxonomies rather than hard-coding a static region list; taxonomy results are limited to your authorized counties.
Search parameters
| Parameter | Type | Required | Filters on | Allowed/example values | Notes/reliability |
|---|---|---|---|---|---|
q | text | No | Address fields, city, ZIP/postal code, listing ID, and PublicRemarks. | 110 Shell Midden, 29928, 1052240617 | Case-insensitive; full addresses work across the separately stored street number, name, and unit fields. |
transaction | enum | No | PropertyType. | sale, rent, all | Omitted preserves existing all-listings behavior. Sale excludes ResidentialLease; rent returns leases only. |
status | text/CSV | No | Trestle StandardStatus. | Active,Pending; feed includes Active (2,986), Closed (13,008), Pending, ActiveUnderContract, and others. | Use exact MLS status values. CSV is supported. |
region | text/CSV | No | MLSAreaMajor. | Sea Pines or Bluffton/General,Hilton Head/General | About 65 mixed MLS-area values. Not reliable for county scoping; see Regions vs counties. |
county | text/CSV | No | The feed's clean county field (CountyOrParish). | Beaufort,Jasper | Live Narrows within your key's authorized counties; case-insensitive. Never widens: out-of-entitlement counties are ignored, and asking only for them returns zero rows. |
sub_area | text/CSV | No | MLSAreaMinor subdivision. | A current value from get_area_taxonomies | 261 distinct values; CSV is supported. Query the taxonomy tool for current spelling. |
view | text/CSV | No | CSV-in-string View tokens. | One or more current View tokens, comma-separated | About 98% populated (16,496). Match tokens, not a display label invented by the client. |
style | text/CSV | No | ArchitecturalStyle. | One or more current style tokens, comma-separated | About 71% populated (11,972); missing values are common. |
amenity | text/CSV | No | AssociationAmenities. | One or more current amenity tokens, comma-separated | About 95% populated (16,015). This is the amenity field used by the server. |
exterior | text/CSV | No | ExteriorFeatures. | One or more current exterior-feature tokens, comma-separated | About 86% populated (14,383). |
specialty | enum | No | Server-defined specialty behavior. | foreclosures, featured, sold | foreclosures matches SpecialListingConditions Foreclosure/REO/BankOwned variants (about 31 listings; rare). sold means Closed status (13,008). featured means the calling consumer's tenant featured listings, in configured display order. |
min_pricemax_price | number | No | ListPrice lower/upper bound. | min_price: 500000, max_price: 1000000 | Prices are about 95% sanely populated. Apply client-side sanity bounds because occasional bad outliers can exist. |
min_bedsmin_baths | number | No | BedroomsTotal / stored bath value sourced from BathroomsFull for this feed. | min_beds: 3, min_baths: 2 | Both are about 80% populated. Do not build on BathroomsTotalInteger: it is 0% populated. The API exposes the normalized bath value as bathrooms_total. |
year_built_minyear_built_max | number | No | YearBuilt lower/upper bound. | year_built_min: 2000, year_built_max: 2020 | About 89% populated. |
sqft_min | number | No | LivingArea lower bound. | sqft_min: 1800 | About 87% populated. |
pagepage_size | number | No | Result pagination, not listing fields. | page: 2, page_size: 50 | Defaults are page 1 and 50 results. page_size is capped at 100. |
Worked examples
Text, status, and MLS area
{
"q": "oceanfront",
"status": "Active,Pending",
"region": "Sea Pines,Bluffton/General",
"sub_area": "<a current MLSAreaMinor value>",
"page": 1,
"page_size": 25
}
County narrowing (within your entitlement)
{
"status": "Active",
"county": "Jasper",
"min_beds": 3,
"page_size": 50
}
Features and price
{
"min_price": 500000,
"max_price": 1000000,
"min_beds": 3,
"min_baths": 2,
"sqft_min": 1800,
"view": "<current View token>",
"amenity": "<current AssociationAmenities token>",
"exterior": "<current ExteriorFeatures token>"
}
Age, style, and specialty
{
"year_built_min": 2000,
"year_built_max": 2020,
"style": "<current ArchitecturalStyle token>",
"specialty": "foreclosures",
"page_size": 100
}
Replace angle-bracket placeholders with exact tokens from current feed data. county values outside your key's entitlement are ignored rather than erroring.
Call the API from your server
Use the REST tool endpoint at https://api.cayiq.com/api/mcp/tools/<tool> from trusted server-side code. Send the one-time key in the Authorization: Bearer header. Do not put the key in browser JavaScript, a React client component, NEXT_PUBLIC_* environment variables, local storage, a URL, source control, a screenshot, or a support ticket.
Store the key in your deployment secret manager as CAYIQ_MCP_API_KEY. The examples query search_listings; replace the input object only with filters supported above. The county parameter can narrow a key’s scope but can never widen it.
Python
Install the dependency once
python -m pip install requests
Set the key in your server environment, then run this code
import os
import requests
BASE_URL = "https://api.cayiq.com/api/mcp"
API_KEY = os.environ["CAYIQ_MCP_API_KEY"]
def call_tool(tool: str, arguments: dict) -> dict:
response = requests.post(
f"{BASE_URL}/tools/{tool}",
headers={
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
},
json=arguments,
timeout=30,
)
response.raise_for_status()
return response.json()
result = call_tool("search_listings", {
"status": "Active",
"county": "Beaufort",
"min_beds": 3,
"page_size": 10,
})
# Log safe aggregates, not the credential.
print({"total": result.get("total"), "page": result.get("page")})
JavaScript (Node.js)
Create a local server-only .env file, add it to .gitignore, then run Node with --env-file
# .env (do not commit this file) CAYIQ_MCP_API_KEY=<your one-time key> # Run: node --env-file=.env search-listings.mjs
const BASE_URL = "https://api.cayiq.com/api/mcp";
const apiKey = process.env.CAYIQ_MCP_API_KEY;
if (!apiKey) {
throw new Error("CAYIQ_MCP_API_KEY must be set in the server environment.");
}
async function callTool(tool, arguments_) {
const response = await fetch(`${BASE_URL}/tools/${tool}`, {
method: "POST",
headers: {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json",
},
body: JSON.stringify(arguments_),
});
if (!response.ok) {
throw new Error(`CayIQ MCP request failed with HTTP ${response.status}`);
}
return response.json();
}
const result = await callTool("search_listings", {
status: "Active",
county: "Beaufort",
min_beds: 3,
page_size: 10,
});
// Log safe aggregates, not the credential.
console.log({ total: result.total, page: result.page });
For protocol-native clients, the same bearer key also works with the JSON-RPC MCP endpoint at https://api.cayiq.com/api/mcp. Start with initialize, then call tools/list or tools/call. The REST examples above are the simplest way to validate a server integration.
Field reliability in responses
Build address displays from StreetName (100% populated) plus City (99.97% populated), and include other address components when available. Do not use UnparsedAddress; it is 0% populated in this feed. For bedrooms use BedroomsTotal (about 80%). For baths use BathroomsFull (about 80%), represented by the API's normalized bathrooms_total; BathroomsTotalInteger is 0% populated. The server returns its normalized fields in lower snake case, including street_name, city, bedrooms_total, and bathrooms_total.
Search, map, and bulk-export rows include an absolute primary_image_url when image media exists. get_listing also includes ordered images[] entries with URL, order, caption, media type, MIME type, and suggested filename. Follow redirects when downloading. URLs always use the CayIQ media proxy; raw OAuth-protected Cotality/Trestle URLs are never returned.