Files
Hanson.xyz Dev acc8ac87a0 wip
2026-01-04 17:50:08 -06:00

299 lines
7.2 KiB
Markdown
Executable File

# MLS Grid API Reference
Documentation for the MLS Grid API v2 used by this plugin.
## Base URL
```
https://api.mlsgrid.com/v2
```
## Authentication
Bearer token authentication via HTTP header:
```
Authorization: Bearer {access_token}
```
**Required Header:**
```
Accept-Encoding: gzip
```
The API requires gzip compression and will return error 400 without it.
## Rate Limits
| Limit | Value |
|-------|-------|
| Per Second | 2 requests |
| Per Hour | 7,200 requests |
| Per Day | 40,000 requests |
| Data Per Hour | 4GB |
Exceeding limits returns HTTP 429 and temporarily suspends access.
## Endpoints
### Property
```
GET /Property
```
Main endpoint for listing data.
**Query Parameters:**
- `$filter` - OData filter expression (required)
- `$expand` - Include related resources: Media, Rooms, UnitTypes
- `$top` - Records per page (max 5000, max 1000 with $expand)
- `$select` - Specific fields to return
- `$orderby` - Sort order
**Example:**
```
/Property?$filter=OriginatingSystemName eq 'northstar' and MlgCanView eq true&$expand=Media&$top=1000
```
### Member
```
GET /Member
```
Agent/member records.
### Office
```
GET /Office
```
Brokerage office records.
### OpenHouse
```
GET /OpenHouse
```
Open house event records.
### Lookup
```
GET /Lookup
```
Field value definitions. Query no more than once per day.
## OData Filter Syntax
### Operators
| Operator | Description | Example |
|----------|-------------|---------|
| eq | Equals | `City eq 'Austin'` |
| ne | Not equals | `Status ne 'Sold'` |
| gt | Greater than | `ListPrice gt 200000` |
| ge | Greater or equal | `BedroomsTotal ge 3` |
| lt | Less than | `ListPrice lt 500000` |
| le | Less or equal | `YearBuilt le 2020` |
| and | Logical AND | `City eq 'Austin' and BedroomsTotal ge 3` |
| or | Logical OR | Limited to 5 per query |
| in | In list | `City in ('Austin', 'Dallas')` |
### Required Filters
Every Property request MUST include:
```
OriginatingSystemName eq 'northstar'
```
For initial import, add:
```
MlgCanView eq true
```
### Timestamp Filters
For incremental sync:
```
ModificationTimestamp gt 2024-01-15T00:00:00.000Z
```
## Pagination
Responses include `@odata.nextLink` field containing URL for next page.
```json
{
"@odata.context": "...",
"value": [...],
"@odata.nextLink": "https://api.mlsgrid.com/v2/Property?$filter=...&$skip=1000"
}
```
Continue fetching until `@odata.nextLink` is absent.
## Property Fields
### Core Fields
| Field | Type | Description |
|-------|------|-------------|
| ListingKey | string | Unique identifier |
| ListingId | string | MLS listing number |
| StandardStatus | string | Active, Pending, Closed, etc. |
| ListPrice | decimal | Listing price |
| ClosePrice | decimal | Sold price |
### Address Fields
| Field | Type | Description |
|-------|------|-------------|
| StreetNumber | string | Street number |
| StreetName | string | Street name |
| StreetSuffix | string | St, Ave, Blvd, etc. |
| UnitNumber | string | Unit/apt number |
| City | string | City name |
| StateOrProvince | string | State abbreviation |
| PostalCode | string | ZIP code |
| CountyOrParish | string | County name |
| Latitude | decimal | GPS latitude |
| Longitude | decimal | GPS longitude |
### Property Details
| Field | Type | Description |
|-------|------|-------------|
| PropertyType | string | Residential, Land, Commercial, etc. |
| PropertySubType | string | Single Family, Condo, etc. |
| BedroomsTotal | integer | Total bedrooms |
| BathroomsTotalInteger | integer | Total bathrooms |
| BathroomsFull | integer | Full bathrooms |
| BathroomsHalf | integer | Half bathrooms |
| LivingArea | integer | Square feet |
| LotSizeArea | decimal | Lot size |
| LotSizeUnits | string | Acres, SqFt |
| YearBuilt | integer | Year built |
| GarageSpaces | integer | Garage spaces |
### Description Fields
| Field | Type | Description |
|-------|------|-------------|
| PublicRemarks | string | Property description |
| Directions | string | Driving directions |
### Agent/Office Fields
| Field | Type | Description |
|-------|------|-------------|
| ListAgentKey | string | Listing agent ID |
| ListAgentMlsId | string | Agent MLS ID |
| ListOfficeKey | string | Listing office ID |
| ListOfficeName | string | Office name |
| ListOfficeMlsId | string | Office MLS ID |
### Timestamps
| Field | Type | Description |
|-------|------|-------------|
| ModificationTimestamp | datetime | Last modified (use for sync) |
| PhotosChangeTimestamp | datetime | Media last changed |
| ListingContractDate | date | Listed date |
| CloseDate | date | Sold date |
| DaysOnMarket | integer | DOM count |
### MLS Grid Fields
| Field | Type | Description |
|-------|------|-------------|
| MlgCanView | boolean | OK to display (false = delete) |
| MlgCanUse | array | Permitted use cases (IDX, VOW, etc.) |
| OriginatingSystemName | string | Source MLS identifier |
## Media (via $expand)
When using `$expand=Media`, each property includes Media array:
```json
{
"Media": [
{
"MediaKey": "abc123",
"MediaURL": "https://media.mlsgrid.com/...",
"Order": 1,
"ImageWidth": 1200,
"ImageHeight": 800,
"MediaModificationTimestamp": "2024-01-15T10:30:00Z"
}
]
}
```
**Important:** MediaURL is for downloading only. Store images locally.
## Sync Strategy
### Initial Import
1. Query with `MlgCanView eq true` to get viewable records
2. Follow `@odata.nextLink` for pagination
3. Store `ModificationTimestamp` from last record
### Incremental Sync
1. Query with `ModificationTimestamp gt {last_timestamp}`
2. Do NOT filter by MlgCanView (need to see deletions)
3. If `MlgCanView = false`, delete local record
### Media Sync
1. Check `PhotosChangeTimestamp` on each property
2. If changed, replace all media for that listing
3. Match by `MediaKey`, download via `MediaURL`
4. Delete media where `MediaKey` no longer exists
### Error Recovery
Store `@odata.nextLink` after each page. On failure, resume from that URL.
## Best Practices
1. **Sequential requests only** - Do not parallelize API calls
2. **Respect rate limits** - 2 req/sec max, pause if approaching limits
3. **Use $expand wisely** - Reduces per-page limit from 5000 to 1000
4. **Store raw JSON** - Keep original response for debugging
5. **Query Lookup sparingly** - Once per day maximum
6. **Don't hotlink media** - Download and serve from local storage
## Error Responses
```json
{
"error": {
"code": 400,
"message": "Error description",
"target": "misc",
"details": []
}
}
```
| Code | Meaning |
|------|---------|
| 400 | Bad request (check filters, missing gzip) |
| 401 | Unauthorized (invalid token) |
| 429 | Rate limited (wait and retry) |
| 500+ | Server error (retry with backoff) |
## Resources
- [MLS Grid Documentation](https://docs.mlsgrid.com/)
- [API v2 Reference](https://docs.mlsgrid.com/api-documentation/api-version-2.0)
- [Best Practices Guide](https://www.mlsgrid.com/resources)