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 400 response replaces error_code/error_message with an error_results array 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 lock and unlock are throttled. book, cancel-booking, take-photo, sync-device, and every force-* 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 on unlock (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 429 means your request was otherwise valid and only sent too soon. A request rejected for being in the wrong state returns 400 (see Command state preconditions below) and never touches the throttle window.

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_STATUS naming 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.