Guide

MLS Data Pipeline Architecture: A Reference Design for the RESO Web API

A reference architecture for ingesting MLS data via the RESO Web API: replication, media, normalization, duplicates, compliance checkpoints and failure modes.

Illustration: five pipeline stages from connector to search index and API, with media worker and display policy alongside.

The short answer

An MLS integration is a replication system, not a set of API calls. RESO describes the pattern: replicators initially pull an entire data set, and then continually request the most recent data changes. A design that holds up has nine parts: a connector per feed, replication jobs with a stored cursor, a raw store, normalization to the RESO Data Dictionary, a separate media worker, a canonical store, duplicate linking across boards, a display-policy filter, and a search index that can be rebuilt. Compliance and monitoring are attached to specific stages, not added at the end.

This guide is the design reference. For what changes between RETS and the Web API, see RESO Web API vs RETS; for licenses and approval, see how to get MLS data access.

The reference architecture

Reference architecture for an MLS data pipelineA top-to-bottom flow. MLS feeds enter a connector, then replication, a raw store, normalization and a media worker, a canonical store, duplicate linking, a display policy filter, and finally the search index and API that websites and apps read. Four stages carry a compliance checkpoint and seven send a signal to a monitoring rail on the right. Each stage is described in the list below the diagram.MLS feeds (RESO Web API)MONITORING AND ALERTS1ConnectorOAuth 2 token · paging · rate limiter2Replicationcursor per feed and resourceC3Raw storepayload as received, tagged by feedC4NormalizeData Dictionary names and lookups5Media workerdownload once to your own storageC6Canonical storeone row per listing per feed7Link duplicatesgroup cross-listed properties8Display policyopt-outs · scope · attributionC9Search index and APIderived, rebuildable from the storeCcompliance checkpointmonitoring signal
Reference architecture for an MLS data pipeline. Stages 4 and 5 both read from the raw store and run side by side; they are drawn in sequence to keep the diagram readable on a phone.

The same architecture as a list:

  1. Connector. One connector per feed holds the credentials, follows the server-driven paging links and keeps requests under that platform’s rate limits. Monitoring signal: HTTP 429 and error rate per feed.
  2. Replication. Three jobs share one cursor per feed and resource: the initial load, the incremental sync on ModificationTimestamp, and the reconciliation pass that finds records the feed no longer carries. Compliance checkpoint: removals applied inside the refresh window. Monitoring signal: sync lag: now minus the newest timestamp received.
  3. Raw store. Every record is saved as received, with its feed, originating system and license type, so mappings can be re-run without re-downloading. Compliance checkpoint: license scope recorded per record.
  4. Normalize. Field names and lookup values are mapped to the RESO Data Dictionary; local fields are kept under their own namespace. Monitoring signal: unmapped fields and lookup values.
  5. Media worker. A separate queue downloads each photo once into your own storage and serves it from there, never from the source URL. Compliance checkpoint: no display from source media URLs. Monitoring signal: media queue depth and failures.
  6. Canonical store. The system of record holds one normalized row per listing per originating system, keyed by originating system and listing key. Monitoring signal: record count against the feed’s count.
  7. Link duplicates. Listings of the same property from different boards are linked into a group; neither record is deleted. Monitoring signal: duplicate-group rate per market.
  8. Display policy. A filter applies seller opt-outs, the license scope of each surface and the attribution each board requires before anything reaches search or a page. Compliance checkpoint: display rules enforced before the index.
  9. Search index and API. The search index and the API are derived from the canonical store and can be rebuilt from it; websites, apps and CRMs read from here. Monitoring signal: index count against the store.

Two properties matter more than the choice of database. First, every stage after the raw store can be re-run from stored data, so a mapping fix or a rule change does not need a new download. Second, each feed runs on its own: its own credentials, cursor, queue and error budget, so one board's outage does not stop the others.

1. Connector: authentication, paging and rate limits

The transport is fixed by the standard. The RESO Web API uses the OAuth2 Bearer Token and Client Credentials standards for authorization, requires TLS 1.2 or above, and says providers must support server-driven paging using @odata.nextLink. So the paging loop is the same everywhere: request, store, follow the next link until there is none.

What differs per platform is the budget, and the connector has to enforce it:

In practice: one worker per feed and resource, a token-bucket limiter set below the published limit, and backoff that treats 429 as a signal to slow the whole feed rather than to retry one request.

