For partners

Put apartment search
inside your own product

The same community search that powers sortresidential.com — one line of HTML on a website, one URL in a mobile app. Free, and built for the organizations already helping people find a place to live.

Sign in and get your key →

Takes a minute. We email you a code — no password — and hand you snippets with your key already in them.

Live preview — this is the real widget, running right now

Who this is for

If your organization helps someone reach the point of needing an apartment, this closes the last gap — without sending them off your product to do it.

Reentry & housing programs

A participant ready for a place of their own browses real listings inside the app they already trust, instead of being handed a link and a hope.

Workforce & relocation

Show housing near a new job site or campus, filtered to the right city and price band before the first conversation.

Universities & employers

Off-campus and new-hire housing pages that stay current on their own, because the listings come from the properties themselves.

Community & faith organizations

Point members at vetted communities from your own site, with any resulting inquiry going straight to that property's leasing team.

Read the docs

Sign in to see the snippets, the options table and the mobile guides. We'll send a code — no password to remember.

Use whichever we already have for you — a partner account is matched on either. Your key and your leads live on your partner dashboard.

Quick start — websites

Two lines. Paste them where you want the search to appear. (Building a mobile app? Skip to the app instructions.)

<!-- Sort apartment search -->
<div data-sort-embed data-key="emb_your_key_here"></div>
<script src="https://sortresidential.com/embed/sort-embed.js" async></script>

That's the whole integration. The widget loads the live community list, sizes itself to fit your page, and routes every inquiry to the property's leasing team.

You don't have to fill anything in by hand. Sign in and every snippet on your dashboard — website, iframe, React Native, Flutter, iOS, Android — arrives with your key already in it, next to a live count of the leads it has produced. The key is safe to publish: it credits your organization and grants access to nothing.

Scoped to one city, no filter bar

<div data-sort-embed
     data-key="emb_your_key_here"
     data-city="Atlanta"
     data-state="GA"
     data-maxrent="1500"
     data-filters="0"
     data-accent="#0F766E"></div>
<script src="https://sortresidential.com/embed/sort-embed.js" async></script>

No JavaScript at all

If your CMS won't allow a script tag, use a plain iframe. You lose only the automatic height — set one that suits your layout.

<iframe src="https://sortresidential.com/embed/find-property/?key=emb_your_key_here&city=Atlanta"
        width="100%" height="900" style="border:0"
        title="Apartment search by Sort Residential"></iframe>

Options

Every option is a data- attribute on the container — and the same name as a query parameter if you're using the plain iframe or a mobile WebView (?key=emb_your_key_here&city=Atlanta). All are optional.

AttributeValuesWhat it does
data-keyemb_…Your issued embed key — credits every lead and widget load to your organization. Get yours.
data-citycity nameRestricts the widget to one city. Visitors can't filter out of it.
data-state2-letter codeRestricts to one state, e.g. GA.
data-bedsstudio, 0–4Starting bedroom filter. 4 means 4 or more.
data-maxrentnumberHides communities whose starting rent is above this. Communities that don't publish a price still show.
data-pets1Pet-friendly communities only.
data-limitnumberCaps how many communities render — good for a sidebar or a teaser block.
data-layoutgrid, listgrid is the default. list forces one card per row for narrow columns.
data-filters0Hides the search and filter controls, leaving a fixed list.
data-contact0Hides the contact form. Visitors are sent to the full listing on Sort instead.
data-accent#RRGGBBRecolors buttons and links to match your brand.
data-heightpixelsStarting height before the widget reports its own. Only matters for the first paint.

Mobile apps

The widget is a web page, so any native app can host it in a WebView — no SDK to add, nothing to ship an update for when listings change. Point the WebView at the same URL with your partner slug on it.

https://sortresidential.com/embed/find-property/?key=emb_your_key_here

In an app the widget skips the resize handshake and simply scrolls, the way any other screen does. Register the bridge below and it will also hand you events your app can act on.

React Native

import { WebView } from 'react-native-webview';
import { Linking } from 'react-native';

<WebView
  source={{ uri: 'https://sortresidential.com/embed/find-property/?key=emb_your_key_here' }}
  onMessage={(e) => {
    const msg = JSON.parse(e.nativeEvent.data);
    // the visitor tapped through to a full listing
    if (msg.type === 'sort-embed:open') Linking.openURL(msg.url);
  }}
/>

Flutter

