# Search Properties
> Source: /api-reference/members/properties/search
> Paginated property search with optional map markers
> Endpoint: POST /members/properties/search

## Overview [#overview]

Paginated property search with comprehensive filtering, sorting, and optional map markers. When `includeMarkers` is true, the backend runs a single pipeline execution and returns both the scored top-N results for the list view and lean map markers for all matching properties, reducing round-trips compared to separate search and map calls.

This is the primary search endpoint for Journey's property discovery experience.

## Authentication [#authentication]

Requires a valid Clerk JWT token in the Authorization header.

## Request Body [#request-body]

<ParamField body="limit" type="number">
  Maximum number of properties to return (default: 20)
</ParamField>

<ParamField body="offset" type="number">
  Number of properties to skip for pagination (default: 0)
</ParamField>

<ParamField body="sort" type="string">
  Sort order for results. Options: "recommended", "price_low", "price_high", "rating", "distance"
</ParamField>

<ParamField body="includeMarkers" type="boolean">
  Whether to include map markers for all matching properties (default: false)
</ParamField>

<ParamField body="dates" type="object">
  Check-in and check-out dates for availability and pricing
  
  <Expandable title="properties">
    <ParamField body="checkIn" type="string">
      Check-in date (YYYY-MM-DD)
    </ParamField>
    
    <ParamField body="checkOut" type="string">
      Check-out date (YYYY-MM-DD)
    </ParamField>
  </Expandable>
</ParamField>

<ParamField body="location" type="object">
  Location-based search filters
  
  <Expandable title="properties">
    <ParamField body="latitude" type="number">
      Latitude coordinate
    </ParamField>
    
    <ParamField body="longitude" type="number">
      Longitude coordinate
    </ParamField>
    
    <ParamField body="radius" type="number">
      Search radius in kilometers
    </ParamField>
    
    <ParamField body="city" type="string">
      City name
    </ParamField>
    
    <ParamField body="state" type="string">
      State or region
    </ParamField>
    
    <ParamField body="country" type="string">
      Country code (ISO 3166-1)
    </ParamField>
  </Expandable>
</ParamField>

<ParamField body="priceRange" type="object">
  Price range in points per night
  
  <Expandable title="price range">
    <ParamField body="min" type="number">
      Minimum price per night
    </ParamField>
    
    <ParamField body="max" type="number">
      Maximum price per night
    </ParamField>
  </Expandable>
</ParamField>

<ParamField body="guestRating" type="number">
  Minimum guest rating (1-5 stars)
</ParamField>

<ParamField body="amenities" type="array">
  Required amenities (array of amenity IDs)
</ParamField>

<ParamField body="propertyTypes" type="array">
  Property types to include (e.g., "hotel", "resort", "villa")
</ParamField>

<ParamField body="maxGuests" type="number">
  Maximum guest capacity required
</ParamField>

<ParamField body="brands" type="array">
  Specific brands to include
</ParamField>

<ParamField body="tags" type="array">
  Property tags for themed filtering
</ParamField>

<RequestExample>

```bash cURL
curl -X POST "http://localhost:3000/v1/members/properties/search" \
  -H "Authorization: Bearer YOUR_CLERK_JWT" \
  -H "Content-Type: application/json" \
  -d '{
    "limit": 20,
    "offset": 0,
    "sort": "recommended",
    "includeMarkers": true,
    "dates": {
      "checkIn": "2024-06-15",
      "checkOut": "2024-06-17"
    },
    "location": {
      "city": "New York",
      "state": "NY",
      "country": "US"
    },
    "priceRange": {
      "min": 30000,
      "max": 100000
    },
    "guestRating": 4.0,
    "amenities": ["pool", "spa"],
    "propertyTypes": ["hotel", "resort"]
  }'
```

```typescript TypeScript
const searchProperties = async (searchParams) => {
  const response = await fetch('http://localhost:3000/v1/members/properties/search', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer YOUR_CLERK_JWT',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(searchParams)
  });
  
  return response.json();
};

// Example usage
const results = await searchProperties({
  limit: 20,
  offset: 0,
  sort: 'recommended',
  includeMarkers: true,
  dates: {
    checkIn: '2024-06-15',
    checkOut: '2024-06-17'
  },
  location: {
    city: 'New York',
    state: 'NY',
    country: 'US'
  },
  priceRange: { min: 30000, max: 100000 },
  guestRating: 4.0,
  amenities: ['pool', 'spa'],
  propertyTypes: ['hotel', 'resort']
});
```

```javascript Bruno
// From: bruno/Members (Surface)/Properties/Search.bru
POST {{host}}/members/properties/search
Authorization: Bearer {{clerkJwt}}
Content-Type: application/json

{
  "limit": 20,
  "offset": 0,
  "sort": "recommended", 
  "includeMarkers": true,
  "dates": {
    "checkIn": "2024-06-15",
    "checkOut": "2024-06-17"
  },
  "location": {
    "city": "New York",
    "state": "NY", 
    "country": "US"
  },
  "priceRange": {
    "min": 30000,
    "max": 100000
  },
  "guestRating": 4.0,
  "amenities": ["pool", "spa"],
  "propertyTypes": ["hotel", "resort"]
}
```

</RequestExample>

## Response [#response]

Returns a paginated list of properties matching the search criteria, with optional map markers.