2. Initial replication

The first load is the largest job the pipeline will run, and it should be resumable. MLS Grid's guidance is explicit: order is by ModificationTimestamp, so after an error you continue where you left off by adding the ModificationTimestamp that you last received to the query. It asks for the initial query to filter on MlgCanView equal to true, so deleted records are not loaded, and for a single OriginatingSystemName in every request, which means one load per MLS. A request returns at most 5,000 records, or 1,000 when $expand is used. Its best-practices guide also says to ask support for a grace period in advance if the initial import will exceed the normal limits.

Trestle works differently at volume. A normal query returns a maximum of 1,000 records; for more than 1,000,000 records you need the replication endpoint. That endpoint is not resumable: if the process fails part way through, you have to restart it, and its links expire after 5 minutes of inactivity. Write each page to the raw store as it arrives so a slow downstream step can never stall the download.

3. Incremental sync and removals

After the first load, each run asks for records changed since the cursor. Three details decide whether the copy stays correct.

The cursor is a value you received, not your clock. Trestle recommends tracking the latest value you received, so clock differences do not cause you to miss updates. MLS Grid says the same, and adds that if you store only part of the data, the cursor must still be the greatest ModificationTimestamp you have received, whether or not you store the record. It also warns against range queries on ModificationTimestamp: use “greater than the cursor” only.

Know whose timestamp it is. On MLS Grid, ModificationTimestamp is the time the record was modified by MLS Grid; the MLS's own time is in OriginatingSystemModificationTimestamp. On Trestle a record's timestamp can move because mappings or enumerations that affect it were updated, not only because the listing changed. Both mean a large batch of “changes” can arrive with no agent having touched a listing. Trestle names this directly: being prepared for mass updates is important.

Removals do not arrive the same way on every platform. MLS Grid flags them: when MlgCanView changes to false, the record must be removed from your local data store, and after 7 days such records are removed from the feed entirely. A sync that is down for longer than that will never see those removals, so it needs a full comparison when it comes back. Trestle's incremental feed does not report removals at all; you query for all of the keys and remove any records that do not match, and a key-only query can return up to 300,000 records at a time.

How often to run is a platform answer too. MLS Grid says a replication request once every 15 minutes is sufficient; Trestle suggests every few minutes to every hour. RESO has ratified an event-based alternative to timestamp polling, the EntityEvent resource, with a sequence number that can only increase, which removes the timestamp problems above where a provider offers it.

4. Media

Photos need their own design, and on MLS Grid the rules changed in 2026:

Those four facts dictate the design. The media worker takes the URL from the replication response and downloads straight away, inside the hour; it never stores a URL to fetch later. A failed download is retried by asking the API for the record again, not by replaying the URL. Work is keyed by MediaKey, so a photo is fetched once and a missing key means a deletion.

On Trestle, media URLs have their own quota of 18,000 requests per hour and 480 per minute, separate from data queries. Trestle notes this suits downloading primary photos first and secondary photos when they are requested. Either way, media runs on its own queue with its own limiter, so a backlog of photos cannot delay price and status changes.

5. Normalization to the Data Dictionary

The target schema is the RESO Data Dictionary. DD 2.0 is the current version for RESO certification, with 41 resources, 1,745 fields and 3,683 lookups; a 2.1 draft is in development. A platform that converts for you still leaves work:

Keep the mapping as versioned configuration per originating system, and expect it to be replaced. When MARIS moved to a new MLS system, MLS Grid published it under a new OriginatingSystemName and told every consumer to run a new initial import, with new local-field and key prefixes. A pipeline that treats “one MLS” as a fixed schema has to be rewritten for that; one that treats each originating system as a loadable source just adds a source and retires the old one.

6. Identity and duplicates across boards

Three identifiers do three jobs. ListingKey is a unique identifier for the record from the immediate source. ListingId is the number people quote, and RESO warns that in a merged system it may not be unique. OriginatingSystemKey is the key from the system with authoritative control over the record. The primary key of the canonical store is therefore the pair of originating system and key, never the listing number alone. On MLS Grid, key fields carry an MLS prefix that should be removed before displaying externally but added back when requesting records, so store both forms.