final controller = WebViewController()
  ..setJavaScriptMode(JavaScriptMode.unrestricted)
  ..addJavaScriptChannel('SortEmbedFlutter', onMessageReceived: (m) {
    final msg = jsonDecode(m.message);
    if (msg['type'] == 'sort-embed:open') launchUrl(Uri.parse(msg['url']));
  })
  ..loadRequest(Uri.parse(
    'https://sortresidential.com/embed/find-property/?key=emb_your_key_here'));

iOS — WKWebView

// the handler must be named "sortEmbed"
let config = WKWebViewConfiguration()
config.userContentController.add(self, name: "sortEmbed")

let webView = WKWebView(frame: view.bounds, configuration: config)
webView.load(URLRequest(url: URL(
  string: "https://sortresidential.com/embed/find-property/?key=emb_your_key_here")!))

// MARK: WKScriptMessageHandler
func userContentController(_ c: WKUserContentController,
                           didReceive message: WKScriptMessage) {
    guard let body = message.body as? [String: Any],
          body["type"] as? String == "sort-embed:open",
          let url = (body["url"] as? String).flatMap(URL.init) else { return }
    UIApplication.shared.open(url)
}

Android — WebView

webView.settings.javaScriptEnabled = true
// the interface must be named "SortEmbedAndroid"
webView.addJavascriptInterface(object {
    @JavascriptInterface
    fun postMessage(raw: String) {
        val msg = JSONObject(raw)
        if (msg.getString("type") == "sort-embed:open") {
            startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(msg.getString("url"))))
        }
    }
}, "SortEmbedAndroid")

webView.loadUrl("https://sortresidential.com/embed/find-property/?key=emb_your_key_here")

Events your host receives

Every bridge — web, React Native, Flutter, iOS, Android — receives the same four events. Ignore the ones you don't need.

EventCarriesWhen
sort-embed:openurl, property_id, property_slugThe visitor tapped through to a full listing. In an app, open it yourself — a WebView won't.
sort-embed:leadproperty_id, property_slug, property_nameAn inquiry was sent to that property's leasing team.
sort-embed:heightheightWeb only. The widget's content height changed; the loader resizes the frame for you.
sort-embed:scroll-into-viewWeb only. A listing was opened or closed and the frame should be scrolled to.
Communication is one-way by design. The widget reports what happened; it never runs anything your app or page sends back to it.

Single-page apps

If your container mounts after page load — React, Vue, Angular, or anything that renders late — call the loader yourself.

// after the container is in the DOM
window.SortEmbed.render('#housing', {
  key: 'emb_your_key_here',
  city: 'Atlanta',
  maxrent: 1500,
});

// or re-scan the page for any new [data-sort-embed] containers
window.SortEmbed.refresh();

The loader is idempotent — a container that already holds a widget is skipped, so refresh() is safe to call on every route change.

What happens to the people who use it

Privacy and security

The widget collects a name, email, optional phone, and message — only when a visitor chooses to contact a property — plus anonymous usage counts so we can report your embed's activity back to you. It asks for no location, no camera, and no login, and it sets one anonymous identifier in its own storage, never in yours.

On the web it runs in a sandboxed iframe on Sort's origin: it cannot read your page, your cookies, or your users' data, and your page cannot be reached from inside it. In a mobile WebView it can only reach Sort's own listing service, and it talks to your app through the one-way event bridge — nothing more. Nothing a visitor enters is sold or shared beyond the property they contacted — see our privacy policy and security practices.

Serving your site over HTTPS is required — browsers block a secure frame inside an insecure page.

Troubleshooting

Nothing renders

Check that the container has the bare data-sort-embed attribute and that the script URL is exact. If your site sets a Content Security Policy, it needs frame-src https://sortresidential.com and script-src https://sortresidential.com.

The frame is too short, or scrolls inside itself

That means the height messages aren't arriving — usually the plain-iframe snippet, which has no loader to receive them. Switch to the script version, or set a taller fixed height.

In my app, tapping a listing does nothing

A WebView won't follow a target="_blank" link on its own. Register the bridge for your platform above and open the URL from the sort-embed:open event — that's exactly what it's there for. Also confirm JavaScript is enabled on the WebView.

An empty list

Your filters may be narrower than the current inventory — a data-city with no Sort communities in it yet, or a data-maxrent below local pricing. Remove them one at a time to confirm.

Still stuck? Email [email protected] with the page URL and we'll look at it.

Want it on your site?

Sign in, copy your snippet, done. Building something unusual? We're happy to help you scope it.

Get your embed key →

[email protected]