Purpose
Retrieve active for-sale listings matching location, property type, and optional price or ZIP constraints. The site accepts search state as a JSON-encoded condition query parameter and supports pagination and sorting directly in the URL. ZIP filtering is available through location.zipCode; property types use site enum values such as singleFamily.
When to Use
Use for requests to find current residential listings on cospringshomefinder.com, especially when the request specifies a city, county, state, ZIP code, property type, maximum price, or multiple pages of results.
Workflow
- Build a condition object from caller inputs. Prefer the narrowest supplied location:
- ZIP:
{"location":{"zipCode":["{zip}"]},"price":",{max-price}","propertyType":["{property-type-enum}"]} - City/state:
{"location":{"city":["{city}, {state}"]},"price":",{max-price}","propertyType":["{property-type-enum}"]} - County/state:
{"location":{"county":["{county}"],"state":"{state}"},"price":",{max-price}","propertyType":["{property-type-enum}"]}Omit optional keys when the caller did not request those constraints. The observed maximum-price form is the string',{max-price}'; retain that representation for compatibility.
- URL-encode the JSON and navigate directly to
https://cospringshomefinder.com/listing?condition={encoded-condition}&listingSort=PRICE_ASC&page=1. If the site ignoreslistingSort, omit it; pagination remainspage={number}. - After client rendering, run this evaluator as-is:
(() => {
const rows = [...document.querySelectorAll('.house-table-row')];
const candidates = rows.length ? rows : [...document.querySelectorAll('a[href*="/listing-detail/"]')].map(a => a.closest('tr') || a.closest('[class*=card]') || a.parentElement);
const seen = new Set();
return candidates.map((row, index) => {
const link = row.querySelector('a[href*="/listing-detail/"]');
if (!link || seen.has(link.href)) return null;
seen.add(link.href);
const text = (row.innerText || '').replace(/\s+/g, ' ').trim();
const priceMatch = text.match(/\$\s*[\d,]+(?:\.\d{2})?/);
const zipMatch = text.match(/\b\d{5}(?:-\d{4})?\b/);
return {
index,
price: priceMatch ? Number(priceMatch[0].replace(/[$,\s]/g, '')) : null,
href: link.href,
zip: zipMatch ? zipMatch[0] : null,
text
};
}).filter(Boolean);
})()- Navigate directly to successive URLs with the same encoded condition and
page={page+1}. Stop when a page has no.house-table-rowelements and no/listing-detail/anchors, then concatenate and deduplicate byhref. - Enforce the caller's requested maximum price and, when needed, verify ZIP, city, county, state, and property type from each row's address/details. Return all matching pages, not merely the first page.
- If the listing page errors or unexpectedly returns no content, diagnose without using the form. Probe, as needed: the bare listing URL with
listingSort=PRICE_ASC&page=1; the requested condition with a numeric price; the requested condition with the price as a string; andcondition=%7B%7D. After each client-rendered page, run:
(() => ({title: document.title, bodyLen: document.body.innerText.length, snippet: document.body.innerText.slice(0,800), rowCount: document.querySelectorAll('.house-table-row').length, detailLinkCount: document.querySelectorAll('a[href*="/listing-detail/"]').length}))()Compare probes to identify condition parsing or value-type failures.
Site-Specific Gotchas
- Search state is a URL-encoded JSON object in
condition. location.zipCodeaccepts an array of ZIP strings and is the direct ZIP-filter form; use it instead of relying only on post-extraction filtering.location.cityaccepts an array, typically with values formatted asCity, ST;location.countyaccepts an array and may be combined withstate.- The observed property-type enum is
singleFamily; other caller terminology may need mapping to the site's enum. - The observed maximum-price encoding is the string
',{max-price}'; numeric and string representations can behave differently, so test both when diagnosing failures. listingSort=PRICE_ASCandpage={number}are direct query parameters; use them instead of sort or pagination controls.- Results may be client-rendered after
domcontentloaded; wait briefly for.house-table-rowor listing-detail anchors before treating the page as empty. - Listing rows commonly use
.house-table-row; card layouts may expose listing cards througha[href*="/listing-detail/"]. Deduplicate absolute listing URLs. - The first currency amount in a row/card is treated as the listing price, and the first listing-detail anchor is its URL; verify when a card contains multiple monetary values.
- A cookie-consent banner may appear on first visit. If it blocks interaction, click the visible
div[role="button"]containingAccept All Cookies; direct listing URLs remain preferred. - When combining location filters or applying a narrower ZIP constraint, verify the returned address text rather than trusting only the search URL.
Expected Output
Return one record per active matching listing with at least {price, href, zip, text}. Include all pages, remove duplicate href values, and report the requested location, property type, price, sorting, and pagination filters. For debugging, also report each probe URL and its {title, bodyLen, snippet, rowCount, detailLinkCount}, identifying the first differing probe.