Skip to content

The Mill API

The Mill is Pillow’s core API service, built in Go. It serves as the central hub for property data management, providing powerful endpoints for querying, analyzing, and submitting property listings.

The Mill provides:

  • RESTful API endpoints for property operations
  • Authentication and authorization services
  • Data validation and geocoding
  • Rate limiting and security features
  • Health monitoring and metrics
Production: https://api.garagejs.com
Local Development: http://localhost:4000

The /api/v1/properties endpoint allows you to search and filter properties with advanced query parameters.

Get all properties with default pagination:

Terminal window
curl http://localhost:4000/api/v1/properties

Control the number of results and offset:

Terminal window
# Get 20 properties, skip the first 40
curl http://localhost:4000/api/v1/properties?limit=20&offset=40
Terminal window
# Get all houses
curl http://localhost:4000/api/v1/properties?property_type=house
# Get all apartments
curl http://localhost:4000/api/v1/properties?property_type=apartment
Terminal window
# Get active listings
curl http://localhost:4000/api/v1/properties?status=active
# Get sold properties
curl http://localhost:4000/api/v1/properties?status=sold
Terminal window
# Properties between $500k and $1M
curl http://localhost:4000/api/v1/properties?min_price=500000&max_price=1000000
Terminal window
# At least 3 bedrooms
curl http://localhost:4000/api/v1/properties?min_bedrooms=3
# At least 2 bathrooms
curl http://localhost:4000/api/v1/properties?min_bathrooms=2
Terminal window
# Properties in San Francisco
curl http://localhost:4000/api/v1/properties?city=San%20Francisco

Combine multiple filters for precise searches:

Terminal window
curl "http://localhost:4000/api/v1/properties?property_type=apartment&status=active&min_price=300000&max_price=600000&min_bedrooms=2&city=Oakland&limit=50"
{
"success": true,
"data": [
{
"id": "123e4567-e89b-12d3-a456-426614174000",
"external_id": "prop-12345",
"source": "realestate-com-au",
"url": "https://example.com/property/12345",
"property_type": "house",
"address": {
"formatted": "123 Main St, Auckland, New Zealand",
"street": "123 Main St",
"city": "Auckland",
"state": "Auckland",
"country": "New Zealand",
"postal_code": "1010"
},
"details": {
"bedrooms": 3,
"bathrooms": 2,
"square_meters": 150,
"price_current": 850000
},
"status": "active",
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:30:00Z"
}
],
"metadata": {
"total": 1234,
"limit": 50,
"offset": 0,
"has_more": true
}
}
ParameterTypeDescriptionDefaultMax
limitintegerNumber of results per page501000
offsetintegerNumber of results to skip0-
property_typestringFilter by property type (house, apartment, etc.)--
statusstringFilter by status (active, sold, etc.)--
min_pricenumberMinimum price filter--
max_pricenumberMaximum price filter--
min_bedroomsintegerMinimum number of bedrooms--
min_bathroomsintegerMinimum number of bathrooms--
citystringFilter by city name--

Retrieve complete information about a specific property using its ID.

Terminal window
curl http://localhost:4000/api/v1/properties/123e4567-e89b-12d3-a456-426614174000
{
"success": true,
"data": {
"id": "123e4567-e89b-12d3-a456-426614174000",
"external_id": "prop-12345",
"source": "realestate-com-au",
"url": "https://example.com/property/12345",
"title": "Stunning 3 Bedroom House with Ocean Views",
"description": "Beautiful family home in prime location...",
"property_type": "house",
"property_subtype": "detached",
"status": "active",
"address": {
"formatted": "123 Main St, Auckland, New Zealand",
"street": "123 Main St",
"city": "Auckland",
"state": "Auckland",
"country": "New Zealand",
"postal_code": "1010"
},
"location": {
"latitude": -36.8485,
"longitude": 174.7633
},
"financial": {
"price_current": 850000,
"currency": "NZD",
"price_per_sqm": 5666.67
},
"dimensions": {
"square_meters_total": 150,
"square_meters_interior": 130,
"land_area_sqm": 400
},
"rooms": {
"bedrooms_total": 3,
"bathrooms_total": 2,
"living_rooms": 1,
"parking_spaces": 2
},
"construction": {
"year_built": 2010,
"building_type": "house",
"condition": "excellent"
},
"features": [
"ocean_view",
"modern_kitchen",
"hardwood_floors",
"central_heating"
],
"listing": {
"listing_status": "active",
"listing_date": "2024-01-05T00:00:00Z",
"days_on_market": 15
},
"images": [
{
"url": "https://example.com/images/main.jpg",
"type": "main",
"order": 1
}
],
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:30:00Z"
}
}
{
"success": false,
"error": {
"code": "NOT_FOUND",
"message": "Property not found"
}
}

