INCREX
INCREX key [BYFLOAT increment | BYINT increment] [LBOUND lowerbound] [UBOUND upperbound] [SATURATE] [EX seconds | PX milliseconds | EXAT unix-time-seconds| PXAT unix-time-milliseconds | PERSIST] [ENX]
- Available since:
- Redis Open Source 8.8.0
- Time complexity:
- O(1)
- ACL categories:
-
@fast,@string,@write, - Compatibility:
- Redis Software and Redis Cloud compatibility
Increments or decrements the numeric value stored at key by the specified amount, with optional upper/lower bounds and expiration control, in a single atomic operation.
If the key does not exist, it is set to 0 before performing the operation.
An error is returned if the key contains a value of the wrong type or a string that cannot be interpreted as a number.
Unlike INCR and INCRBY, INCREX returns an array of two elements: the new value of the key after the increment, and the increment that was actually applied. When the computed result would fall outside an explicit LBOUND/UBOUND or the type limits, the default is to skip the operation and reply with [current_value, 0], leaving the key and its TTL untouched. The SATURATE flag changes this behavior so the result is capped at the bound instead.
Required arguments
key
The name of the key to increment.
Optional arguments
BYFLOAT increment | BYINT increment
Specifies the increment amount and type:
BYFLOAT increment: increment the value by the given long-double float. The key's existing value may be either an integer or a float, since integers can be promoted to floats losslessly. Results that would produce NaN or Infinity are rejected.BYINT increment: increment the value by the given 64-bit signed integer. The increment may be negative to decrement the value.BYINTrequires the key's existing value to be integer-typed; a stored float such as"1.5"cannot be parsed back as an integer. This is consistent withINCR/INCRBY(integer-only) andINCRBYFLOAT(accepts both).
If neither BYFLOAT nor BYINT is specified, the key is incremented by 1 in integer mode. BYFLOAT and BYINT are mutually exclusive.
LBOUND lowerbound
Sets a lower bound for the resulting value. If the computed result would fall below lowerbound, the operation is skipped and the reply is [current_value, 0] (or use the SATURATE flag to floor the result at lowerbound instead). When omitted, the bound is LLONG_MIN in integer mode or -LDBL_MAX in BYFLOAT mode. LBOUND must be less than or equal to UBOUND when both are specified.
UBOUND upperbound
Sets an upper bound for the resulting value. If the computed result would exceed upperbound, the operation is skipped and the reply is [current_value, 0] (or use the SATURATE flag to cap the result at upperbound instead). When omitted, the bound is LLONG_MAX in integer mode or LDBL_MAX in BYFLOAT mode. UBOUND must be greater than or equal to LBOUND when both are specified.
SATURATE
When specified, an out-of-bounds result is capped at UBOUND or floored at LBOUND (or saturated to the type limits when no explicit bound is given). The second element of the reply reflects the saturated delta. An error is returned if the delta cannot be represented as a 64-bit signed integer in integer mode, or would produce Infinity in BYFLOAT mode. Any expiration option is still applied as specified.
A bound violation includes both exceeding an explicit LBOUND/UBOUND and overflowing the type limits when no explicit bound is given.
expiration flags
The INCREX command supports a set of options that modify its expiration behavior:
EX seconds: set the specified expiration time in seconds (a positive integer).PX milliseconds: set the specified expiration time in milliseconds (a positive integer).EXAT unix-time-seconds: set the specified Unix time in seconds (a positive integer) at which the key will expire.PXAT unix-time-milliseconds: set the specified Unix time in milliseconds (a positive integer) at which the key will expire.PERSIST: remove the expiration associated with the key.
When no expiration option is given, the key's existing TTL (if any) is preserved.
ENX
Only sets the TTL/expiration if the key currently has no TTL/expiration. If the key already has a TTL, the increment is still applied but the TTL is left unchanged. ENX can ensure that a window counter rate limiter's TTL is set only when it is created, and not reset on subsequent token requests.ENX must be combined with EX, PX, EXAT, or PXAT and is incompatible with PERSIST.
Examples
Default increment (by 1), starting from 0 if the key does not exist:
Increment by a specific integer using BYINT, including a negative increment to decrement:
Increment by a floating-point number using BYFLOAT:
Set an expiration on every increment with EX:
Use ENX to set an expiration only when the key has no existing TTL. The increment is always applied regardless:
Use PERSIST to remove the key's expiration while incrementing:
Compare the default out-of-bounds behavior with SATURATE when the result would exceed UBOUND. By default the key is left untouched and the reply reports a zero delta; with SATURATE the result is capped at the bound and the reply reflects the saturated delta:
Pattern: window counter rate limiter
A common rate-limiting pattern requires atomically incrementing a counter and setting its expiration. With plain INCR and EXPIRE, this typically requires a Lua script to be atomic.
INCREX requires a single native command. UBOUND enforces the rate cap — by default, once the cap is reached the operation is skipped — and ENX ensures that a new window with the correct duration is created if the previous one has expired; if a window already exists, it won't be extended. When the counter has already reached the cap, actual_increment is 0, giving the caller immediate feedback without extra reads or error handling:
new_val, actual_incr = redis.execute_command(
"INCREX", f"ratelimit:{user_id}",
"BYINT", 1, "UBOUND", 100,
"EX", 60, "ENX",
)
if actual_incr == 0:
reject_request() # rate limit exceeded
Redis Software and Redis Cloud compatibility
| Redis Software |
Redis Cloud |
Notes |
|---|---|---|
| ❌ Standard |
❌ Standard |
Return information
Array reply: a two-element array:
- New value — the value of the key after the increment, or the unchanged current value when an out-of-bounds result caused the operation to be skipped.
- Actual increment — the increment that was actually applied. May differ from the requested increment when
SATURATEcaps the result at a bound, and is always0when an out-of-bounds result caused the operation to be skipped.
Both elements are Integer replies in integer mode (default or BYINT), or Bulk string replies representing the float values in BYFLOAT mode.