Developer Documentation
Complete API reference and integration guides for SourceMLS badge verification
Contents
Getting Started
The SourceMLS API allows you to request verified badges for real estate listings and retrieve attribution information. All requests are authenticated using JWT tokens that link listings to their MLS organizations and authorized licensees.
https://www.sourcemls.org/api/v1
Quick Start
- Register for a SourceMLS account
- Configure your MLS organization settings
- Obtain your organization's secret key
- Generate a JWT token with listing and licensee information
- Make API requests using your JWT token
- Display badges and attribution on your website
Data Model Overview
Key Concepts:
- MLS Organization - Owns all listings (e.g., "Chicago REALTORĀ® Association")
- Listing - A property listing identified by MLS number
- Licensee - A company/brokerage authorized to use the organization's listings
- Badge - A visual verification element with attribution information
Authentication
All API requests require a valid JWT token. Tokens should be generated using your organization's secret key and must include required claims using RESO Data Dictionary standard field names.
Required JWT Claims (RESO Format)
{
"SourceSystemID": "your-org-code",
"LicenseeID": "licensee-key-from-mls",
"LicenseeName": "ABC Realty Group",
"ListingID": "MLS-2026-12345",
"StandardStatus": "Active",
"ModificationTimestamp": "2026-02-01T17:30:00Z"
}
Claim Descriptions
| Claim | Type | Description |
|---|---|---|
SourceSystemID |
String | Your MLS organization code (provided during setup) |
LicenseeID |
String | Unique identifier for the licensee/brokerage in your MLS system |
LicenseeName |
String | Display name for the licensee (e.g., company name) |
ListingID |
String | Unique listing identifier (MLS number) in your system |
StandardStatus |
String | Current listing status (Active, Pending, Sold, etc.) |
ModificationTimestamp |
ISO 8601 | When the listing was last modified |
LicenseeID or ListingID doesn't exist in
SourceMLS, it will be automatically created on first badge request. This enables zero-friction integration.
exp (expiration) claim. Include
StandardStatus and ModificationTimestamp to enable staleness detection.
API Endpoints
/api/v1/:token/badge.:format
supporting multiple formats (.png, .html, .js, .json) via URL extension or Accept header.
Retrieve badge in various formats for different integration methods.
Parameters:
token- The JWT token containing listing and licensee informationformat- Optional format extension:.png,.html,.js,.json
Format Options:
/api/v1/:token/badge.png
Returns a static badge image (12KB SourceMLS branded PNG)
- Content-Type:
image/png - Cache: 1 hour, public
- Use case: Simple image embed with
<img>tag
<img src="https://www.sourcemls.org/api/v1/YOUR_JWT_TOKEN/badge.png" alt="SourceMLS Verified">
/api/v1/:token/badge.html
Returns complete badge HTML markup with styling
- Content-Type:
text/html - Cache: 24 hours, public
- Use case: Embed in iframe or fetch and insert
<iframe src="https://www.sourcemls.org/api/v1/YOUR_JWT_TOKEN/badge.html" width="300" height="200"></iframe>
/api/v1/:token/badge.js
Returns self-executing JavaScript that dynamically inserts the badge
- Content-Type:
text/javascript - Cache: 24 hours, public
- Exposes data via
window.SourceMLSBadge.data - Use case: Simple script tag embed
<script src="https://www.sourcemls.org/api/v1/YOUR_JWT_TOKEN/badge.js"></script>
/api/v1/:token/badge.json
Returns badge data and metadata in JSON format
- Content-Type:
application/json - Use case: Build custom badge UI with API data
JSON Response Example:
{
"data": {
"badge_html": "<div class=\"sourcemls-badge\">...</div>",
"attribution": "John Doe Real Estate",
"listing_address": "123 Main St, Springfield, IL 62701",
"mls_organization": "Demo MLS Board"
},
"meta": {
"version": "v1",
"timestamp": "2025-11-19T17:30:00Z",
"warnings": []
}
}
Response Fields:
data.badge_html- Ready-to-use HTML for displaying the badgedata.attribution- Licensee display name for attributiondata.listing_address- Full address of the listingdata.mls_organization- Name of the MLS organizationmeta.version- API version (currently "v1")meta.timestamp- Response generation timestampmeta.warnings- Array of warning codes (see below)
Warning Codes:
The warnings array may include the following codes when JWT data doesn't match current listing state:
status_changed- Listing status has changed since token was createdlisting_modified- Listing has been modified since token timestamplisting_inactive- Listing is no longer active in the system
Log a badge request for analytics tracking.
Parameters:
token- The JWT token
Automatic Data Captured:
- IP address
- User agent
- Referrer URL
- Timestamp
Response:
{
"success": true,
"badge_html": "<div class=\"sourcemls-badge\">...</div>",
"request_id": "request-uuid"
}
This endpoint is useful for tracking badge impressions and clicks for analytics purposes.
Retrieve attribution information as formatted HTML.
Parameters:
token- The JWT token
Response:
Returns formatted HTML containing:
- Listing information (MLS#, address, price)
- Licensee company name
- MLS organization attribution template
- Disclaimer text (if configured)
Content-Type: text/html
Retrieve complete attribution information as JSON.
Parameters:
token- The JWT token
Response:
{
"listing": {
"mls_number": "ML123456",
"address": "123 Main St, Springfield, IL 62701",
"price": "$250,000",
"bedrooms": 3,
"bathrooms": 2.5,
"square_feet": 1800
},
"organization": {
"name": "Demo MLS Board",
"code": "DEMO_MLS",
"attribution_template": "Listing courtesy of {{licensee_name}}",
"disclaimer_text": "Information deemed reliable but not guaranteed"
},
"licensee": {
"name": "Premium Realty Group"
},
"disclaimer": "Information deemed reliable but not guaranteed",
"attribution_html": "<div class=\"sourcemls-attribution\">...</div>",
"timestamp": "2025-11-12T10:30:00Z"
}
Badge Display
Display SourceMLS badges on your website using multiple integration methods. Choose the format that best fits your implementation.
Method 1: PNG Image (Simplest)
Embed the badge as a simple image - works everywhere, no JavaScript required.
<!-- Direct image embed -->
<img src="https://www.sourcemls.org/api/v1/YOUR_JWT_TOKEN/badge.png"
alt="SourceMLS Verified"
width="200">
Method 2: JavaScript Embed (Dynamic)
Embed via script tag - automatically inserts badge HTML and exposes listing data.
<!-- JavaScript embed - simplest dynamic option -->
<script src="https://www.sourcemls.org/api/v1/YOUR_JWT_TOKEN/badge.js"></script>
<!-- Access listing data after script loads -->
<script>
// Data is available at window.SourceMLSBadge.data
console.log(window.SourceMLSBadge.data);
</script>
Method 3: HTML Iframe
Embed complete styled badge HTML in an iframe.
<!-- Iframe embed -->
<iframe src="https://www.sourcemls.org/api/v1/YOUR_JWT_TOKEN/badge.html"
width="300"
height="200"
frameborder="0"></iframe>
Method 4: JSON API (Custom)
Fetch JSON data and build your own custom badge UI.
<!-- Fetch badge data and display -->
<div id="sourcemls-badge"></div>
<script>
fetch('https://www.sourcemls.org/api/v1/YOUR_JWT_TOKEN/badge.json')
.then(response => response.json())
.then(data => {
// Use the pre-rendered HTML
document.getElementById('sourcemls-badge').innerHTML = data.data.badge_html;
// Or build custom UI with the data
console.log('Listing:', data.data.listing_address);
console.log('Organization:', data.data.mls_organization);
console.log('Attribution:', data.data.attribution);
// Check for warnings
if (data.meta.warnings.length > 0) {
console.warn('Badge warnings:', data.meta.warnings);
}
});
</script>
Method 5: Fetch HTML Directly
Fetch the HTML format and insert it directly.
// Fetch badge HTML and display
fetch('https://www.sourcemls.org/api/v1/YOUR_JWT_TOKEN/badge.html')
.then(response => response.text())
.then(html => {
document.getElementById('badge').innerHTML = html;
})
.catch(error => {
console.error('Error loading badge:', error);
});
Attribution Display
Display attribution information when users interact with badges.
Fetch Attribution (HTML)
fetch('https://www.sourcemls.org/api/v1/YOUR_JWT_TOKEN/attribution')
.then(response => response.text())
.then(html => {
document.getElementById('attribution').innerHTML = html;
});
Fetch Attribution (JSON)
fetch('https://www.sourcemls.org/api/v1/YOUR_JWT_TOKEN/attribution_json')
.then(response => response.json())
.then(data => {
// Access structured attribution data
console.log('MLS Organization:', data.organization.name);
console.log('Licensee:', data.licensee.name);
console.log('Listing:', data.listing.address);
console.log('Disclaimer:', data.disclaimer);
// Or use the pre-formatted HTML
document.getElementById('attribution').innerHTML = data.attribution_html;
});
Code Examples
Ruby (Rails)
require 'jwt'
# Generate JWT token with RESO field names
payload = {
SourceSystemID: 'your-org-code', # Your MLS organization code
LicenseeID: 'LIC-12345', # Licensee key from your MLS system
LicenseeName: 'ABC Realty Group', # Licensee display name
ListingID: 'MLS-2026-12345', # Listing identifier (MLS number)
StandardStatus: 'Active', # Current listing status
ModificationTimestamp: Time.current.iso8601
}
token = JWT.encode(payload, ENV['MLS_SECRET_KEY'], 'HS256')
# Use in badge URLs - multiple format options
badge_png_url = "https://www.sourcemls.org/api/v1/#{token}/badge.png"
badge_html_url = "https://www.sourcemls.org/api/v1/#{token}/badge.html"
badge_js_url = "https://www.sourcemls.org/api/v1/#{token}/badge.js"
badge_json_url = "https://www.sourcemls.org/api/v1/#{token}/badge.json"
# Make API request (JSON format)
require 'net/http'
uri = URI(badge_json_url)
response = Net::HTTP.get_response(uri)
badge_data = JSON.parse(response.body)
# Access badge data
puts badge_data['data']['listing_address']
puts badge_data['data']['attribution']
JavaScript (Node.js)
const jwt = require('jsonwebtoken');
// Generate JWT token with RESO field names
const payload = {
SourceSystemID: 'your-org-code', // Your MLS organization code
LicenseeID: 'LIC-12345', // Licensee key from your MLS system
LicenseeName: 'ABC Realty Group', // Licensee display name
ListingID: 'MLS-2026-12345', // Listing identifier (MLS number)
StandardStatus: 'Active', // Current listing status
ModificationTimestamp: new Date().toISOString()
};
const token = jwt.sign(payload, process.env.MLS_SECRET_KEY, { algorithm: 'HS256' });
// Use in badge URLs - multiple format options
const badgeUrls = {
png: `https://www.sourcemls.org/api/v1/${token}/badge.png`,
html: `https://www.sourcemls.org/api/v1/${token}/badge.html`,
js: `https://www.sourcemls.org/api/v1/${token}/badge.js`,
json: `https://www.sourcemls.org/api/v1/${token}/badge.json`
};
// Make API request (JSON format)
fetch(badgeUrls.json)
.then(res => res.json())
.then(data => {
console.log('Listing:', data.data.listing_address);
console.log('Attribution:', data.data.attribution);
});
PHP
<?php
require_once 'vendor/autoload.php';
use \Firebase\JWT\JWT;
// Generate JWT token with RESO field names
$payload = array(
"SourceSystemID" => "your-org-code", // Your MLS organization code
"LicenseeID" => "LIC-12345", // Licensee key from your MLS system
"LicenseeName" => "ABC Realty Group", // Licensee display name
"ListingID" => "MLS-2026-12345", // Listing identifier (MLS number)
"StandardStatus" => "Active", // Current listing status
"ModificationTimestamp" => date('c')
);
$token = JWT::encode($payload, $_ENV['MLS_SECRET_KEY'], 'HS256');
// Use in badge URLs - multiple format options
$badgePngUrl = "https://www.sourcemls.org/api/v1/{$token}/badge.png";
$badgeHtmlUrl = "https://www.sourcemls.org/api/v1/{$token}/badge.html";
$badgeJsUrl = "https://www.sourcemls.org/api/v1/{$token}/badge.js";
$badgeJsonUrl = "https://www.sourcemls.org/api/v1/{$token}/badge.json";
// Make API request (JSON format)
$response = file_get_contents($badgeJsonUrl);
$badgeData = json_decode($response, true);
// Access badge data
echo $badgeData['data']['listing_address'];
echo $badgeData['data']['attribution'];
?>
Best Practices
Security
- Always generate JWT tokens server-side, never in client-side JavaScript
- Store your organization secret key in environment variables, not in code
- Use HTTPS for all API requests
- Include
standard_statusandmodification_timestampin tokens for staleness detection - Tokens do not expire - they're validated against current listing state
Performance
- Use PNG format (
.png) for simplest, fastest display - Use JS format (
.js) for dynamic embed with minimal code - Use HTML format (
.html) for styled iframe embeds - Use JSON format (
.json) for custom implementations - Badge responses are cached (PNG: 1hr, HTML/JS: 24hrs)
- Consider loading badges asynchronously to avoid blocking page renders
- Implement error handling and fallback UI for failed requests
Analytics
- Use the
POST /api/v1/:token/badge/requestendpoint to track impressions and clicks - Include referrer information for accurate traffic attribution
- Monitor your organization's badge usage through the dashboard
- Set up alerts for unusual traffic patterns or quota limits
JWT Checker Tool
Use our online JWT checker to validate and decode your tokens during development.
Open JWT Checker ToolError Handling
The API returns standard HTTP status codes and JSON error responses.
| Status Code | Meaning |
|---|---|
200 |
Success |
400 |
Bad Request - Invalid parameters |
401 |
Unauthorized - Invalid or malformed JWT |
404 |
Not Found - Resource doesn't exist |
429 |
Too Many Requests - Rate limit exceeded |
500 |
Internal Server Error |
Error Response Format
{
"error": {
"message": "Invalid JWT token",
"code": "INVALID_TOKEN",
"details": "Token signature verification failed"
}
}
Rate Limiting
API requests are rate limited per organization to ensure fair usage and system stability.
- 1,000 requests per hour per organization
- 10,000 requests per day per organization
Contact your account administrator to adjust rate limits for your organization.