Commercial Units Developer API commercialunits.com →

CORS & Embedding on your site

There are two distinct things you might do from your website, and they have different rules:

  1. Embed listing photos — just <img src>. No CORS, no API key.
  2. 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 with crossorigin="anonymous" (the CDN returns Access-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)

  1. Dashboard → Settings → Developer.
  2. 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 ``

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