Site Exposure API

One request, one point in Australia, three readings back: the wind class to AS 4055, an indicative bushfire attack level to AS 3959, and the atmospheric corrosivity category to AS 4312 with the National Construction Code environment beside it. Send coordinates or an address; the answer names the state, the lot on plan and, where the state publishes it, the council it resolved to.

Keys are issued by RapidSiteplan. There is no self-serve signup: tell us what you are building and we set the quotas with you.

AS 4055 wind classAS 3959 bushfire attack levelAS 4312 corrosivityNCC 2022 durability environmentJSON over HTTPS

What it returns

Any Australian point or address
WindThe AS 4055 site wind classification, with the gust speeds, region, terrain, shielding and topography behind it.
BushfireWhether the point is inside mapped bushfire prone land and, where it is, an indicative bushfire attack level to AS 3959 with the distance to the nearest hazard, every compass direction that carries vegetation, and one sentence on what the level asks of the building. A hazards list says whether flood, landslide, bushfire, heritage, vegetation, an easement, a sewer main or a stormwater main is mapped at the point.
CorrosivityThe AS 4312:2019 atmospheric corrosivity category, the NCC durability environment, the two shore distances it was read from, the AS/NZS 2728 sheet grade and one line of fastener and coating guidance for that category.
The site itselfThe coordinates the reading was taken at, the formatted address, the state, the council and the lot on plan where one is published.

Every block carries its own standard string and the response carries one notice: indicative planning-stage readings, not an assessment or a certificate under any standard. Both are part of the contract: show them wherever you show the readings.

Keys and authentication

Issued by hand, under a written agreement. Nothing here is self-serve.

Send your key in the x-api-key header on every request (an Authorization: Bearer header carrying the same key works too). There is no OAuth step, no token exchange and no secret key in the query string.

Header

x-api-key: ex_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxx

A key is shown once, when we create it, and we keep only its fingerprint, so keep it secret and keep it server-side. Test keys begin ex_test_, are capped at 25 calls a day, and mark every answer with "test": true. Live keys begin ex_live_. Browser keys begin ex_pub_: they are public by design, answer only the widget route from your recorded domains, and drive the panel described below. Tell us and we revoke a key immediately; the rest of yours keep working.

The subscription behind your keys

A monthly request quota, paid by card through Stripe and renewing until it is cancelled.

We register you, agree a monthly request quota with you and send a payment link. The link opens Stripe's payment page, which takes the card and your business details; your receipts come by email. Your keys answer at the paid quota from the moment the payment lands. Some agreements carry no quota for a set period, and v1/me then answers quota unlimited with the date it runs to.

  • The quota is softRequests over it are still answered, up to twice the quota in a calendar month (UTC), and billed as overage at the rate in your agreement. The overage is charged at the end of the month, on the card that pays the subscription.
  • You hear about it onceThe first request over the quota in a month sends one email to your contact address. Past twice the quota every key answers monthly_limit (429) until the 1st, or until we move you to a larger quota. A move takes effect at once; the new amount starts at your next renewal.
  • Test keys never billA test key keeps its 25 calls a day and its marked answers, and none of its calls count toward the quota.
  • Renewal and the endIf a renewal is not paid, or the subscription is cancelled, every key answers subscription_ended (402) from the end of the period paid. Paying the renewal, or a renewal link we send, brings every key back. To cancel or change it, write to billing@rapidsiteplan.au.

GET /api/exposure/v1/me

{
  "version": "v1",
  "object": "subscriber",
  "test": false,
  "name": "Example Patio Software",
  "monthly_quota": 100,
  "subscription": {
    "status": "active",
    "renews_at": "2026-11-21T03:12:08.000Z",
    "ends_at": null,
    "interval": "month",
    "quota": 100,
    "used_this_month": 37,
    "over_quota": 0,
    "overage_rate": "your agreed rate",
    "limit_this_month": 200,
    "words": "100 requests a month, 37 used this month. Requests over the quota are billed at your agreed rate at the end of the month, and the keys answer up to 200 requests this month."
  }
}