Get comprehensive statistics about properties in the database, perfect for dashboards and market analysis.

Terminal window
curl http://localhost:4000/api/v1/properties/stats
{
"success": true,
"data": {
"total_properties": 15847,
"properties_by_city": {
"San Francisco": 4523,
"Oakland": 3842,
"Berkeley": 2876,
"San Jose": 2156,
"Sacramento": 2450
},
"average_sale_price": 685450.75,
"average_price_by_status": {
"active": 725000.5,
"pending": 695000.25,
"sold": 665000.0,
"off_market": 580000.0
},
"price_distribution": [
{
"price_range": "$0-$250k",
"count": 1247,
"min_price": 0,
"max_price": 250000
},
{
"price_range": "$250k-$500k",
"count": 3842,
"min_price": 250000,
"max_price": 500000
},
{
"price_range": "$500k-$750k",
"count": 4523,
"min_price": 500000,
"max_price": 750000
},
{
"price_range": "$750k-$1M",
"count": 3156,
"min_price": 750000,
"max_price": 1000000
},
{
"price_range": "$1M+",
"count": 3079,
"min_price": 1000000,
"max_price": null
}
],
"properties_by_type": {
"house": 7234,
"apartment": 5623,
"condo": 2156,
"townhouse": 834
},
"timestamp": "2024-01-15T10:30:00Z"
}
}
  • Dashboard Analytics: Display market overview and trends
  • Market Analysis: Compare prices across cities and property types
  • Business Intelligence: Track inventory and pricing patterns
  • Reporting: Generate insights for stakeholders

Harvesters can submit property data to the Mill for validation, geocoding, and storage. Two endpoints are available:

  • Single Property: For submitting one property at a time
  • Batch Submission: For submitting multiple properties efficiently

Both endpoints require authentication and a harvester source identifier.

Terminal window
curl -X POST http://localhost:4000/harvesters/properties/single \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "X-Harvester-Source: your-source-name" \
-d '{
"title": "Stunning 2 Bedroom Apartment for Sale",
"property_type": "residential",
"property_subtype": "apartment",
"status": "for_sale",
"price": 750000,
"financial": {
"price_current": 750000,
"currency": "AUD"
},
"address": {
"street": "45 High Street",
"city": "Sydney",
"state": "NSW",
"country": "Australia",
"postal_code": "2000"
},
"location": {
"latitude": -33.8688,
"longitude": 151.2093
},
"dimensions": {
"square_meters_total": 85
},
"rooms": {
"bedrooms_total": 2,
"bathrooms_total": 1
},
"construction": {
"year_built": 2015
},
"listing": {
"listing_status": "active",
"listing_date": "2024-01-05T00:00:00Z"
},
"data_quality": {
"data_source": "realestate-com-au",
"data_quality_score": 0.7
}
}'
{
"success": true,
"data": {
"property_id": "123e4567-e89b-12d3-a456-426614174000",
"status": "created",
"message": "Property successfully validated and stored"
},
"validation": {
"passed": true,
"warnings": []
},
"geocoding": {
"status": "success",
"normalized_address": "45 High Street, Sydney, NSW 2000, Australia",
"confidence": 0.95
}
}
{
"success": false,
"error": {
"code": "VALIDATION_FAILED",
"message": "Property validation failed"
},
"validation": {
"passed": false,
"errors": [
{
"field": "financial.price_current",
"message": "Price must be greater than 0"
},
{
"field": "address.city",
"message": "City is required"
}
]
}
}

