CORS & Embedding on your site
There are two distinct things you might do from your website, and they have different rules:
- Embed listing photos — just
<img src>. No CORS, no API key. ✅ - Fetch the listings JSON from the browser — needs an allowed origin and your API key. CORS applies. ⚠️
Embedding images (no CORS needed)
The images array on each listing holds public CDN URLs (https://commercialunits.com/images/...). Browsers load <img> (and CSS background-image) cross-origin freely, so you can embed them directly:
<img
src="https://commercialunits.com/images/listings/8b1f3c2a/cover.jpg"
alt="Corner retail unit, Suntec City"
loading="lazy"
/>
You do not need to register an origin or send an API key to display an image.
Exception —
<canvas>/ pixel access. If you draw a listing image to a<canvas>and then read it back (getImageData,toDataURL), the browser enforces CORS on the image. For that, fetch the image withcrossorigin="anonymous"(the CDN returnsAccess-Control-Allow-Origin: *for image assets) or fetch it via the CORS-enabled JSON path. For ordinary display, plain<img src>is all you need.
Fetching the JSON from a browser (CORS applies)
When your page calls the API directly from the user's browser (a client-side fetch), the browser sends an Origin header and enforces CORS. The API will only return the cross-origin response if your site's origin is on your allow-list.
1. Register your origin(s)
- Dashboard → Settings → Developer.
- Under Allowed origins, add each origin your site is served from. An origin is
scheme://host[:port]with no path:
`` https://www.yourcompany.com https://yourcompany.com https://staging.yourcompany.com ``
- Save. Changes take effect within a minute or two.
For requests from an allowed origin, the API responds with:
Access-Control-Allow-Origin: https://www.yourcompany.com
Vary: Origin
Access-Control-Allow-Methods: GET, OPTIONS
Access-Control-Allow-Headers: Authorization
The browser also sends a OPTIONS preflight for the Authorization header; the API answers it automatically for allowed origins. From a non-allowed origin the request is rejected with 403 Forbidden and no Access-Control-Allow-Origin header, so the browser blocks it.
2. Browser fetch example
<script type="module">
const API_BASE = 'https://api.commercialunits.com';
// NOTE: a live key in front-end code is visible to anyone. See the warning below.
const API_KEY = 'sg_live_3f9aK2pLqWzXyR7vT1mN8cD4';
const res = await fetch(`${API_BASE}/v1/listings?per_page=24`, {
headers: { Authorization: `Bearer ${API_KEY}` },
});
if (!res.ok) throw new Error(`API ${res.status}`);
const { items } = await res.json();
console.log(`${items.length} listings`);
</script>
Where to put the key: server-side is safer
A live API key embedded in client-side JavaScript is readable by anyone who opens your page. Because the developer API is read-only and scoped to your own already-public listings, a leaked key only lets someone read what's already on your public commercialunits.com profile — low blast radius — but it can still be rate-limited against you by a third party.
Recommended patterns, best first:
- Proxy through your own server. Your backend holds the key, calls the API, and serves the JSON (or pre-rendered HTML) to your page. The key never reaches the browser, and you don't need to register a CORS origin at all (server-to-server requests aren't subject to CORS).
- Build-time fetch. Pull the listings at build/deploy time (static-site generator, cron) and ship static JSON/HTML. The key stays in CI.
- Direct browser fetch (the pattern above) — simplest, but the key is exposed. Acceptable for this read-only, public-data API if you accept the rate-limit risk; prefer the proxy for anything you care about.
See the Quickstart for a complete page that fetches and renders listings.