The harder problem is one property listed in two MLSs. RESO's answer is the Universal Parcel Identifier, created so that property data does not depend on addresses and standalone parcel numbers for identification. But the Data Dictionary page for UniversalPropertyId shows usage adoption of 21%, 90 of 420 organizations, so you cannot rely on it being present. A workable order of evidence:

  1. UniversalPropertyId, where both records carry it.
  2. Parcel number plus county or region.
  3. A normalized address (unit included) plus coordinates within a small distance.
  4. Listing-level agreement as a tie-breaker: same listing office, price and status dates.

Link duplicates into a group; do not merge or delete. Each record still belongs to its board, with that board's attribution and removal rules, and a wrong match must be reversible. Choose which record to show by a written rule (for example, the board the viewing brokerage belongs to). MLS Grid's IDX rules also require that where MLS data is shown with data from other sources, the source of the information is prominently identified, so the source has to survive every later stage.

7. Search index and read path

The index is a derived copy. Build it only from records that passed the display-policy filter, and make a full rebuild from the canonical store a routine job, because rule changes and mapping fixes both need it. Index removals in the same run that applies them to the store.

Two of our published projects show this shape. The real estate SaaS case study describes MongoDB as the source of truth, Elasticsearch for search and Redis as a cache, a sync engine rewritten with delta detection, retry logic and per-feed error isolation, and property detail pages read directly from the database. The W3|re case study describes a pipeline that ingests from four MLS systems, normalizes schemas and deduplicates cross-listed properties.

8. Compliance checkpoints

Display and license rules are enforced by code at fixed points, so they cannot be skipped by a new page or a new API route. The rules below are MLS Grid's public IDX Rules, updated September 17, 2026, used as a worked example; every MLS has its own, and yours are in your license.

CheckpointWhere it is enforcedPublished rule (example)
License scopeRecorded at the raw store; checked by the display-policy filter per surfaceMLS Grid's MlgCanUse field indicates which use cases a record qualifies for; records with only the VOW value cannot be used for IDX
Refresh and removalReplication; alert when sync lag approaches the limitRefresh at least once every twelve (12) hours, including removing data no longer in the feed
Seller opt-outsDisplay-policy filter, before the indexData Dictionary flags for whether the seller allows the listing and the address on Internet sites; MLS Grid rule 7 covers withholding the listing or its address from display
AttributionFields carried to the index; templates for results and detail pagesShow the listing brokerage name, the listing number, the email or phone provided, and the status next to the property information
Mixed sourcesSource kept on every record; a filter in searchSearch across MLS and other sources must let the user filter results by the source of the information; the rule lists MLSs it does not apply to
MediaMedia workerDo not link directly to the media URLs you receive
AI featuresAnything that builds embeddings or sends listing data to a modelMLS Grid's AI addendum defines AI training to include vector embeddings and retrieval indices that persist beyond a single user session, and bars it without prior express authorization
ScrapingEdge and application logsMake reasonable efforts to avoid scraping, including monitoring the website for signs of it
Audit accessOperationsGive MLS Grid direct access for monitoring and ensuring compliance; Stellar MLS vendor products are subject to routine data audit checks

Rules also differ inside one platform. MLS Grid's rules carry clauses for single MLSs, such as an MRED requirement that a site which limits listings by objective criteria states that some listings have been excluded. Model rules as data keyed by originating system, not as conditions scattered through templates. For Canada, where CREA's rules apply instead, see MLS data access in Canada.

Failure modes and how to detect them

Many of these failures raise no error; the data just stops being right. Each row below is a failure that follows from the platform behavior cited above, with the signal that exposes it.