The subscription block says where your keys stand in plain figures: the status, the renewal date, the quota, the month's billable requests, the requests over the quota and the most the keys answer this month. A subscription by written agreement answers status by_agreement with its quota and the month's count.

Routes

Three, all GET, all JSON.

Base: /api/exposure/v1
RouteParametersWhat comes back
GET /api/exposure/v1/sitelat and lng, or address · state optionalThe wind class, the indicative bushfire attack level and the corrosivity category for one point, with the site it resolved to.
GET /api/exposure/v1/menoneWho the key belongs to, its label, whether it is a test key, the quotas on it, where the subscription stands (status, renewal date, quota and this month’s requests) and the routes it can call.
GET /api/exposure/v1/coveragenoneWhat is covered in each state, in the same words as the table below, so an integration can read it rather than hard-code it.

On /site, give either lat and lng as decimal degrees, or a single address string of 140 characters or fewer. Coordinates are read as sent; an address is resolved first and the point it resolved to comes back in the answer, so you can check it. Add state only with coordinates whose state you already know: it must agree with the point, and with an address it is ignored. Every response, success or failure, carries an x-request-id header: quote it if you ever need to ask us about one.

On a large lot, send the building site's coordinates: the wind terrain, the shielding, the bushfire distance and the corrosion distance are read at the point, so the spot where it will be built can read differently from the street address. Every answer carries point with the coordinates read and from, pin when you sent lat and lng or address when the point came from the address. In the panel, put data-lat and data-lng on the container beside the address and its address line says at the pin.

A call, and what comes back

One property in Logan City, Queensland. The shape is the contract.

Request

curl -s \
  -H "x-api-key: ex_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  "https://www.rapidsiteplan.au/api/exposure/v1/site?lat=-27.876944&lng=153.010907"

200 OK

