Developer Documentation

Complete API reference and integration guides for SourceMLS badge verification

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.

Base URL: https://www.sourcemls.org/api/v1

Quick Start

  1. Register for a SourceMLS account
  2. Configure your MLS organization settings
  3. Obtain your organization's secret key
  4. Generate a JWT token with listing and licensee information
  5. Make API requests using your JWT token
  6. 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
Auto-Creation: If a LicenseeID or ListingID doesn't exist in SourceMLS, it will be automatically created on first badge request. This enables zero-friction integration.
Important: Tokens no longer require an exp (expiration) claim. Include StandardStatus and ModificationTimestamp to enable staleness detection.
Security Note: Never expose your organization's secret key in client-side code. Generate tokens on your server.

API Endpoints

New Unified Endpoint: The badge API now uses /api/v1/:token/badge.:format supporting multiple formats (.png, .html, .js, .json) via URL extension or Accept header.
GET /api/v1/:token/badge.:format

Retrieve badge in various formats for different integration methods.

Parameters:
  • token - The JWT token containing listing and licensee information
  • format - Optional format extension: .png, .html, .js, .json
Format Options:
1. PNG Format /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">
2. HTML Format /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>
3. JavaScript Format /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>
4. JSON Format /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 badge
  • data.attribution - Licensee display name for attribution
  • data.listing_address - Full address of the listing
  • data.mls_organization - Name of the MLS organization
  • meta.version - API version (currently "v1")
  • meta.timestamp - Response generation timestamp
  • meta.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 created
  • listing_modified - Listing has been modified since token timestamp
  • listing_inactive - Listing is no longer active in the system
Note: Warnings indicate stale data but don't prevent the request from succeeding. The badge will still display, but you may want to regenerate the JWT with current listing data.
POST /api/v1/:token/badge/request

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.

GET /api/v1/:token/attribution

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

GET /api/v1/:token/attribution_json

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_status and modification_timestamp in 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/request endpoint 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 Tool

Error 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.

Default Limits:
  • 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.

Need Help?

Can't find what you're looking for? Our support team is here to help.

Contact Support