FailureHow to detect itWhat to do
Sync stalls silently (expired token, crashed worker, suspended access)Sync lag per feed: current time minus the newest timestamp received. Alert well inside the refresh rule in your licensePage someone; lag is the one metric every feed needs
Removed listings stay on the siteDaily count of your keys against the feed's keys, per originating system; any key you hold that the feed does notRun reconciliation on a schedule, not only after incidents
Outage longer than the removal windowLag exceeded the platform's retention for removal flags (7 days on MLS Grid)Do a full key comparison before resuming incremental sync
Missed updates from a clock-based cursorSample of records whose stored timestamp is older than the feed'sStore the greatest timestamp received and query strictly from it
Mass update floods the queueChanged-record count per run far above the feed's normal rangeLet it drain under the rate limit; skip downstream work when a content hash is unchanged
Rate-limit suspension429 responses; request and byte counters per hour and per day against the published limitsLimiter below the limit, backoff on 429, one replication request at a time per feed
Photos missing or staleListings with zero stored photos; media queue age; MediaKeys in the store without a fileDownload at once from a fresh URL; re-request the record instead of retrying an expired URL
Schema drift (new field, renamed field, new lookup value)Count of unmapped fields and lookup values per run; a diff of the metadata documentHold unknown values in the raw store, alert, extend the mapping, re-run normalization
Source system replacedPlatform notices; a feed whose change volume drops to zeroLoad the new originating system as a new source, then switch and retire the old one
Wrong duplicate matchGroups whose members disagree on price, beds or coordinates beyond a toleranceUnlink (records were never merged) and tighten the rule
Record shown outside its license scopeAutomated test per surface that queries for records it must not show; expect zeroEnforce scope in the display-policy filter, not in page code
Index out of step with the storeDocument count in the index against displayable rows in the storeRebuild from the store; the index holds nothing that cannot be rebuilt

A build checklist

  • List every feed: platform, license type, resources, rate limits, and how removals are signaled.
  • Give each feed its own credentials store, limiter, cursor and queue.
  • Make the initial load resumable where the platform allows it, and write pages to the raw store as they arrive.
  • Store the cursor as the greatest timestamp received, per feed and resource.
  • Schedule reconciliation separately from incremental sync.
  • Put media on its own queue, keyed by MediaKey, writing to your own storage.
  • Keep mappings and display rules as versioned data per originating system.
  • Key the canonical store by originating system and key; link duplicates, never merge them.
  • Enforce opt-outs, license scope and attribution fields before the index.
  • Alert on sync lag, key-count drift, 429s, media queue age and unmapped values.
  • Rehearse a full index rebuild and a full re-normalization before launch.

Feed and license fees are a separate question: the MLS and IDX cost calculator adds up published fees for the boards you pick, and the cost guide explains them. If you are still deciding whether to own a pipeline at all, read IDX vendor or custom build.

Frequently Asked Questions

How often should an MLS sync run?

Often enough to stay well inside your license’s refresh rule. MLS Grid’s IDX rules require a refresh at least once every twelve (12) hours, and its replication guide says a request once every 15 minutes is sufficient. Trestle suggests every few minutes to every hour.

How do you detect deleted MLS listings?

It depends on the platform. MLS Grid sets a flag: when MlgCanView changes to false the record must be removed. Trestle’s incremental feed does not report removals, so you query for all of the keys and remove any records that do not match. Run that comparison on a schedule either way.

Can we display photos straight from the MLS media URLs?

Not on MLS Grid, which says you must maintain your own copy of all media files and that its media URLs are signed, single-use and expire after one hour. Download each photo once to your own storage and serve it from there.

How do you deduplicate a property listed in two MLSs?

Link the records rather than merging them. Use the RESO Universal Property Identifier where present, but the Data Dictionary shows usage adoption of 21% for that field, so fall back to parcel number, normalized address and coordinates. Keep both records so each board’s attribution and removal rules still apply.

Can MLS data be used for AI search or embeddings?

Check each license. MLS Grid’s AI Use Addendum permits AI-assisted search and response for IDX and VOW uses, but defines AI training to include vector embeddings and retrieval indices that persist beyond a single user session and bars it without prior express authorization. Other MLSs set their own terms.

Standards, platform limits and rules were read from the linked RESO, MLS Grid, Trestle and Stellar MLS pages on October 10, 2026. NAR's policy handbook required a member login on that date, so display rules are quoted from MLS Grid's public IDX Rules as an example. Limits and rules change and differ by MLS; your license and the current documentation decide.

Planning or repairing an MLS pipeline? See our MLS and IDX integration service. Every project starts with a 30-minute technical discovery call with an engineer.

Technical consultation

Designing or Repairing an MLS Pipeline?

Tell us which feeds you pull, how they sync today and where the data goes wrong. An engineer will tell you which parts of this architecture your setup is missing.

  • A 30-minute technical discovery call with an engineer, not a salesperson.
  • Fixed-scope projects, monthly retainers, or a combination, agreed after the call.
  • Small, well-defined tasks get a scoped estimate. We reply within 1 business day.

Prefer to pick a time? Book a call on Calendly

Tell us about your project

We'll review your details and reach out within 1 business day.

Book Strategy CallCase Studies