Errors & Rate Limits
The error envelope
Almost every error response from the Bikeep API — across all services — uses the same four-field envelope:
{
"error_code": "ERRORS.LOCK_COMMAND_THROTTLED",
"error_message": "a lock or unlock command was just sent to this device, please wait before retrying",
"status_code": 429,
"correlation_id": "3f2a1c8e-..."
}
Branch your error handling on error_code — it is the stable, machine-readable field. error_message is a human-readable description and may change wording without notice; don’t parse it. status_code repeats the HTTP status code inside the body. correlation_id is always present — include it when contacting Bikeep support.
Note: Batch endpoints are the exception. Where a request carries many items and some succeed while others fail, the
400response replaceserror_code/error_messagewith anerror_resultsarray naming the individual failures. Successful items are not rolled back.
Rate limits
lock and unlock share a single per-device throttle window. Sending either command starts the window; sending either command again before it elapses returns 429 ERRORS.LOCK_COMMAND_THROTTLED. The window length depends on the device’s power_save_strategy, which is already returned on every device response (see Device Types — Power Save and Sleep):
power_save_strategy |
Throttle window |
|---|---|
NONE |
3 seconds |
REMOTE_WAKEUP |
15 seconds |
LOCAL_WAKEUP |
15 seconds |
Derive your own client-side backoff from this table — there is no Retry-After header and no field exposing the remaining wait time.
What to know about the throttle:
- Per device, not per user. The window is keyed on the device, not the caller. If another integrator (or another user of your own app) locks or unlocks the same device, everyone’s window resets.
- Only
lockandunlockare throttled.book,cancel-booking,take-photo,sync-device, and everyforce-*command are not subject to this window. - A rejected attempt does not extend the window. A 429 response does not reset the clock — the stored last-attempt time is only updated on a successful claim. Retrying right after a 429 does not push your next eligible attempt further out.
- Entrance doors (
BIKE_HOUSE_DOOR) are still throttled. Sessionless devices skip the state-precondition check onunlock(see below), but they are not exempt from this throttle — expect 15-second windows on wakeup hardware.
Note: Rate limiting and state validation are independent checks, run in this order: health check → state/permission validation → throttle claim. A
429means your request was otherwise valid and only sent too soon. A request rejected for being in the wrong state returns400(see Command state preconditions below) and never touches the throttle window.
Recommended backoff
For ERRORS.LOCK_COMMAND_THROTTLED, wait out the applicable window from the table above before retrying — polling faster just produces more 429s, since a rejected attempt doesn’t reset anything. If you don’t already know the device’s power_save_strategy, default to a 15-second wait rather than guessing 3.
Error code reference
unlock returns a more specific code than the generic wrong-state error when the device is already unlocked or already unlocking — these short-circuit before the general check runs, so they only ever apply to unlock.
| Command | Cause | error_code |
Status |
|---|---|---|---|
lock |
Device not in UNLOCKED, BOOKED, or ALLOCATED |
ERRORS.DEVICE_INVALID_UNLOCK_STATUS |
400 |
unlock |
Device not in LOCKED or LOCKING |
ERRORS.DEVICE_INVALID_LOCK_STATUS |
400 |
unlock |
Device already UNLOCKED |
ERRORS.DEVICE_ALREADY_UNLOCKED |
400 |
unlock |
Device already UNLOCKING |
ERRORS.DEVICE_ALREADY_UNLOCKING |
400 |
book |
Device not in UNLOCKED or ALLOCATED |
ERRORS.DEVICE_NOT_UNLOCKED |
400 |
lock, book |
Your own ALLOCATED device, but state.timeout_at has not passed yet |
ERRORS.DEVICE_ALLOCATION_NOT_TIMED_OUT |
400 |
lock, book |
Device is allocated or locked by a different user | ERRORS.DEVICE_ALREADY_OCCUPIED_BY_ANOTHER_USER |
400 |
cancel-booking |
Device not BOOKED |
ERRORS.DEVICE_NOT_BOOKED |
400 |
lock, unlock |
Throttle window still open (see Rate limits) | ERRORS.LOCK_COMMAND_THROTTLED |
429 |
Tip: The
*_LOCK_STATUS/*_UNLOCK_STATUSnaming refers to the state the device needs to be in, not the command you sent — use the command column above rather than reasoning about the name in isolation.
Other 400 codes you may encounter on the commands endpoint: ERRORS.USER_ID_MISMATCH, ERRORS.COMMAND_NOT_SUPPORTED, ERRORS.UNKNOWN_COMMAND, ERRORS.UNHEALTHY_DEVICE, ERRORS.DEVICE_UNDER_MAINTENANCE, ERRORS.TOO_MANY_DEVICES_ALREADY_OCCUPIED, ERRORS.INVALID_TIMEOUT_AT.