{
  "version": "v1",
  "generated_at": "2026-09-06T04:12:55.118Z",
  "test": false,
  "site": {
    "lat": -27.876944,
    "lng": 153.010907,
    "address": null,
    "state": "QLD",
    "council": "Logan City",
    "lot_plan": "30RP836911"
  },
  "point": { "lat": -27.876944, "lng": 153.010907, "from": "pin" },
  "wind": {
    "class": "N3",
    "ultimate_ms": 50,
    "serviceability_ms": 32,
    "region": "B1",
    "region_area": "SE Queensland",
    "near_region_boundary": false,
    "terrain_category": "TC2.5",
    "shielding": "NS",
    "topography": "T1",
    "elevation_m": 62,
    "lots_per_ha": 2.4,
    "hill_height_m": 31,
    "hill_slope": 0.052,
    "slope_note": "mid third of a 31 m rise, steepest slope 1:19 through the site (E-W)",
    "topography_margin": { "line": "1:20", "alternative_topography": "T0", "alternative_class": "N3" },
    "site_speed_ms": 47.1,
    "site_pressure_kpa": 1.33,
    "equivalent_class": "N2.7",
    "critical_direction": "N",
    "site_speed_if_flat_ms": null,
    "site_speed_standard": "AS/NZS 1170.2:2021 site wind speed at 5 m, importance level 2, indicative; the AS 4055 class is the design class",
    "standard": "AS 4055 site classification from the AS/NZS 1170.2 region map, indicative"
  },
  "bushfire": {
    "prone": true,
    "status": "prone",
    "rating": "BAL-12.5",
    "range": ["BAL-LOW", "BAL-12.5"],
    "headline": "BAL-LOW to BAL-12.5 (indicative range)",
    "nearest_hazard_m": 52,
    "distance_bands_m": null,
    "directions": [{ "bearing": 90, "name": "E", "distance_m": 52.0, "mapped_m": null, "tree_line_m": 52.0, "slope_deg": 2.3, "downslope": false, "rating": "BAL-12.5", "vegetation_rows": ["forest"], "formation": null, "height_m": 18, "cover_pct": 45, "beyond_20_degrees": false }],
    "ecosystems": null,
    "structure": null,
    "estimate": null,
    "construction": "Ember protection: seal the gaps, screen the openings, and use non-combustible or bushfire-resisting material in the 400 mm band above the ground and above any deck, carport roof or awning.",
    "standard": "AS 3959",
    "method": "AS 3959 Method 1, indicative",
    "vegetation": "all seven classes judged, the worst case shown (no vegetation map)"
  },
  "corrosivity": {
    "category": "C3",
    "category_word": "medium",
    "ncc_environment": "Medium",
    "shore_distance_m": 40898,
    "coast_distance_m": 40898,
    "tidal_distance_m": 20622,
    "water": "open coast with breaking surf",
    "guidance": "Screws AS 3566.2 Class 3; connectors Z275 under cover, 304 stainless or 600 g/m² hot dip where rain reaches them; hot dip galvanising lasts 40 years or more.",
    "sheet": "Prepainted sheet to AS/NZS 2728: the standard sheet (type 4 on an AM100 substrate) sits inside its maker's 1 km distance condition here.",
    "sheet_grade": "Standard sheet",
    "sheet_materials": null,
    "standard": "AS 4312:2019 category, AS/NZS 2728 sheet and NCC 2022 durability environment, indicative"
  },
  "hazards": [{ "kind": "flood", "status": "clear" }, { "kind": "landslide", "status": "clear" }, { "kind": "bushfire", "status": "mapped" }, { "kind": "heritage", "status": "clear" }, { "kind": "vegetation", "status": "clear" }, { "kind": "easement", "status": "clear" }, { "kind": "sewer", "status": "clear", "text": "not found" }, { "kind": "stormwater", "status": "none", "text": "not found, no mapping for this council" }],
  "coverage": {
    "wind": "...",
    "bushfire": "...",
    "corrosivity": "...",
    "not_covered": []
  },
  "notice": "Indicative planning-stage readings from public mapping and elevation data at this point. Not a wind, bushfire or corrosivity assessment, certificate or determination under any standard; the certifier's site assessment governs.",
  "attribution": {
    "name": "RapidSiteplan",
    "url": "https://www.rapidsiteplan.au",
    "logo_url": "https://www.rapidsiteplan.au/mark-128.png",
    "badge_light_url": "https://www.rapidsiteplan.au/badge-light.svg",
    "badge_dark_url": "https://www.rapidsiteplan.au/badge-dark.svg",
    "text": "Powered by RapidSiteplan",
    "required": true,
    "condition": "Wherever a reading from this API is shown to a person, show the RapidSiteplan name or mark beside it with a link to https://www.rapidsiteplan.au. This block is part of every response and is not removable."
  }
}

coverage repeats, for this point, what the table below says for its state, and not_covered names anything that could not be read there. Fields are added over time; nothing shown here is removed without a new version.

The readings inside your own software

A drop-in script draws the wind, bushfire and corrosion readings in your page, for the address your form already holds.

exposure.js

Load the script, give it your browser key and the element to draw in, and pass the site address. The panel draws a small bordered card with the three readings, the coverage line for the state, the notice and Readings by RapidSiteplan with a link. When your address field changes, call update and it redraws.

Four lines in your page

<div id="exposure"></div>
<script src="https://www.rapidsiteplan.au/exposure.js"></script>
<script>RapidExposure.mount({ key: 'ex_pub_xxxxxxxxxxxxxxxx', container: '#exposure', address: form.address.value })</script>
<script>form.address.addEventListener('change', e => RapidExposure.update({ address: e.target.value }))</script>
  • The address comes firstThe readings follow the address your form holds, so capture it before the design starts. A form with no address yet shows the line to enter it, and the answer carries the address as the lot records it, so you can show it back to your customer.
  • A browser key, bound to your domainsBrowser keys begin ex_pub_. They are public by design, so they answer only from the domains we record for you; each domain covers its subdomains, and localhost works while you build. A page anywhere else gets domain_not_bound and spends nothing.
  • Quiet when something is wrongA wrong key, a domain not recorded, a quota used up or an address not found draws one plain line in the card, never a blank space in your screen.
  • Nothing kept in your pageNo cookies, no tracking and nothing stored. One request per address, sent only when the address changes and after a short pause while someone types. Each reading counts against the same quota as your server calls.

