Property Searches

Part of the Content API. Saved property searches drive the listings shown on content pages, area pages and the home page, and search links (/search/results/?...) appear throughout site content. This page covers the criteria format, how to discover the fields a site accepts, and a procedure for building searches that actually filter.

Criteria format

Criteria are an object of {"field_id": ["value", ...]}. The same shape is accepted by the search and search_field_defaults fields on pages, by /api/v2/search/ as a query string, and in /search/results/?... links.

  • Keys are field ids from the site’s search form; values are always arrays of strings, and never empty — every field present must have at least one value.
  • Range fields take <id>_min / <id>_max keys, e.g. {"list_price_max": ["800000"]}.
  • Multi-select fields take the stored value, not the display label: "con", not "Condo". Repeat values for several, e.g. {"type": ["res", "con"]}.
  • Boolean fields take ["True"] or ["False"].
  • Only include fields relevant to the search — an omitted field means “any”.

Discovering a site’s search fields

GET/search_forms/api/advanced_search_form.json
GET/api/v2/search/metadata/
GET/api/v2/search/autocomplete-options/
GETPOST/api/v2/search/

Search fields and their values are site-specific (they follow the site’s MLS and its search form setup). These endpoints on the site’s public search stack let a tool explore them before writing a criteria object or a search link into content. They sit outside /api/v3/ and need no API key.

  • GET /search_forms/api/advanced_search_form.json returns the advanced search form as {"primary": [...], "secondary": [...]}. Each entry is one field: attr (the criteria key), label, widget_type, default_value, value (one row per checkbox value, e.g. type=res), dependent_fields, and choices as [value, label] pairs. City and subdivision choice lists follow the site’s default county.
  • GET /api/v2/search/autocomplete-options/ returns the site’s autocomplete index as [{"field", "value"}, ...] — every value the site knows for a field across all counties and cities, beyond the form’s default lists.
  • GET /api/v2/search/metadata/?<criteria> echoes {"description", "criteria"}: the site’s own parse of a query string. The site’s search pages silently drop unknown criteria (the page “works” but matches everything), so compare what you sent with the echoed criteria — any key missing there was ignored.
  • GET /api/v2/search/?<criteria> runs the search; page, per_page, fields, include_description and sort_highest / sort_lowest / sort_latest / sort_oldest / sort_relevance control the response, and the match count is in the X-Total-Count header.

Building criteria that work

These are the rules our own search-building tools follow. They exist because the failure mode of a bad search is silent: the page renders, the listings look plausible, and nothing is actually being filtered.

  • Only use field ids the site lists. Never guess or invent a field id. The site’s search pages ignore unknown keys silently; the /api/v3/ search field rejects them with a 422 naming each one.
  • Verify values before using them. Values are site-specific: a city, subdivision or school name that exists on one site may not exist on another, and a misspelled value matches nothing. Confirm it in the form’s choices or the autocomplete index first. Where fields depend on each other (subdivisions within a city, schools within an area), check the value against the rest of your criteria, not just globally.
  • Map only what actually maps. When a request mentions something no field covers — “jacuzzi”, “good schools”, “nice neighborhood” — skip it rather than approximating with an unrelated field, and say so in whatever notes or summary you give the user. A wrong filter is worse than a missing one.
  • Set the property type when one is implied. “Homes”, “condos”, “townhouses”, “land” all map to the type field; include it whenever the request names one.
  • Check the parse. Before writing a search link into content, run the criteria through metadata/ and confirm every key came back. A link built from ignored keys shows every listing on the site.

Test before you attach

A criteria object that validates can still be a bad search. Before saving it or attaching it to a page:

  1. Run it (GET /api/v2/search/?<criteria>&per_page=1) and read the match count from X-Total-Count.
  2. Zero results? Broaden stepwise while keeping the request’s core intent: drop the least important filters first, widen price ranges, reduce bedroom/bathroom minimums, expand the location to nearby areas — re-verifying values as you go. Re-test after each change and keep the best version rather than the last one. If nothing helps after a few rounds, report that honestly instead of shipping a search that shows nothing.
  3. Suspiciously large counts? If the count is close to the site’s whole inventory, the criteria probably are not filtering — check for ignored keys with metadata/.
  4. Sanity-check the description. metadata/ returns the site’s own human-readable description of the search; if it does not read like what was asked for, the criteria do not mean what you think.

POST /api/v2/search/?<criteria> stores the search and returns {"search_id", "search_id_int"}. The base36 search_id is the short id that appears in the site’s own URLs; identical criteria reuse the same saved search.

  • Link to results with /search/results/<search_id>/ (or /search/results/?<criteria> without saving), and the map view with /map_search/results/<search_id>/1/.
  • Attach the search to a page through the search field on content pages, area pages and the home page — it accepts the search_id, the numeric id, or a criteria object directly (the API saves or reuses a search for you).
  • search_field_defaults takes the same values to pre-fill a page’s search form without filtering its listings.