7.2 KiB
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.
{
"@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:
{
"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
- Query with
MlgCanView eq trueto get viewable records - Follow
@odata.nextLinkfor pagination - Store
ModificationTimestampfrom last record
Incremental Sync
- Query with
ModificationTimestamp gt {last_timestamp} - Do NOT filter by MlgCanView (need to see deletions)
- If
MlgCanView = false, delete local record
Media Sync
- Check
PhotosChangeTimestampon each property - If changed, replace all media for that listing
- Match by
MediaKey, download viaMediaURL - Delete media where
MediaKeyno longer exists
Error Recovery
Store @odata.nextLink after each page. On failure, resume from that URL.
Best Practices
- Sequential requests only - Do not parallelize API calls
- Respect rate limits - 2 req/sec max, pause if approaching limits
- Use $expand wisely - Reduces per-page limit from 5000 to 1000
- Store raw JSON - Keep original response for debugging
- Query Lookup sparingly - Once per day maximum
- Don't hotlink media - Download and serve from local storage
Error Responses
{
"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) |