Dwell API
The Dwell API is a RESTful interface for accessing real-time parking availability, managing bookings, streaming occupancy data, and querying analytics across Spacer-connected venues. All requests use HTTPS; all responses are JSON.
https://api.dwell.spacer.com/v1Try any endpoint right now — no signup required — in the interactive sandbox.
Request format
For GET requests, parameters are passed as query strings. For POST, PUT, and PATCH requests, send a JSON body with Content-Type: application/json.
Response format
All responses return a top-level object field identifying the resource type. Lists include a meta object with pagination and latency information. Errors always include a code, message, and optional param field.
Authentication
Dwell uses API keys for authentication. Include your key in the Authorization header of every request.
Authorization: Bearer YOUR_API_KEY
API keys are prefixed to indicate their environment:
dw_live_— Production key. Connects to live sensors and processes real bookings.dw_test_— Test key. All responses are simulated; no real bookings are created.dw_demo_— Demo key (read-only). Used by the sandbox. Works without signup.
Rate Limits
Rate limits are applied per API key per sliding 60-second window. When exceeded, the API returns a 429 Too Many Requests with a Retry-After header.
| Plan | Requests / minute | Requests / month | WebSocket connections |
|---|---|---|---|
| Developer (free) | 60 | 1,000 | 1 |
| Startup | 600 | 100,000 | 10 |
| Enterprise | Unlimited | Unlimited | Unlimited |
Every response includes rate limit headers:
X-RateLimit-Limit: 600 X-RateLimit-Remaining: 587 X-RateLimit-Reset: 1749471843
Errors
Dwell uses standard HTTP status codes. Error responses always include a JSON body:
{
"error": {
"code": "invalid_parameter",
"message": "Parameter 'radius' must be between 50 and 10000 metres.",
"param": "radius",
"request_id": "req_8f2a93bd1e"
}
}
| Status | Code | Description |
|---|---|---|
| 400 | invalid_parameter | A required parameter is missing or invalid. |
| 401 | unauthorized | API key is missing or invalid. |
| 403 | forbidden | Your key does not have access to this resource. |
| 404 | not_found | The requested resource does not exist. |
| 409 | conflict | The space is no longer available for the requested time. |
| 429 | rate_limit_exceeded | Too many requests. See Retry-After header. |
| 500 | internal_error | Something went wrong on our end. We're automatically alerted. |
Search Spaces
Returns a ranked list of parking spaces near the given coordinates, including real-time availability from ground sensors, pricing, and amenities.
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| lat | number | required | Latitude of search centre (WGS84). |
| lng | number | required | Longitude of search centre (WGS84). |
| radius | integer | optional | Search radius in metres. Default: 500. Max: 10000. |
| type | string | optional | Filter by space type: covered, outdoor, multi-deck, underground. |
| available_only | boolean | optional | If true, only return spaces with at least 1 available bay. Default: false. |
| ev_charging | boolean | optional | Filter for spaces with EV charging. |
| limit | integer | optional | Results per page. Default: 10. Max: 100. |
| page | integer | optional | Page number. Default: 1. |
Code Example
curl -X GET "https://api.dwell.spacer.com/v1/spaces/search" \ -H "Authorization: Bearer dw_demo_abc123xyz" \ -G \ -d "lat=-33.8688" \ -d "lng=151.2093" \ -d "radius=500" \ -d "type=covered"
Example Response
{
"object": "list",
"data": [
{
"id": "sp_3f8a2b9c",
"object": "space",
"venue": "Westfield Sydney Carpark",
"address": "188 Pitt St, Sydney NSW 2000",
"location": { "lat": -33.8726, "lng": 151.2071 },
"type": "covered",
"sensor_count": 84,
"realtime": {
"available": 47,
"total": 120,
"occupancy_pct": 39.2,
"ev_available": 6,
"status": "open",
"last_updated": "2026-06-09T11:24:03Z"
},
"pricing": {
"currency": "AUD",
"hourly_rate": 8.50,
"daily_cap": 45.00,
"dynamic": { "enabled": true, "current_multiplier": 1.0 }
},
"amenities": ["covered", "24h_access", "ev_charging", "security_camera"],
"distance_m": 120
}
],
"meta": { "total": 23, "page": 1, "per_page": 10, "latency_ms": 38 }
}
Live Availability
Returns live occupancy from ground sensors plus an ML-generated hourly forecast for the next 6 hours. Sensor health is included to indicate data reliability.
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| id | string | required | The space ID (e.g. sp_3f8a2b9c). |
Example Response
{
"object": "availability",
"space_id": "sp_3f8a2b9c",
"venue": "Westfield Sydney Carpark",
"realtime": {
"available": 47,
"total": 120,
"occupancy_pct": 60.8,
"status": "open",
"status_label": "Good availability",
"last_updated": "2026-06-09T11:24:12Z"
},
"forecast": [
{ "time": "12:00", "predicted_occupancy_pct": 85.2, "confidence": 0.91 },
{ "time": "13:00", "predicted_occupancy_pct": 92.1, "confidence": 0.88 },
{ "time": "14:00", "predicted_occupancy_pct": 87.4, "confidence": 0.85 },
{ "time": "15:00", "predicted_occupancy_pct": 74.8, "confidence": 0.90 },
{ "time": "16:00", "predicted_occupancy_pct": 62.3, "confidence": 0.92 },
{ "time": "17:00", "predicted_occupancy_pct": 48.9, "confidence": 0.94 }
],
"sensor_health": { "online": 82, "total": 84, "health_pct": 97.6 }
}
Create Booking
Creates a confirmed booking. On success, returns the assigned bay number, QR code, numeric entry code, and payment receipt — all in a single response.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| space_id | string | required | ID of the target space. |
| from | datetime | required | Start time (ISO 8601 with timezone offset). |
| to | datetime | required | End time (ISO 8601 with timezone offset). |
| driver.name | string | required | Full name of the driver. |
| driver.email | string | required | Email for booking confirmation. |
| driver.vehicle_plate | string | optional | Licence plate for ANPR integration. |
| payment_method_id | string | optional | Stripe payment method ID. If omitted, uses the default payment method on the account. |
Example Response
{
"object": "booking",
"id": "bk_4c9e2a8f",
"status": "confirmed",
"space_id": "sp_3f8a2b9c",
"venue": "Westfield Sydney Carpark",
"bay": "B2-047",
"confirmation_code": "ST-8F2C-9A",
"driver": {
"name": "Alex Chen",
"email": "alex.chen@email.com",
"vehicle_plate": "ABC123"
},
"schedule": {
"from": "2026-06-10T09:00:00+10:00",
"to": "2026-06-10T17:00:00+10:00",
"duration_hours": 8
},
"pricing": {
"currency": "AUD",
"subtotal": 45.00,
"service_fee": 2.25,
"total": 47.25,
"dynamic_multiplier": 1.0,
"payment_status": "paid"
},
"access": {
"entry_code": "8842",
"app_deeplink": "spacerdwell://booking/bk_4c9e2a8f"
},
"created_at": "2026-06-09T11:24:31Z"
}
Dynamic Pricing
Returns the current effective pricing for a space, including the dynamic multiplier applied by the ML engine, plus a forward-looking schedule of expected rate changes.
{
"object": "pricing",
"space_id": "sp_3f8a2b9c",
"current": {
"currency": "AUD",
"base_hourly": 8.50,
"current_multiplier": 1.0,
"effective_hourly": 8.50,
"next_update": "2026-06-09T12:00:00+10:00"
},
"schedule": [
{ "time": "12:00-14:00", "multiplier": 1.8, "reason": "peak_demand", "effective_hourly": 15.30 },
{ "time": "14:00-16:00", "multiplier": 1.4, "reason": "elevated_demand", "effective_hourly": 11.90 },
{ "time": "18:00-20:00", "multiplier": 0.75,"reason": "off_peak", "effective_hourly": 6.38 },
{ "time": "20:00-06:00", "multiplier": 0.50, "reason": "night_rate", "effective_hourly": 4.25 }
],
"demand_signal": {
"current_level": "moderate",
"occupancy_pct": 60.8,
"trend": "stable",
"ml_confidence": 0.93
}
}
Occupancy Analytics
Query occupancy, revenue, and dwell-time data across any time range up to 2 years. Results are bucketed by day or hour depending on the period requested.
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| venue_id | string | optional | Filter to a specific space ID. Omit for account-wide data. |
| period | string | optional | Preset: last_7_days, last_30_days, last_90_days, this_year. |
| from | date | optional | Custom range start (ISO 8601 date). Overrides period. |
| to | date | optional | Custom range end (ISO 8601 date). |
| granularity | string | optional | hourly or daily. Auto-selected if omitted. |
Webhooks
Webhooks allow Dwell to push real-time events to your server the moment something changes — no polling required. Register an HTTPS endpoint and choose which events to subscribe to.
| Field | Type | Required | Description |
|---|---|---|---|
| url | string | required | Your HTTPS endpoint URL. |
| events | array | required | List of event types to subscribe to. |
Event Reference
| Event | Triggered when |
|---|---|
| booking.created | A new booking is confirmed. |
| booking.cancelled | A booking is cancelled by the driver or operator. |
| booking.modified | A booking's schedule or details are changed. |
| space.status_changed | A space transitions between open / filling_fast / full / closed. |
| occupancy.threshold | Occupancy crosses a configured percentage threshold (e.g. 80%). |
| pricing.updated | The dynamic pricing multiplier changes for a space. |
| sensor.offline | A sensor stops reporting and health drops below 90%. |
Signature Verification
Every webhook payload is signed with HMAC-SHA256 using your webhook secret. Verify signatures to confirm the request genuinely came from Dwell.
const crypto = require('crypto');
function verifyWebhook(payload, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(payload, 'utf8')
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(`sha256=${expected}`)
);
}
// In your Express handler:
app.post('/webhooks/spacerdwell', express.raw({ type: 'application/json' }), (req, res) => {
const sig = req.headers['x-dwell-signature'];
if (!verifyWebhook(req.body, sig, process.env.WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}
const event = JSON.parse(req.body);
console.log('Received event:', event.type);
res.json({ received: true });
});
WebSocket Stream
Subscribe to a real-time stream of sensor events using a persistent WebSocket connection. Events arrive as JSON within 50ms of the physical sensor update.
wss://stream.dwell.spacer.com/v1/sensors?api_key=YOUR_KEY&venue_id=sp_3f8a2b9c
Event Payload
{
"type": "sensor.update",
"timestamp": "2026-06-09T11:24:03.441Z",
"venue_id": "sp_3f8a2b9c",
"sensor_id": "WFC-B2-047",
"bay": "B2-047",
"status": "vacant",
"prev_status": "occupied",
"occupancy_snapshot": { "available": 48, "total": 120, "pct": 40.0 }
}
const ws = new WebSocket(
'wss://stream.dwell.spacer.com/v1/sensors?api_key=dw_live_••••••••&venue_id=sp_3f8a2b9c'
);
ws.onopen = () => console.log('Stream connected');
ws.onclose = () => console.log('Stream disconnected');
ws.onmessage = ({ data }) => {
const event = JSON.parse(data);
if (event.type === 'sensor.update') {
const { bay, status, occupancy_snapshot } = event;
console.log(`Bay ${bay}: ${status} — ${occupancy_snapshot.available} available`);
updateMapMarker(bay, status);
}
};
JavaScript / Node SDK
npm install @spacer/dwell
import SpacerDwell from '@spacer/dwell';
const dwell = new SpacerDwell('dw_live_••••••••');
// All SDK methods return typed promises
const spaces = await dwell.spaces.search({ lat: -33.8688, lng: 151.2093 });
const booking = await dwell.bookings.create({ space_id: 'sp_3f8a2b9c', ... });
const stream = dwell.stream.connect({ venue_id: 'sp_3f8a2b9c' });
stream.on('sensor.update', event => { /* ... */ });
Python SDK
pip install spacer-dwell
from spacer_dwell import SpacerDwell client = SpacerDwell(api_key='dw_live_••••••••') spaces = client.spaces.search(lat=-33.8688, lng=151.2093, radius=500) booking = client.bookings.create(space_id='sp_3f8a2b9c', ...)
Other SDKs
| Language | Package | Status |
|---|---|---|
| Swift | SpacerDwell (Swift Package) | Stable · iOS 15+, macOS 12+ |
| Kotlin | com.spacer:dwell-android | Stable · Android API 26+ |
| Go | github.com/spacer/dwell-go | Beta · Go 1.21+ |
| Ruby | spacer-dwell (RubyGems) | Coming Q3 2026 |
Changelog
v1.4.2 — 2 June 2026
- Added
sensor_healthto availability response. - Dynamic pricing multiplier now returned in search results.
- Webhook delivery retries increased from 3 to 7 attempts.
v1.4.0 — 14 May 2026
- ML occupancy forecast added to
GET /spaces/:id/availability. - New
sensor.offlinewebhook event type. - Revenue analytics endpoint launched.
- Go SDK reaches beta.
v1.3.0 — 22 Mar 2026
- WebSocket streaming endpoint (
wss://stream.dwell.spacer.com/v1/sensors) launched. - ANPR vehicle plate matching for ANPR-equipped venues.
- EV bay availability now surfaced in search results.