For better performance when submitting multiple properties, use the batch endpoint:

Terminal window
curl -X POST http://localhost:4000/harvesters/properties/batch \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "X-Harvester-Source: your-source-name" \
-d '{
"properties": [
{
"external_id": "prop-12345",
"title": "3 Bedroom House",
"property_type": "residential",
"property_subtype": "house",
"status": "for_sale",
"price": 850000,
"financial": {
"price_current": 850000,
"currency": "NZD"
},
"address": {
"street": "123 Main St",
"city": "Auckland",
"country": "New Zealand"
},
"rooms": {
"bedrooms_total": 3,
"bathrooms_total": 2
}
},
{
"external_id": "prop-12346",
"title": "2 Bedroom Apartment",
"property_type": "residential",
"property_subtype": "apartment",
"status": "for_sale",
"price": 650000,
"financial": {
"price_current": 650000,
"currency": "NZD"
},
"address": {
"street": "456 Queen St",
"city": "Auckland",
"country": "New Zealand"
},
"rooms": {
"bedrooms_total": 2,
"bathrooms_total": 1
}
}
]
}'

Batch Response (200 - Synchronous Processing)

Section titled “Batch Response (200 - Synchronous Processing)”

For batches of 100 or fewer properties:

{
"success": true,
"data": {
"batch_id": "batch_20240115103045",
"status": "completed",
"summary": {
"total_submitted": 2,
"accepted": 2,
"rejected": 0,
"duplicates": 0,
"processing_time_ms": 1250
},
"results": [
{
"external_id": "prop-12345",
"property_id": "123e4567-e89b-12d3-a456-426614174000",
"status": "created"
},
{
"external_id": "prop-12346",
"property_id": "223e4567-e89b-12d3-a456-426614174001",
"status": "created"
}
]
}
}

Batch Response (202 - Asynchronous Processing)

Section titled “Batch Response (202 - Asynchronous Processing)”

For batches of more than 100 properties:

{
"success": true,
"data": {
"batch_id": "batch_20240115103045",
"status": "queued",
"message": "Batch queued for asynchronous processing",
"summary": {
"total_submitted": 250,
"estimated_processing_time_minutes": 5
},
"status_url": "/api/v1/harvesters/batches/batch_20240115103045"
}
}
HeaderDescriptionRequired
Content-TypeMust be application/jsonYes
AuthorizationBearer token for authenticationYes
X-Harvester-SourceIdentifies the source of the dataYes

When properties are submitted, the Mill performs:

  1. Validation: Checks required fields, data types, and business rules
  2. Geocoding: Normalizes addresses and enriches with coordinates
  3. Deduplication: Identifies and handles duplicate properties
  4. Storage: Persists validated data to the database
  5. Enrichment: Adds computed fields and metadata

All harvester endpoints require authentication. Include your API token in the Authorization header:

Terminal window
Authorization: Bearer YOUR_API_TOKEN

Contact your administrator to obtain API tokens.


The Mill API uses standard HTTP status codes:

Status CodeDescription
200Success
202Accepted (async processing)
400Bad request (invalid parameters)
401Unauthorized (missing or invalid token)
404Resource not found
422Validation failed
500Internal server error

All error responses follow this format:

{
"success": false,
"error": {
"code": "ERROR_CODE",
"message": "Human-readable error message"
},
"meta": {
"timestamp": "2024-01-15T10:30:00Z"
}
}

To ensure fair usage, the Mill API implements rate limiting:

  • Public Endpoints: 100 requests per minute per IP
  • Harvester Endpoints: 1000 requests per minute per API token

Rate limit information is included in response headers:

X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1610712000

The Mill includes a powerful geocoding system for address parsing, normalization, and coordinate lookup. Learn more about:

  • Address parsing and component extraction
  • Multi-provider support (Nominatim, libpostal)
  • Integration with property submission
  • Best practices and troubleshooting

👉 Geocoding System Documentation