TL;DR
There is no single public MLS API. RESO defines common fields and Web API rules; a local MLS or licensed provider supplies the data, credentials, and usage terms. IDX covers approved listing display, not unrestricted reuse.
The companion Colab turns authorized
PublicRemarksinto filterable signals with exact source evidence. It uses synthetic data by default and never sends an MLS URL or restricted remarks to ScrapeGraphAI.
Does MLS have an API?
Many multiple listing services expose property data through a Web API, a licensed vendor, or an IDX delivery product. There is no nationwide endpoint where any developer can create a key and download every listing. Access comes from the organization that controls the records in the markets your application covers.
The RESO Web API gives those organizations a common technical language. It uses web standards including HTTP, REST, JSON, and OData. The RESO Data Dictionary standardizes names such as ListingKey, ListPrice, and PublicRemarks. RESO is a standards body, not a source of MLS records or credentials. RESO directs data-access requests back to the local MLS or software provider.
The official RESO Web API page, captured August 3, 2026. RESO standardizes how participating systems transport data; it does not provide a universal MLS database or API key.
A vendor can pass RESO certification and still have no contract to supply the market you need. A valid JSON response also says nothing about whether your product may store, display, enrich, or redistribute the records. Those permissions live in the agreement, not in the response schema.
If your requirement is aggregate housing research rather than active property listings, an MLS feed may be unnecessary. The Redfin API and Data Center guide covers official market CSVs and their limits. The Zillow scraper guide covers public listing-page extraction as a separate workflow. Neither one substitutes for licensed MLS access in a listing product.
MLS, RESO Web API, IDX, and RETS compared
These terms often appear together, but they are not interchangeable.
| Term | What it is | What it does not grant |
|---|---|---|
| MLS | A local or regional listing organization and its data systems | Automatic access outside its participant, broker, vendor, and licensing rules |
| RESO Data Dictionary | Standard field names, resources, definitions, and lookups | Listing records, an API key, or permission to use data |
| RESO Web API | A standard way to transport real estate data with HTTP and OData | One universal base URL, authentication method, price, or license |
| IDX | A policy and delivery framework for approved display of other participants' listings | Unrestricted analytics, resale, model training, redistribution, or permanent storage |
| RETS | The older Real Estate Transaction Standard transport | A current integration target for new systems; RESO describes RETS as deprecated |
The National Association of Realtors IDX policy describes IDX as a way for MLS participants to authorize limited electronic display of their listings by other participants. That is narrower than "the data is public." A listing can be visible on a broker website while storage, bulk reuse, enrichment, and downstream distribution remain controlled by the applicable rules.
RETS still appears in old documentation and vendor pages because real estate systems have long migration tails. For new work, ask whether the provider supports a certified RESO Web API and which Data Dictionary version its metadata follows. Do not build a fresh integration around RETS merely because an old code sample is easy to find.
How MLS API access actually works
Start with the market and product use, not the SDK. "We need an MLS API" is too vague for a data owner to approve.
- List the MLS territories your application needs.
- State who will use the product: an MLS participant, brokerage staff, consumers, analysts, or another group.
- Describe what the application will do with the records, including display, alerts, internal analysis, enrichment, and exports.
- Identify the agreement that covers each use. This may involve an MLS participant, sponsoring broker, vendor agreement, IDX approval, or another data license.
- Ask the MLS or its designated provider for the service root, authentication flow, test environment, metadata, refresh method, and support process.
- Map the provider's actual metadata before coding against expected fields.
The organization may operate its own API or route delivery through a platform. Authentication may use an access token, an OAuth flow, or another provider-specific mechanism. The response may be query-on-demand, a replicated feed, or both. Treat the credential instructions supplied with your agreement as the source of truth.
What does MLS API access cost?
There is no standard MLS API price. Costs can depend on the MLS, geography, participant status, application type, number of offices, display rights, refresh method, and vendor. Some providers publish fees. Others quote after reviewing the use case. Ask for the complete recurring and one-time cost, not only an API line item.
Useful commercial questions include:
- Does the price cover every required MLS or only one territory?
- Are certification, setup, compliance review, and support billed separately?
- Is usage metered by request, record, office, user, or market?
- Which environments and rate limits are included?
- What must happen to cached data when a listing changes, expires, or leaves the feed?
- May derived fields be stored after the underlying record must be removed?
A cheap endpoint with vague provenance can cost more than a licensed feed once a product depends on it. The real estate scraper guide discusses source selection across listing platforms, government records, and specialized providers.
How do you get an MLS API key?
Apply to the MLS or provider named in the relevant data agreement. RESO does not issue a key that unlocks member MLS databases. A marketplace key belongs to that marketplace and should not be presented as an official key from every MLS whose name appears in its catalog.
Before accepting a credential, get written answers for permitted fields, approved users, display requirements, retention, caching, attribution, refresh obligations, security controls, and termination. Save those decisions beside the integration configuration. Your code cannot reconstruct them later from a bearer token.
Read the data contract before the endpoint
An API schema tells you what a server can return. The data agreement tells you what your application may do next. Keep both under version control or in an auditable contract registry.
IDX rules can permit consumer display while restricting bulk downloads or unrelated analytics. Public display eligibility also does not make every field safe for every external processor. Write down the deletion behavior before implementing a cache because listing changes and license termination can require records to disappear. Derived fields need their own rule too: a normalized amenity or research tag still came from a licensed record.
The article's notebook therefore uses a conservative design. It accepts an export only after the reader confirms processing rights, sends a minimum field set, rejects restricted columns, validates every extracted statement against the source text, and keeps failed batches out of the publishable CSV. This is an engineering control, not legal advice. The broader web scraping legality guide explains why access, contracts, privacy, and downstream use need separate review.
Provider-neutral RESO Web API request flow
A provider should give you a service root and authentication instructions. Do not copy a base URL from another MLS and assume it applies to yours. The service root, token flow, available resources, and query limits come from your provider.
Start by reading the OData metadata document. It describes the entity sets and fields that this service exposes. Then request only the fields your feature needs.
import os
import requests
base_url = os.environ["MLS_WEB_API_URL"].rstrip("/")
access_token = os.environ["MLS_ACCESS_TOKEN"]
headers = {
"Authorization": f"Bearer {access_token}",
"Accept": "application/json",
}
metadata = requests.get(
f"{base_url}/$metadata",
headers=headers,
timeout=30,
)
metadata.raise_for_status()
print(metadata.text[:500])After confirming that the metadata exposes a Property entity set and the requested fields, a small query can look like this:
params = {
"$select": (
"ListingKey,ListPrice,BedroomsTotal,"
"BathroomsTotalInteger,LivingArea,PublicRemarks"
),
"$filter": "StandardStatus eq 'Active'",
"$top": "10",
}
response = requests.get(
f"{base_url}/Property",
headers=headers,
params=params,
timeout=30,
)
response.raise_for_status()
records = response.json()["value"]Use this as a request pattern after matching it to your provider's metadata. Token handling, pagination, replication, filters, headers, and entity-set paths can differ. $select is still worth using because a narrow response is cheaper to validate and easier to protect. Leave contact, security, showing, and confidential fields out when the feature does not need them.
For a production sync, follow the provider's documented incremental-update mechanism instead of repeatedly downloading every active listing. Store the source organization, retrieval time, metadata or schema version, and permitted-use context with each batch. If your pipeline combines MLS data with public websites, keep the provenance separate. The market research scraping guide shows how source-level evidence prevents unrelated datasets from collapsing into one unverifiable table.
Why a free MLS API is usually the wrong starting question
A free sandbox can help you test authentication and OData code, but it does not prove production coverage or rights. A sample dataset may be synthetic, delayed, limited to one market, or licensed only for evaluation. A third-party "free MLS API" may actually scrape consumer listing sites or return a vendor-owned dataset with different terms.
Ask these questions before integrating any free or low-cost option:
- Who supplied the underlying listing records?
- Which markets and property types are covered?
- How quickly do new, changed, and removed listings appear?
- Is the data for testing, internal analysis, consumer display, or redistribution?
- Can you inspect a representative response and the governing license before signup?
- What changes when the trial ends?
If you only need public market statistics, use an official download or government dataset instead of forcing an MLS-shaped solution. If you need live listings in a customer product, budget for licensed access. If you need data from public web pages for a separate permitted workflow, a web scraping API solves the page-extraction problem, not the MLS licensing problem.
The useful ScrapeGraphAI layer starts after access
An MLS API already returns structured prices, bedroom counts, status values, areas, and identifiers. Re-extracting those fields with an AI model would add cost and uncertainty. The useful gap is the narrative text in PublicRemarks.
The official RESO definition says PublicRemarks contains text intended for online public display, typically the selling points of a building or land. The same definition says local rules determine allowed content and generally exclude property-entry details, seller or tenant information, and listing-member contacts. RESO's PrivateRemarks definition is much simpler: those remarks may contain security or proprietary information and should be restricted from public view.
The notebook acts directly on that difference:
| Input | Notebook behavior |
|---|---|
| Existing structured MLS fields | Keep them unchanged and local |
ListingKey |
Send only to match output to the source row |
Authorized PublicRemarks |
Send as escaped text for schema extraction |
PrivateRemarks, agent notes, showing instructions, lockbox or contact columns |
Stop before the API call |
Only ListingKey and authorized PublicRemarks leave the notebook. ScrapeGraphAI converts the narrative into six research categories: condition, amenity, energy feature, HOA term, seller concession, and open-house detail. Each result includes an exact evidence substring from the same remark.
The resulting columns are useful because every normalized value keeps the sentence that supports it.
Build an evidence-backed remarks pipeline
The companion Google Colab starts with five synthetic RESO-like rows. You can run it immediately, then switch to an authorized CSV after reviewing your rights.
The result schema keeps every normalized value attached to evidence:
from typing import Literal
from pydantic import BaseModel, Field
class ListingSignal(BaseModel):
category: Literal[
"condition",
"amenity",
"energy_feature",
"hoa_term",
"seller_concession",
"open_house",
]
value: str = Field(min_length=1, max_length=160)
evidence: str = Field(min_length=1, max_length=300)
class ListingAnalysis(BaseModel):
listing_key: str = Field(min_length=1)
signals: list[ListingSignal] = Field(default_factory=list)
class ListingBatch(BaseModel):
listings: list[ListingAnalysis] = Field(default_factory=list)Five remarks are escaped and placed in separate <listing> elements. No MLS URL appears in the request. One extraction call handles the five-row sample:
with ScrapeGraphAI(api_key=api_key) as client:
response = client.extract(
PROMPT,
html=build_source(listings[["ListingKey", "PublicRemarks"]]),
schema=ListingBatch.model_json_schema(),
)
if response.status != "success":
raise RuntimeError(response.error or response.status)
analysis = ListingBatch.model_validate(response.data.json_data)Schema validation is only the first check. The notebook also requires every requested listing key exactly once. It then normalizes whitespace and verifies that each evidence value occurs in the matching source remark:
source = normalize_text(remarks[listing.listing_key])
for signal in listing.signals:
if normalize_text(signal.evidence) not in source:
raise ValueError(
f"Unsupported evidence for {listing.listing_key}: "
f"{signal.evidence!r}"
)A failed batch enters mls_quarantine.csv; it does not silently enter the analysis file. Valid signals are flattened into paired columns such as energy_feature_values and energy_feature_evidence.
Tested output
The five-row synthetic batch ran on August 3, 2026 with Python 3.12, scrapegraph-py==2.1.0, Pydantic 2.13.4, and pandas 2.3.3. One call returned all five keys. Every accepted signal passed the evidence check, and zero rows were quarantined.
| ListingKey | Signals | Selected normalized values |
|---|---|---|
SYN-001 |
4 | Fresh roof installed in 2024; owned solar panels; EV charger; $5,000 closing-cost credit |
SYN-002 |
3 | Kitchen needs updating; pool and tennis courts; Sunday open house from 1 to 3 PM |
SYN-003 |
3 | Original hardwood floors; detached workshop; heat pump replaced in 2023 |
SYN-004 |
0 | None |
SYN-005 |
3 | $145 monthly HOA dues; 2-1 rate buydown; August 9 open house |
SYN-004 deliberately contains family, school-quality, and contact language. The prompt excludes protected-characteristic proxies, school quality, subjective suitability, and contact instructions, so the row returned no signal. An empty signal list means that no allowed fact was explicitly supported. It does not mean the property lacks features.
Run the MLS API Colab
Open the notebook and follow this sequence:
- Run the pinned installation cell.
- Enter a ScrapeGraphAI key through the hidden prompt.
- Leave
USE_SYNTHETIC_SAMPLE = Truefor the first run. - Run the local safety checks. They cover missing input, restricted columns, invalid categories, unsupported evidence, and a simulated API failure.
- Run the one-call extraction and inspect the evidence columns.
- Download
mls_public_remark_signals.csv. - For your own data, set synthetic mode to false and acknowledge that you have processing rights before uploading one CSV.
The upload path requires ListingKey and PublicRemarks, limits the run to ten rows, and processes at most five listings per call. It keeps only a small allowlist of local structured fields. Any column name matching private remarks, agent remarks, showing instructions, lockbox data, email, phone, or contact data stops the notebook before extraction.
Do not publish a copy of the notebook with an uploaded file, generated output, or credential attached. Colab sharing controls access to the notebook file; they do not transfer the rights attached to MLS data.
Production checks for an MLS data pipeline
A production service must add controls around the same bounded transformation.
Use an approved service account or delegated credential process for the MLS feed. Keep MLS credentials separate from the ScrapeGraphAI key. Log the provider, data agreement version, retrieval time, entity-set metadata version, input record identifiers, extraction prompt version, and validation outcome.
Send the minimum text needed for the declared purpose. Review whether a third-party processor is allowed before enabling enrichment. Encrypt licensed data, limit operator access, and implement the provider's deletion and refresh obligations. Do not keep a derived tag after its source record must be removed unless the agreement explicitly permits that retention.
Run extraction in bounded batches with retry limits and a quarantine queue. Alert when keys disappear, evidence validation fails, null rates jump, or a provider removes a field. A model response that still passes JSON validation can change semantically, which is why exact evidence and source-level sampling remain necessary.
Do not use the remark signals for housing eligibility, protected-class inference, neighborhood profiling, steering, automated valuation, or claims about who should live in a property. Keep a human review path for any customer-facing use. These columns are research aids tied to explicit listing text.
MLS API questions developers ask
Is there one MLS database API for the United States?
No. MLS data is organized across local and regional organizations and delivery providers. RESO standardizes the technical vocabulary, but it does not combine every listing into a public national database.
Can I get a free MLS API key?
Not from RESO. An MLS or vendor may offer evaluation data, a sandbox, or a trial under its own terms. Confirm coverage and production rights before building around it.
Is the RESO Web API the same as an MLS API?
RESO Web API is a transport standard that an MLS data provider can implement. The actual MLS API is the provider's service, metadata, authentication, records, limits, and license.
Is an IDX API unrestricted MLS access?
No. IDX supports approved display use under MLS policy and local rules. Analytics, bulk export, long-term storage, enrichment, and redistribution can have different requirements.
Can ScrapeGraphAI access MLS data without a license?
No. This workflow starts after authorized data access. ScrapeGraphAI receives supplied PublicRemarks text and returns a validated schema. It does not create MLS credentials, unlock a database, or change the source license.
Can I store the extracted remark signals?
Only if the applicable agreement and processing terms permit it. Keep each signal linked to its source listing and evidence, then apply the same update and deletion rules required for the underlying record unless your agreement says otherwise.
Related Articles
- Redfin API Documentation and Python Guide (2026)
- Zillow Scraper: Extract Listing Data with One API
- The Complete Guide to Real Estate Scraper Tools in 2026
- AI Web Scraping for Market Research Dashboards
- Web Scraping API: How to Choose One in 2026
- Is Web Scraping Legal? Legality Guide and Best Practices
