# 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)