REHL API
The REHL API lets your brokerage read its leads and listings programmatically, each lead with its live REHL AI temperature score. It also exposes public endpoints for listing search, neighborhood guides, home valuations, and pre-qualification, so you can build search and data experiences on top of REHL.
Introduction
The API is organized into two sections. The Developer API is authenticated with an API key and returns your brokerage's own data: your leads and your listings, and nothing from any other brokerage. The Public data section needs no key and serves the same active listing, guide, valuation, and review data that powers REHL consumer sites.
All requests are made over HTTPS. Responses are JSON unless noted (listing photos are served as image URLs). Timestamps are UTC in ISO 8601. Money is represented in integer cents.
Base URL
https://api.rehl.usThis reference is served at docs.rehl.us; the API itself is served at api.rehl.us, the base URL above. Point your requests at api.rehl.us.
Authentication
Developer API requests are authenticated with a secret API key. Send it as a bearer token in the Authorization header. The X-API-Key header is also accepted. Keys look like rehl_sk_....
# Bearer token (preferred)
curl "https://api.rehl.us/api/v1/leads" \
-H "Authorization: Bearer rehl_sk_EXAMPLEkeyDONOTUSE0000000000000000000"
# Or the X-API-Key header
curl "https://api.rehl.us/api/v1/leads" \
-H "X-API-Key: rehl_sk_EXAMPLEkeyDONOTUSE0000000000000000000"Get your key
A brokerage admin creates an API key in the REHL app under Settings → API keys. API access is a feature of the Power plan (and any plan that includes API access). The full key is shown once, at creation, and only the key holder ever sees it; REHL stores only a hash, so a lost key cannot be recovered and must be replaced with a new one.
A key stops working immediately if it is revoked, or if the brokerage's plan loses API access. Keep keys server-side and never embed them in a browser or mobile app.
Rate limits
All requests are rate limited, both the authenticated Developer API and the public endpoints. When you exceed the limit the API responds with 429 Too Many Requests and a Retry-After header telling you how many seconds to wait. Back off and retry after that interval. Paginate with limit and offset rather than requesting large pages.
Errors
Errors use standard HTTP status codes and a small JSON body with an error code and a human-readable message.
| Status | Meaning |
|---|---|
| 401 | The API key is missing, invalid, revoked, or expired. The response is deliberately uniform and reveals no detail about which of these it was. |
| 403 | The brokerage's plan does not include API access. Upgrade to a plan with API access to use the Developer API. |
| 429 | Rate limited. Wait the number of seconds in the Retry-After header before retrying. |
{
"error": "invalid_api_key",
"message": "Invalid or missing API key."
}Developer API
Authenticated with your API key. Returns your brokerage's own data only.
Leads
Read your brokerage's own leads, newest first, with their live REHL AI temperature score and pipeline stage. Scoped to your brokerage by the API key, you only ever see your own leads.
/api/v1/leadsAPI keyList your brokerage's leads, newest first.
Query parameters
| Field | Type | Description |
|---|---|---|
| limit | integer | Page size. 1 to 100. Defaults to a small page. |
| offset | integer | Rows to skip, for pagination. Defaults to 0. |
| band | string | Filter by temperature band: cold, warm, hot, or very_hot. Any other value returns an empty page rather than an error. |
Response fields
| Field | Type | Description |
|---|---|---|
| id | uuid | The lead's stable identifier. |
| string | null | Lead email, when known. | |
| phone | string | null | Lead phone, when known. |
| temperature_score | integer | null | 0 to 100 REHL AI temperature, or null when unscored. |
| temperature_band | string | null | cold, warm, hot, or very_hot, or null when unscored. |
| pipeline_stage | string | Current pipeline stage. |
| acquisition_source | string | null | How the lead entered, e.g. consumer_site, organic_search, referral. |
| created_at | timestamp | When the lead was created (UTC, ISO 8601). |
| updated_at | timestamp | When the lead was last updated (UTC, ISO 8601). |
This endpoint returns identity basics, temperature, pipeline stage, and timestamps only. It does NOT return score internals (score_factors, model version), enrichment blobs, or any cross-surface identifier.
curl "https://api.rehl.us/api/v1/leads?limit=25&band=hot" \
-H "Authorization: Bearer rehl_sk_EXAMPLEkeyDONOTUSE0000000000000000000"{
"results": [
{
"id": "00000000-0000-0000-0000-000000000001",
"email": "buyer@example.com",
"phone": "+15555550142",
"temperature_score": 72,
"temperature_band": "hot",
"pipeline_stage": "engaged",
"acquisition_source": "consumer_site",
"created_at": "2026-07-01T14:05:00Z",
"updated_at": "2026-07-12T09:22:00Z"
}
],
"page": { "limit": 25, "offset": 0, "total": 1 }
}Listings
Read your brokerage's own listings, newest first: address, core facts, and status. Scoped to your brokerage by the API key.
/api/v1/listingsAPI keyList your brokerage's own listings, newest first.
Query parameters
| Field | Type | Description |
|---|---|---|
| limit | integer | Page size. 1 to 100. Defaults to a small page. |
| offset | integer | Rows to skip, for pagination. Defaults to 0. |
| status | string | Filter by status: active, pending, sold, expired, withdrawn, or unknown. |
Response fields
| Field | Type | Description |
|---|---|---|
| id | uuid | The listing's stable identifier. |
| listing_key | string | The MLS listing key. |
| address_line1 | string | null | Street address. |
| city / state / postal_code | string | null | Location fields. |
| list_price_cents | integer | null | List price in cents. |
| beds | integer | null | Bedroom count. |
| baths_full / baths_half | integer | null | Full and half bath counts. |
| sqft | integer | null | Living area, square feet. |
| year_built | integer | null | Year the home was built. |
| property_type | string | null | Property type. |
| status | string | Listing status. |
| listed_at | timestamp | null | When it went on market. |
Address and core facts only. Enrichment blobs and internal columns are never returned on this endpoint.
curl "https://api.rehl.us/api/v1/listings?limit=25&status=active" \
-H "X-API-Key: rehl_sk_EXAMPLEkeyDONOTUSE0000000000000000000"{
"results": [
{
"id": "00000000-0000-0000-0000-000000000002",
"listing_key": "MFR-EXAMPLE-1001",
"address_line1": "100 Flagler Ave",
"address_line2": null,
"city": "New Smyrna Beach",
"state": "FL",
"postal_code": "32169",
"list_price_cents": 74900000,
"beds": 3,
"baths_full": 2,
"baths_half": 1,
"sqft": 2140,
"year_built": 2019,
"property_type": "single_family",
"status": "active",
"listed_at": "2026-06-20T13:00:00Z",
"created_at": "2026-06-20T13:02:00Z",
"updated_at": "2026-07-11T18:40:00Z"
}
],
"page": { "limit": 25, "offset": 0, "total": 1 }
}Public data
No key required. The same active listing, guide, valuation, and review data that powers REHL consumer sites.
Listings
Anonymous, read-only access to active listing data. No key required. These power search and listing-detail experiences.
/listingsNo authSearch active listings by location, price, and home facts.
Query parameters
| Field | Type | Description |
|---|---|---|
| location | string | Free-text city, ZIP, or street. A 5-digit value matches ZIP exactly; otherwise it matches city or street. |
| city | string | Exact city name. |
| postal_code | string | Exact 5-digit ZIP. |
| min_price_cents | integer | Minimum list price, in cents. |
| max_price_cents | integer | Maximum list price, in cents. |
| min_beds | integer | Minimum bedrooms (0 to 20). |
| min_baths | integer | Minimum full baths (0 to 20). |
| property_type | string | Filter by property type. |
| avoid_sfha | boolean | When true, exclude listings in FEMA Special Flood Hazard Areas. |
| near_lat | number | Latitude to sort proximity by (paired with near_lng). |
| near_lng | number | Longitude to sort proximity by (paired with near_lat). |
| limit | integer | Page size. Defaults to 24. |
| offset | integer | Rows to skip, for pagination. |
Each result carries the full listing shape; enrichment fields (walk_score, flood_zone, school and census data, climate risk) are populated when available and null otherwise. Photos come back as {url, primary} objects. On the search endpoint, enrichment_updated_at is null and price_history is empty, use the by-id endpoint for those.
curl "https://api.rehl.us/listings?location=32169&min_beds=3&max_price_cents=90000000&limit=2"{
"results": [
{
"id": "00000000-0000-0000-0000-000000000002",
"listing_key": "MFR-EXAMPLE-1001",
"address_line1": "100 Flagler Ave",
"city": "New Smyrna Beach",
"state": "FL",
"postal_code": "32169",
"latitude": 29.0258,
"longitude": -80.9270,
"list_price_cents": 74900000,
"beds": 3,
"baths_full": 2,
"baths_half": 1,
"sqft": 2140,
"lot_sqft": 6500,
"year_built": 2019,
"property_type": "single_family",
"status": "active",
"description": "Coastal three-bedroom near Flagler Avenue...",
"photos": [
{ "url": "https://media.example.com/mfr-example-1001/0.jpg", "primary": true }
],
"listed_at": "2026-06-20T13:00:00Z",
"school_district_data": null,
"walk_score": 71,
"transit_score": null,
"bike_score": null,
"flood_zone": "X",
"climate_risk": null,
"list_office_name": "Barsottelli Prime Real Estate",
"list_agent_name": null,
"updated_at_mls": "2026-07-11T18:40:00Z",
"census_data": null,
"amenities": null,
"enrichment_updated_at": null,
"price_history": []
}
],
"total": 1,
"limit": 2,
"offset": 0
}/listings/suggestNo authLocation typeahead over places that actually have active homes.
Query parameters
| Field | Type | Description |
|---|---|---|
| qrequired | string | Typeahead prefix, 2 to 80 characters. Digits match ZIPs; text matches cities and streets. |
| limit | integer | Max suggestions. Defaults to 8. |
Each suggestion has a kind (zip, city, or address), a display label, a value to feed back into the location search param, and count, the number of active listings backing it.
curl "https://api.rehl.us/listings/suggest?q=new%20smyrna&limit=5"{
"query": "new smyrna",
"suggestions": [
{ "kind": "city", "label": "New Smyrna Beach, FL", "value": "New Smyrna Beach", "count": 42 },
{ "kind": "zip", "label": "32169", "value": "32169", "count": 18 }
]
}/listings/by-slug/{subdomain}/{slug}No authFetch a single listing by broker subdomain and URL slug.
Path parameters
| Field | Type | Description |
|---|---|---|
| subdomainrequired | string | The broker subdomain the listing is displayed on. |
| slugrequired | string | The listing's URL slug. |
This endpoint returns a flatter shape than the by-id endpoint: baths is the full bathroom count only (half baths are not included), the MLS number is mls_number, and the brokerage's legal name plus the Article 19 attribution string are included for public display. Only published listings resolve here.
curl "https://api.rehl.us/listings/by-slug/barsottelliprime/100-flagler-ave-new-smyrna-beach-fl-32169"{
"id": "00000000-0000-0000-0000-000000000002",
"mls_number": "MFR-EXAMPLE-1001",
"address_line1": "100 Flagler Ave",
"city": "New Smyrna Beach",
"state": "FL",
"postal_code": "32169",
"list_price_cents": 74900000,
"beds": 3,
"baths": 2,
"sqft": 2140,
"year_built": 2019,
"property_type": "single_family",
"status": "active",
"description": "Coastal three-bedroom near Flagler Avenue...",
"photos": [
{ "url": "https://media.example.com/mfr-example-1001/0.jpg", "primary": true }
],
"public_slug": "100-flagler-ave-new-smyrna-beach-fl-32169",
"source": "mls",
"brokerage": {
"name": "Barsottelli Prime Real Estate",
"subdomain": "barsottelliprime",
"state": "FL"
},
"attribution": "Listing courtesy of Barsottelli Prime Real Estate. Information deemed reliable but not guaranteed. Property information is provided by Stellar MLS, the local IDX. Listings are the property of the listing office and are protected by copyright."
}/listings/{listing_id}No authFetch a single listing by its identifier, with full detail.
Path parameters
| Field | Type | Description |
|---|---|---|
| listing_idrequired | uuid | The listing's stable identifier. |
curl "https://api.rehl.us/listings/00000000-0000-0000-0000-000000000002"{
"id": "00000000-0000-0000-0000-000000000002",
"listing_key": "MFR-EXAMPLE-1001",
"address_line1": "100 Flagler Ave",
"city": "New Smyrna Beach",
"state": "FL",
"postal_code": "32169",
"latitude": 29.0258,
"longitude": -80.9270,
"list_price_cents": 74900000,
"beds": 3,
"baths_full": 2,
"baths_half": 1,
"sqft": 2140,
"lot_sqft": 6500,
"year_built": 2019,
"property_type": "single_family",
"status": "active",
"description": "Coastal three-bedroom near Flagler Avenue...",
"photos": [
{ "url": "https://media.example.com/mfr-example-1001/0.jpg", "primary": true },
{ "url": "https://media.example.com/mfr-example-1001/1.jpg", "primary": false }
],
"listed_at": "2026-06-20T13:00:00Z",
"walk_score": 71,
"transit_score": null,
"bike_score": null,
"flood_zone": "X",
"climate_risk": null,
"list_office_name": "Barsottelli Prime Real Estate",
"list_agent_name": null,
"updated_at_mls": "2026-07-11T18:40:00Z",
"census_data": null,
"amenities": null,
"enrichment_updated_at": "2026-07-10T02:00:00Z",
"price_history": []
}Neighborhood guides
Published neighborhood guides: editorial + local stats. No key required.
/neighborhood-guidesNo authList all published neighborhood guides.
curl "https://api.rehl.us/neighborhood-guides"{
"results": [
{
"slug": "new-smyrna-beach-fl",
"name": "New Smyrna Beach",
"city": "New Smyrna Beach",
"search_city": "New Smyrna Beach",
"state": "FL",
"county": "Volusia",
"content": { "tagline": "Coastal living on Florida's east coast", "highlights": ["Walkable Flagler Ave", "Surf breaks"] }
}
],
"total": 1
}/neighborhood-guides/{slug}No authFetch a single published guide by slug.
Path parameters
| Field | Type | Description |
|---|---|---|
| slugrequired | string | Guide slug, lowercase letters, digits, and hyphens. |
curl "https://api.rehl.us/neighborhood-guides/new-smyrna-beach-fl"{
"slug": "new-smyrna-beach-fl",
"name": "New Smyrna Beach",
"city": "New Smyrna Beach",
"search_city": "New Smyrna Beach",
"state": "FL",
"county": "Volusia",
"content": {
"tagline": "Coastal living on Florida's east coast",
"meta_description": "A guide to living in New Smyrna Beach, FL.",
"description": ["..."],
"highlights": ["Walkable Flagler Ave", "Surf breaks"],
"faqs": [{ "q": "Is it walkable?", "a": "The Flagler Ave district is." }]
}
}Valuations
TrueValue returns an honest, range-based home value estimate from local active-listing prices. When there is not enough local inventory to say anything responsible, the range fields are null.
/valuations/estimateNo authEstimate a home's value range (TrueValue).
Query parameters
| Field | Type | Description |
|---|---|---|
| ziprequired | string | 5-digit ZIP of the home. |
| sqft | integer | Living area, square feet (100 to 100000). |
| beds | integer | Bedroom count (0 to 20). |
| baths | integer | Bath count (0 to 20). |
| property_type | string | Property type. |
| subdomain | string | Broker subdomain whose licensed feed the comps come from. Without it the estimate returns insufficient_data (no range). |
A broker subdomain is required to return a range: comps come from exactly one brokerage's licensed feed (an IDX rule). Without a resolvable subdomain the range fields are null and basis is insufficient_data.
basis is one of zip_active_listings, city_active_listings, or insufficient_data. confidence is one of high, moderate, or low.
curl "https://api.rehl.us/valuations/estimate?zip=32169&sqft=2100&beds=3&subdomain=barsottelliprime"{
"zip": "32169",
"city": "New Smyrna Beach",
"estimate_low_cents": 68000000,
"estimate_mid_cents": 74500000,
"estimate_high_cents": 81000000,
"median_price_per_sqft_cents": 35500,
"comp_count": 12,
"sqft_used": 2100,
"basis": "zip_active_listings",
"confidence": "moderate"
}Pre-qualification
A clearly-labeled affordability estimate for a buyer. This is a screen, not a lead: it persists nothing, creates no record, and never returns a denial. It is a browser-oriented form endpoint, not a pure server-to-server call, see the note below.
/pre-qualification/estimateNo authEstimate a buyer's affordability from self-entered numbers (browser form endpoint).
Body fields
| Field | Type | Description |
|---|---|---|
| context.annual_income_centsrequired | integer | Annual gross income, in cents. |
| context.monthly_debt_cents | integer | Recurring monthly debt payments, in cents. Defaults to 0. |
| context.down_payment_cents | integer | Down payment, in cents. Defaults to 0. |
| context.target_city | string | Optional target city. |
| context.target_state | string | Optional 2-letter target state. |
| extra.turnstile_token | string | Bot-challenge token issued to the browser form widget. Required when bot protection is enabled; without a valid token the request is rejected with 400. This is why the endpoint is browser-oriented, not server-to-server. |
This is a browser-oriented form endpoint, not a pure server-to-server call. It is rate limited and, when bot protection is enabled, it requires a challenge token obtained in the browser (extra.turnstile_token). A request without a valid token is rejected with 400, so a plain server-side call with no token will fail in production. It powers the affordability screen on REHL consumer sites.
Persists nothing financial. The response is always a labeled estimate; a $0 result means no room for a mortgage payment at those numbers, never a denial.
curl -X POST "https://api.rehl.us/pre-qualification/estimate" \
-H "Content-Type: application/json" \
-d '{
"context": {
"annual_income_cents": 14000000,
"monthly_debt_cents": 45000,
"down_payment_cents": 8000000
},
"extra": {
"turnstile_token": "<challenge token from the browser form widget>"
}
}'{
"status": "applied",
"max_price_cents": 62500000,
"income_band": "100k_150k",
"source": "rehl_estimate",
"confidence": "high",
"as_of": "2026-07-12",
"disclosure_version": "prequal.estimate.v1.2026-07"
}Reviews
Published reviews and the star average for a broker subdomain. Only published reviews cross this wire. An unknown subdomain returns an empty set, never a 404.
/reviews/by-subdomain/{subdomain}No authPublished reviews and star average for a broker subdomain.
Path parameters
| Field | Type | Description |
|---|---|---|
| subdomainrequired | string | The broker subdomain to read reviews for. |
curl "https://api.rehl.us/reviews/by-subdomain/barsottelliprime"{
"average": 4.87,
"count": 23,
"reviews": [
{
"id": "00000000-0000-0000-0000-000000000003",
"agent_name": "Ignacio Barsottelli",
"reviewer_name": "A. Buyer",
"rating": 5,
"body": "Guided us through a coastal purchase with total clarity.",
"published_at": "2026-06-30T16:00:00Z"
}
]
}Media
A listing's photos come back on the listing endpoints as {url, primary} objects. REHL also serves any listing photo through a stable cache URL using the pattern below. Use it directly as the src of an <img>, it is an image URL, not a JSON API call.
/media/{listing_id}/{media_ref}No authImage URL pattern for a listing photo.
Path parameters
| Field | Type | Description |
|---|---|---|
| listing_idrequired | uuid | The listing the photo belongs to. |
| media_refrequired | string | The photo's stable MLS media key, or a 0-based index into the listing's display-ordered photo list. |
media_ref is either the photo's stable MLS media key or a 0-based positional index into the listing's photo list.
<img
src="https://api.rehl.us/media/00000000-0000-0000-0000-000000000002/0"
alt="100 Flagler Ave" />