If your page sets a Content Security Policy, allow https://www.rapidsiteplan.au in script-src and in connect-src. The card draws in your page's own document with its own small style, and keeps the attribution the API asks for.

The route under the panel

GET https://www.rapidsiteplan.au/api/exposure/widget?key=ex_pub_xxxxxxxxxxxxxxxx&address=<the address your form holds>
GET https://www.rapidsiteplan.au/api/exposure/widget?key=ex_pub_xxxxxxxxxxxxxxxx&lat=-27.876944&lng=153.010907

200 OK for the Logan City point above, trimmed to what a panel shows

{
  "version": "v1",
  "address": { "asked": null, "resolved": null, "lot_plan": "30RP836911", "state": "QLD", "council": "Logan City" },
  "point": { "lat": -27.876944, "lng": 153.010907, "from": "pin" },
  "readings": [
    { "kind": "wind", "label": "Wind", "value": "N3", "line": "N3, region B1", "fact": "Terrain category 2.5, no shielding, topography T1", "as_per": "as per AS 4055" },
    { "kind": "bushfire", "label": "Bushfire", "value": "BAL-12.5", "line": "BAL-12.5, inside a mapped bushfire prone area", "fact": "Nearest classified vegetation about 52 m", "as_per": "as per AS 3959" },
    { "kind": "corrosion", "label": "Corrosion", "value": "C3", "line": "C3 medium, NCC environment Medium", "fact": "Coast about 40.9 km, tidal water about 20.6 km", "as_per": "as per AS 4312" }
  ],
  "coverage": "In Queensland: the wind class to AS 4055, the bushfire attack level to AS 3959 Method 1 inside mapped bushfire prone areas, and the corrosivity category to AS 4312.",
  "notice": "Indicative planning-stage readings from public mapping and elevation data at this point. ...",
  "attribution": { "name": "RapidSiteplan", "url": "https://www.rapidsiteplan.au", "text": "Readings by RapidSiteplan" }
}

The route takes key and address, or key with lat and lng, and answers with CORS for your recorded domains. Sent with an address, the address block carries it as you sent it and as the lot records it; sent with a point, the lot alone. Each reading carries its value, its line, one fact line and its standard; where a reading is not available its line says why in the coverage words. A refusal carries a line written for your customer beside the hint for your developers.

Attribution

Our name or mark, beside the reading, with a link back.

Every response carries an attribution block. Wherever a reading from this API is shown to a person, show the RapidSiteplan name or mark beside it with a link to www.rapidsiteplan.au. It is a condition of the key, it travels in every response, and it is not removable. The block carries the wording to use and three assets:

Assets

logo_url         https://www.rapidsiteplan.au/mark-128.png
badge_light_url  https://www.rapidsiteplan.au/badge-light.svg
badge_dark_url   https://www.rapidsiteplan.au/badge-dark.svg

The badges are drawn for a light and a dark background. Use whichever suits yours.

Coverage, state by state

Wind and corrosivity everywhere. What bushfire can say depends on what the state publishes.

Also readable at v1/coverage
StateWindBushfireCorrosivity
QLDWind class to AS 4055Bushfire attack level to AS 3959 Method 1, indicativeCorrosivity category to AS 4312
NSWWind class to AS 4055Bushfire attack level to AS 3959 Method 1, indicativeCorrosivity category to AS 4312
VICWind class to AS 4055The published floor level, with a Method 1 estimate beside itCorrosivity category to AS 4312
SAWind class to AS 4055The deemed level, with a Method 1 estimate beside itCorrosivity category to AS 4312
WAWind class to AS 4055Bushfire attack level to AS 3959 Method 1, indicativeCorrosivity category to AS 4312
TASWind class to AS 4055Bushfire attack level to AS 3959 Method 1, indicativeCorrosivity category to AS 4312
ACTWind class to AS 4055Bushfire attack level to AS 3959 Method 1, indicativeCorrosivity category to AS 4312
NTWind class to AS 4055, open terrain assumedNo bushfire prone designation existsCorrosivity category to AS 4312