<ResponseField name="items" type="array" required>
  Array of property summaries matching search criteria
  
  <Expandable title="property items">
    <ResponseField name="id" type="number" required>
      Unique property identifier
    </ResponseField>
    
    <ResponseField name="documentId" type="string" required>
      Property document identifier
    </ResponseField>
    
    <ResponseField name="name" type="string" required>
      Property name
    </ResponseField>
    
    <ResponseField name="brandName" type="string">
      Brand or chain name
    </ResponseField>
    
    <ResponseField name="type" type="string" required>
      Property type (hotel, resort, villa, etc.)
    </ResponseField>
    
    <ResponseField name="baseRoomCategory" type="string">
      Base room type offered
    </ResponseField>
    
    <ResponseField name="nightlyCost" type="number" required>
      Cost per night in points
    </ResponseField>
    
    <ResponseField name="guestRating" type="number">
      Average guest rating (1-5 stars)
    </ResponseField>
    
    <ResponseField name="maxGuests" type="number" required>
      Maximum guest capacity
    </ResponseField>
    
    <ResponseField name="address" type="object" required>
      Property location details
      
      <Expandable title="address">
        <ResponseField name="city" type="string" required>
          City name
        </ResponseField>
        
        <ResponseField name="state" type="string">
          State or region
        </ResponseField>
        
        <ResponseField name="country" type="string" required>
          Country name
        </ResponseField>
        
        <ResponseField name="latitude" type="number" required>
          Latitude coordinate
        </ResponseField>
        
        <ResponseField name="longitude" type="number" required>
          Longitude coordinate
        </ResponseField>
      </Expandable>
    </ResponseField>
    
    <ResponseField name="heroImage" type="object">
      Main property image
      
      <Expandable title="hero image">
        <ResponseField name="url" type="string" required>
          Image URL
        </ResponseField>
        
        <ResponseField name="alt" type="string">
          Alt text for accessibility
        </ResponseField>
      </Expandable>
    </ResponseField>
    
    <ResponseField name="amenities" type="array">
      Available property amenities
    </ResponseField>
    
    <ResponseField name="tags" type="array">
      Property tags for categorization
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="total" type="number" required>
  Total number of properties matching the search criteria
</ResponseField>

<ResponseField name="limit" type="number" required>
  Number of items returned in this response
</ResponseField>

<ResponseField name="offset" type="number" required>
  Number of items skipped for pagination
</ResponseField>

<ResponseField name="markers" type="array">
  Map markers for all matching properties (only when includeMarkers=true)
  
  <Expandable title="markers">
    <ResponseField name="id" type="number" required>
      Property ID for the marker
    </ResponseField>
    
    <ResponseField name="latitude" type="number" required>
      Marker latitude
    </ResponseField>
    
    <ResponseField name="longitude" type="number" required>
      Marker longitude
    </ResponseField>
    
    <ResponseField name="nightlyCost" type="number" required>
      Points cost for map display
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="searchViewport" type="object">
  Suggested map viewport for the search results
  
  <Expandable title="viewport">
    <ResponseField name="bounds" type="object" required>
      Bounding box for the search area
      
      <Expandable title="bounds">
        <ResponseField name="north" type="number" required>
          Northern boundary
        </ResponseField>
        
        <ResponseField name="south" type="number" required>
          Southern boundary
        </ResponseField>
        
        <ResponseField name="east" type="number" required>
          Eastern boundary
        </ResponseField>
        
        <ResponseField name="west" type="number" required>
          Western boundary
        </ResponseField>
      </Expandable>
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseExample>

```json 200 Response - Search Results
{
  "items": [
    {
      "id": 123,
      "documentId": "prop_manhattan_boutique_hotel",
      "name": "Manhattan Boutique Hotel",
      "brandName": "Journey Urban Collection",
      "type": "Hotel",
      "baseRoomCategory": "Deluxe Room",
      "nightlyCost": 45000,
      "guestRating": 4.6,
      "maxGuests": 4,
      "address": {
        "city": "New York",
        "state": "NY",
        "country": "United States",
        "latitude": 40.7128,
        "longitude": -74.0060
      },
      "heroImage": {
        "url": "https://cdn.example.com/hotels/manhattan-boutique.jpg",
        "alt": "Manhattan boutique hotel exterior"
      },
      "amenities": ["WiFi", "Fitness Center", "Pool", "Spa", "Restaurant"],
      "tags": ["business", "luxury", "urban"]
    }
  ],
  "total": 245,
  "limit": 20,
  "offset": 0,
  "markers": [
    {
      "id": 123,
      "latitude": 40.7128,
      "longitude": -74.0060,
      "nightlyCost": 45000
    }
  ],
  "searchViewport": {
    "bounds": {
      "north": 40.8176,
      "south": 40.6829,
      "east": -73.9442,
      "west": -74.0859
    }
  }
}
```

</ResponseExample>

## Search Behavior [#search-behavior]

### Unified Pipeline [#unified-pipeline]
When `includeMarkers=true`, the backend executes a single search pipeline with a limit of 3000 properties, then:
1. Returns the top-N scored results for the list view (based on requested limit/offset)
2. Provides lean map markers for all 3000 matching properties
3. Includes a suggested viewport for the map display

### Scoring and Ranking [#scoring-and-ranking]
The search uses a sophisticated scoring algorithm that considers:
- **Relevance**: Match to search criteria and location
- **Member preferences**: Based on profile and search history
- **Property quality**: Guest ratings and amenity scores
- **Availability**: Real-time availability for requested dates
- **Pricing**: Competitive pricing within the member's range

### Personalization [#personalization]
Search results are personalized using a user seed based on the member ID, ensuring:
- Consistent ranking across pagination
- Personalized recommendations
- A/B testing capabilities
- Privacy-compliant personalization

## Related Endpoints [#related-endpoints]

- [Search Hydrate](/api-reference/members/properties/search-hydrate) - Bulk property enrichment by IDs
- [Get Suggestions](/api-reference/members/properties/suggestions) - Location autocomplete
- [Get Filters](/api-reference/members/properties/filters) - Available filter options
