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.

Base URL: https://api.dwell.spacer.com/v1
Try 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.

HTTP Header
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.
Never expose live keys in client-side code. Use test keys for development and move secrets to environment variables before deployment.

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.

PlanRequests / minuteRequests / monthWebSocket connections
Developer (free)601,0001
Startup600100,00010
EnterpriseUnlimitedUnlimitedUnlimited

Every response includes rate limit headers:

Response 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 Response
{
  "error": {
    "code": "invalid_parameter",
    "message": "Parameter 'radius' must be between 50 and 10000 metres.",
    "param": "radius",
    "request_id": "req_8f2a93bd1e"
  }
}
StatusCodeDescription
400invalid_parameterA required parameter is missing or invalid.
401unauthorizedAPI key is missing or invalid.
403forbiddenYour key does not have access to this resource.
404not_foundThe requested resource does not exist.
409conflictThe space is no longer available for the requested time.
429rate_limit_exceededToo many requests. See Retry-After header.
500internal_errorSomething went wrong on our end. We're automatically alerted.

Search Spaces

GET /v1/spaces/search Find available parking spaces near a location

Returns a ranked list of parking spaces near the given coordinates, including real-time availability from ground sensors, pricing, and amenities.

Query Parameters

ParameterTypeRequiredDescription
latnumberrequiredLatitude of search centre (WGS84).
lngnumberrequiredLongitude of search centre (WGS84).
radiusintegeroptionalSearch radius in metres. Default: 500. Max: 10000.
typestringoptionalFilter by space type: covered, outdoor, multi-deck, underground.
available_onlybooleanoptionalIf true, only return spaces with at least 1 available bay. Default: false.
ev_chargingbooleanoptionalFilter for spaces with EV charging.
limitintegeroptionalResults per page. Default: 10. Max: 100.
pageintegeroptionalPage number. Default: 1.

Code Example

cURL
JavaScript
Python
cURL
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

JSON · 200 OK · 38ms
{
  "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 }
}
Run in Sandbox →

Live Availability

GET /v1/spaces/:id/availability Real-time occupancy + ML forecast

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

ParameterTypeRequiredDescription
idstringrequiredThe space ID (e.g. sp_3f8a2b9c).

Example Response

JSON · 200 OK · 22ms
{
  "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 }
}
Run in Sandbox →

Create Booking

POST /v1/bookings Reserve a bay and receive access credentials

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

FieldTypeRequiredDescription
space_idstringrequiredID of the target space.
fromdatetimerequiredStart time (ISO 8601 with timezone offset).
todatetimerequiredEnd time (ISO 8601 with timezone offset).
driver.namestringrequiredFull name of the driver.
driver.emailstringrequiredEmail for booking confirmation.
driver.vehicle_platestringoptionalLicence plate for ANPR integration.
payment_method_idstringoptionalStripe payment method ID. If omitted, uses the default payment method on the account.

Example Response

JSON · 201 Created · 94ms
{
  "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"
}
Run in Sandbox →

Dynamic Pricing

GET /v1/pricing/dynamic ML-powered current and forecasted rates

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.

JSON · 200 OK · 19ms
{
  "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
  }
}
Run in Sandbox →

Occupancy Analytics

GET /v1/analytics/occupancy Historical occupancy, revenue, and dwell data

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

ParameterTypeRequiredDescription
venue_idstringoptionalFilter to a specific space ID. Omit for account-wide data.
periodstringoptionalPreset: last_7_days, last_30_days, last_90_days, this_year.
fromdateoptionalCustom range start (ISO 8601 date). Overrides period.
todateoptionalCustom range end (ISO 8601 date).
granularitystringoptionalhourly or daily. Auto-selected if omitted.
Run in Sandbox →

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.

POST /v1/webhooks Register a webhook endpoint
FieldTypeRequiredDescription
urlstringrequiredYour HTTPS endpoint URL.
eventsarrayrequiredList of event types to subscribe to.

Event Reference

EventTriggered when
booking.createdA new booking is confirmed.
booking.cancelledA booking is cancelled by the driver or operator.
booking.modifiedA booking's schedule or details are changed.
space.status_changedA space transitions between open / filling_fast / full / closed.
occupancy.thresholdOccupancy crosses a configured percentage threshold (e.g. 80%).
pricing.updatedThe dynamic pricing multiplier changes for a space.
sensor.offlineA 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.

Node.js
Python
Signature Verification
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.

WebSocket URL
wss://stream.dwell.spacer.com/v1/sensors?api_key=YOUR_KEY&venue_id=sp_3f8a2b9c

Event Payload

JSON Event
{
  "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 }
}
JavaScript Example
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
npm install @spacer/dwell
JavaScript
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
pip install spacer-dwell
Python
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

LanguagePackageStatus
SwiftSpacerDwell (Swift Package)Stable · iOS 15+, macOS 12+
Kotlincom.spacer:dwell-androidStable · Android API 26+
Gogithub.com/spacer/dwell-goBeta · Go 1.21+
Rubyspacer-dwell (RubyGems)Coming Q3 2026

Changelog

v1.4.2 — 2 June 2026

  • Added sensor_health to 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.offline webhook 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.