The wind class is returned in every state. Where a state publishes no bushfire designation, the bushfire block says so rather than guessing.

Errors

Every failure from the API is a JSON body with an error code; match the code, not the message. A first call after idle on a large Queensland lot can take close to ten seconds, and a platform timeout answers with an HTML page instead of JSON: treat any non-JSON body as a retry.

CodeHTTPWhat it means
invalid_key401The key is missing, malformed, unknown or revoked, or a browser key was sent to a server route (or a secret key to the widget route).
subscription_ended402The subscription ended, or its renewal is unpaid. Every key answers this from the end of the period paid until it is renewed.
subscriber_inactive403The key is real but the subscriber it belongs to is paused.
domain_not_bound403Widget route: the page calling it is not on a domain recorded for the browser key. Nothing is counted.
missing_site400Neither a lat and lng pair nor an address was given.
address_required400Widget route: no address yet. Capture the site address before the design starts.
bad_coordinates422lat and lng must both be present and both be decimal degrees.
address_too_long422Keep the address to 140 characters or fewer.
outside_australia422The point resolves outside Australia, where nothing here applies.
bad_state422The optional state was not one of the covered states, or the point is not inside it.
no_parcel422No land parcel is mapped at the point: the sea, or a gap in the cadastre. No reading was formed.
address_not_found404The address could not be resolved to a point. Send coordinates instead.
unknown_endpoint404Not one of the three routes.
method_not_allowed405The API is read-only. Use GET.
slow_down429Past 120 calls in the last minute from one IP address. The per-key limits are separate; space the calls.
per_minute429Past 60 calls in the last minute on this key.
daily_limit429By written agreement, the day’s quota is used, every key counted together; a test key has 25 a day of its own. The count resets at midnight UTC.
monthly_limit429By written agreement, the calendar month’s quota is used. On a subscription, twice the quota is used; the requests over the quota are billed as overage. It resets on the 1st.
upstream_failed502A mapping service the reading depends on did not answer. The body carries retry: true; the same call usually succeeds shortly after.
internal_error500The reading could not be formed. Quote the request id if it repeats.
metering_unavailable503The call could not be counted, so it was refused rather than served unmetered.
knowledge_unavailable503The rules knowledge did not load, so no reading was formed and the call was not counted.

A 429 or a 502 is worth retrying; a 400, 401, 403, 404 or 422 is not, until the request or the key changes.

Limits

Per key
  • 60 calls a minutePer key. Past that the answer is per_minute, 429. A separate 120 a minute applies per IP address. v1/me and v1/coverage are never counted.
  • A monthly quotaSet with you in your subscription and counted over the calendar month in UTC. On a subscription it is soft: answered up to twice the quota, the part over it billed as overage, then monthly_limit, 429. By written agreement the quotas are a day and a month, and past them the answer is daily_limit or monthly_limit, 429.
  • Test keys: 25 calls a daySame readings, marked test, so an integration can be built before anything is signed.
  • What your key is allowedCall v1/me: it returns the quotas standing on your key, so nothing has to be hard-coded.

Cache what you fetch. A property’s exposure does not change between two calls a minute apart, and the quota you save is your own.

What these readings are, and what they are not

The same words the response carries, said once in full.

They are indicative, planning-stage readings at the point you send: enough to price a frame, choose a coating or flag a site before anyone visits it. They are not a wind, bushfire or corrosivity assessment, certificate or determination under AS 4055, AS 3959, AS 4312 or any other standard, and they do not replace the site assessment a state, a council or a certifier requires. The certifier’s site assessment governs.

Show the notice string from the response wherever you show the readings, and do not present a response as an assessment. The full terms are in our .

Tell us what you are building.

We register you, set the quotas with you in writing and hand you a key. A test key comes first if you want one: the same readings, marked test, 25 calls a day.