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.
Overview
Section titled “Overview”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
Base URL
Section titled “Base URL”Production: https://api.garagejs.comLocal Development: http://localhost:4000Property Operations
Section titled “Property Operations”1. Request a List of Properties
Section titled “1. Request a List of Properties”The /api/v1/properties endpoint allows you to search and filter properties with advanced query parameters.
Basic Query
Section titled “Basic Query”Get all properties with default pagination:
curl http://localhost:4000/api/v1/propertiesWith Pagination
Section titled “With Pagination”Control the number of results and offset:
# Get 20 properties, skip the first 40curl http://localhost:4000/api/v1/properties?limit=20&offset=40Filter by Property Type
Section titled “Filter by Property Type”# Get all housescurl http://localhost:4000/api/v1/properties?property_type=house
# Get all apartmentscurl http://localhost:4000/api/v1/properties?property_type=apartmentFilter by Status
Section titled “Filter by Status”# Get active listingscurl http://localhost:4000/api/v1/properties?status=active
# Get sold propertiescurl http://localhost:4000/api/v1/properties?status=soldFilter by Price Range
Section titled “Filter by Price Range”# Properties between $500k and $1Mcurl http://localhost:4000/api/v1/properties?min_price=500000&max_price=1000000Filter by Bedrooms and Bathrooms
Section titled “Filter by Bedrooms and Bathrooms”# At least 3 bedroomscurl http://localhost:4000/api/v1/properties?min_bedrooms=3
# At least 2 bathroomscurl http://localhost:4000/api/v1/properties?min_bathrooms=2Filter by City
Section titled “Filter by City”# Properties in San Franciscocurl http://localhost:4000/api/v1/properties?city=San%20FranciscoComplex Query with Multiple Filters
Section titled “Complex Query with Multiple Filters”Combine multiple filters for precise searches:
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"Response Format
Section titled “Response Format”{ "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 }}Query Parameters
Section titled “Query Parameters”| Parameter | Type | Description | Default | Max |
|---|---|---|---|---|
limit | integer | Number of results per page | 50 | 1000 |
offset | integer | Number of results to skip | 0 | - |
property_type | string | Filter by property type (house, apartment, etc.) | - | - |
status | string | Filter by status (active, sold, etc.) | - | - |
min_price | number | Minimum price filter | - | - |
max_price | number | Maximum price filter | - | - |
min_bedrooms | integer | Minimum number of bedrooms | - | - |
min_bathrooms | integer | Minimum number of bathrooms | - | - |
city | string | Filter by city name | - | - |
2. Get Detail on a Property
Section titled “2. Get Detail on a Property”Retrieve complete information about a specific property using its ID.
Request
Section titled “Request”curl http://localhost:4000/api/v1/properties/123e4567-e89b-12d3-a456-426614174000Response
Section titled “Response”{ "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" }}Error Response (404)
Section titled “Error Response (404)”{ "success": false, "error": { "code": "NOT_FOUND", "message": "Property not found" }}3. See Property Statistics
Section titled “3. See Property Statistics”Get comprehensive statistics about properties in the database, perfect for dashboards and market analysis.
Request
Section titled “Request”curl http://localhost:4000/api/v1/properties/statsResponse
Section titled “Response”{ "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" }}Use Cases
Section titled “Use Cases”- 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
4. Submit New Properties
Section titled “4. Submit New Properties”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.
Submit Single Property
Section titled “Submit Single Property”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 Response (200)
Section titled “Success Response (200)”{ "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 }}Validation Error Response (422)
Section titled “Validation Error Response (422)”{ "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" } ] }}Submit Batch of Properties
Section titled “Submit Batch of Properties”For better performance when submitting multiple properties, use the batch endpoint:
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" }}Required Headers
Section titled “Required Headers”| Header | Description | Required |
|---|---|---|
Content-Type | Must be application/json | Yes |
Authorization | Bearer token for authentication | Yes |
X-Harvester-Source | Identifies the source of the data | Yes |
Data Processing Pipeline
Section titled “Data Processing Pipeline”When properties are submitted, the Mill performs:
- Validation: Checks required fields, data types, and business rules
- Geocoding: Normalizes addresses and enriches with coordinates
- Deduplication: Identifies and handles duplicate properties
- Storage: Persists validated data to the database
- Enrichment: Adds computed fields and metadata
Authentication
Section titled “Authentication”All harvester endpoints require authentication. Include your API token in the Authorization header:
Authorization: Bearer YOUR_API_TOKENContact your administrator to obtain API tokens.
Error Handling
Section titled “Error Handling”The Mill API uses standard HTTP status codes:
| Status Code | Description |
|---|---|
200 | Success |
202 | Accepted (async processing) |
400 | Bad request (invalid parameters) |
401 | Unauthorized (missing or invalid token) |
404 | Resource not found |
422 | Validation failed |
500 | Internal 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" }}Rate Limiting
Section titled “Rate Limiting”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: 100X-RateLimit-Remaining: 95X-RateLimit-Reset: 1610712000Geocoding System
Section titled “Geocoding System”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
Next Steps
Section titled “Next Steps”- Geocoding System - Address parsing and geocoding
- API Reference - Complete OpenAPI documentation
- Configuration - Setup and configuration
- Architecture - System design and components