Skip to main content

Listing Autocomplete API

Type-ahead suggestions matched against listing titles and the names of the properties those listings contain. Each suggestion ships with a ready-to-use rawParam you can drop straight into the search request — no separate "resolve selection" step.


Endpoint

GET /api/v1/crs/listings/autocomplete?q={query}&limit={limit}

Also exposed as GET /api/v1/search/listings/autocomplete for callers that already use that prefix.

Query parameters

NameTypeRequiredDefaultDescription
qstringyesUser input. Whitespace-only or empty q returns an empty list (not a 400).
limitintno10Max suggestions returned. Capped at 50.

%, _, and \ in q are escaped before being wrapped in %…%, so a value like 100% won't degenerate into a full table scan.


Response

{
"suggestions": [
{
"label": "Villa Aurora",
"sublabel": "Anjuna, North Goa",
"matchType": "LISTING",
"listingId": "lst_abc123",
"rawParam": {
"filterName": "listingId",
"filterValues": ["lst_abc123"]
}
},
{
"label": "Sea View Suite",
"sublabel": "Villa Aurora — Anjuna, North Goa",
"matchType": "PROPERTY",
"listingId": "lst_abc123",
"propertyName": "Sea View Suite",
"rawParam": {
"filterName": "listingId",
"filterValues": ["lst_abc123"]
}
}
]
}

Field reference

FieldTypeNotes
labelstringPrimary text the UI renders. Listing title for LISTING hits, property name for PROPERTY hits.
sublabelstringlocality, city for LISTING hits; listing title — locality, city for PROPERTY hits. Omitted when empty.
matchTypeenumLISTING or PROPERTY — indicates which column matched q.
listingIdstringThe listing the suggestion resolves to. Same as rawParam.filterValues[0].
propertyNamestringPresent only on PROPERTY hits. Omitted otherwise.
rawParamobjectA { filterName, filterValues } object identical in shape to the entries you send to /listings. Designed to be passed through unchanged.

Ranking is prefix matches first, then alphabetical by the matched label. A property hit and a listing hit that share the same listingId are both returned — the UI can de-duplicate or render both as distinct rows.


The whole point of returning rawParam is that the client doesn't have to know about listingId or any other filter name. Just append the chosen suggestion's rawParam to the search request's rawParams array:

// 1. User types, you fetch suggestions
const { suggestions } = await fetch(`/api/v1/crs/listings/autocomplete?q=${q}`).then(r => r.json());

// 2. User picks one
const chosen = suggestions[0];

// 3. Run the search with the rest of the form state + the chosen rawParam
const searchBody = {
rawParams: [
chosen.rawParam, // pass through unchanged
{ filterName: "checkInDate", filterValues: ["2026-06-01"] },
{ filterName: "checkOutDate", filterValues: ["2026-06-04"] },
{ filterName: "adults", filterValues: ["5"] },
],
};

await fetch("/api/v1/crs/listings", { method: "POST", body: JSON.stringify(searchBody) });

curl equivalent:

curl 'https://bard-crs-api.elivaas.com/api/v1/crs/listings/autocomplete?q=villa%20aurora&limit=5'

Location autocomplete

Same idea, different endpoint — type-ahead over distinct city / state / country / locality values that actually appear on listings. Use this when the user is searching for a place rather than a specific property.

GET /api/v1/crs/listings/autocomplete/locations?q={query}&limit={limit}

Also exposed at /api/v1/search/listings/autocomplete/locations.

Response

{
"suggestions": [
{
"label": "Goa",
"matchType": "STATE",
"rawParam": { "filterName": "state", "filterValues": ["Goa"] }
},
{
"label": "North Goa",
"matchType": "CITY",
"rawParam": { "filterName": "city", "filterValues": ["North Goa"] }
},
{
"label": "Anjuna",
"matchType": "LOCALITY",
"rawParam": { "filterName": "locality", "filterValues": ["Anjuna"] }
}
]
}
matchTyperawParam.filterName
CITYcity
STATEstate
COUNTRYcountry
LOCALITYlocality

sublabel, listingId, and propertyName are omitted on location hits — only label, matchType, and rawParam are returned.

Values are pulled only from rows in the listing table, so a suggested city/state/country/locality is guaranteed to have at least one listing. No more dead-end picks that resolve to zero results.

q, limit, escaping, ordering (prefix matches first), and empty-q handling all behave exactly the same as the main autocomplete endpoint above.


Behavior notes

  • Empty / whitespace q{ "suggestions": [] } with HTTP 200. Lets a debounced type-ahead UI fire on every keystroke without producing 400s on the first character.
  • No matches → empty suggestions array, not 404.
  • limit is clamped to [1, 50]. Values outside that range are silently coerced.
  • Search is case-insensitive (PostgreSQL ILIKE).
  • One listing may appear twice — once as a LISTING hit and once as a PROPERTY hit — when both its title and a property name match q. Use listingId to de-duplicate if desired; both rows share the same rawParam.

Why rawParam and not just listingId?

Returning the full rawParam object means callers can append it without knowing the filter name, escaping rules, or array shape. If a future suggestion type needs to filter by something other than listingId (e.g. tags for a "Pet Friendly" suggestion), only the server changes — the client keeps appending whatever rawParam it received.