diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+# New in 0.1.6.0
+
+- `getrange`, `setrange`, `getbit`, `setbit` added for new ByteString-based primitive
+
 # New in 0.1.4.0
 
 - `srandmembersN` command added
diff --git a/hlrdb-core.cabal b/hlrdb-core.cabal
--- a/hlrdb-core.cabal
+++ b/hlrdb-core.cabal
@@ -1,5 +1,5 @@
 name:                hlrdb-core
-version:             0.1.5.0
+version:             0.1.6.0
 synopsis:            High-level Redis Database Core API
 description:         A library for type-driven interaction with Redis
 license:             MIT
@@ -29,15 +29,15 @@
     , HLRDB.Structures.SSet
     , HLRDB.Internal
   build-depends: base >= 4.9 && < 5.0
-               , bytestring (>= 0.10.8.2 && < 0.10.10.0) || ^>= 0.10.10.0
-               , hashable (>= 1.2.6.1 && < 1.3) || ^>= 1.3
-               , hedis (>= 0.10.1 && <= 0.12.10) || ^>= 0.12.10
-               , lens (>= 4.16 && <= 4.18.1) || ^>= 4.18.1
+               , bytestring >= 0.10.8.2 && < 0.10.11
+               , hashable >= 1.2.6.1 && < 1.4
+               , hedis >= 0.10.1 && < 0.13
+               , lens >= 4.16 && < 4.20
                , mtl ^>= 2.2.2
-               , profunctors (>= 5.2.2 && <= 5.5.1) || ^>= 5.5.1
-               , random ^>= 1.1
-               , time (>=1.6 && <1.9.3) || ^>= 1.9.3
-               , unordered-containers (>= 0.2.8.0 && < 0.2.10.0) || ^>= 0.2.10.0
+               , profunctors >= 5.2.2 && < 5.6
+               , random >= 1.1 && < 1.2
+               , time >=1.6 && <1.10
+               , unordered-containers >= 0.2.8.0 && < 0.2.11
   hs-source-dirs:      src
   default-language:    Haskell2010
   default-extensions:
diff --git a/src/HLRDB/Core.hs b/src/HLRDB/Core.hs
--- a/src/HLRDB/Core.hs
+++ b/src/HLRDB/Core.hs
@@ -18,6 +18,10 @@
        , incrby
        , decr
        , decrby
+       , getrange
+       , setrange
+       , getbit
+       , setbit
 
          -- * List         
        , lrange
diff --git a/src/HLRDB/Internal.hs b/src/HLRDB/Internal.hs
--- a/src/HLRDB/Internal.hs
+++ b/src/HLRDB/Internal.hs
@@ -44,6 +44,7 @@
 primKey :: RedisStructure v a b -> a -> ByteString
 primKey (RKeyValue (E e _ _)) k = e k
 primKey (RKeyValueInteger e _ _) k = e k
+primKey (RKeyValueByteString e) k = e k
 primKey (RList (E e _ _) _) k = e k
 primKey (RHSet (E e _ _) _) k = e k
 primKey (RSet (E e _ _)) k = e k
diff --git a/src/HLRDB/Primitives/Redis.hs b/src/HLRDB/Primitives/Redis.hs
--- a/src/HLRDB/Primitives/Redis.hs
+++ b/src/HLRDB/Primitives/Redis.hs
@@ -20,6 +20,7 @@
   -- Do not allow specifying an encoding for Integer, since Redis commands like incr
   -- demand that *only* the standard encoding is used, e.g., 123 -> "123"
   RKeyValueInteger :: (a -> ByteString) -> (b -> Integer) -> (Integer -> b) -> RedisStructure (BASIC Integer) a b
+  RKeyValueByteString :: (a -> ByteString) -> RedisStructure (BASIC ByteString) a ByteString
   RList :: RE a b -> Maybe TrimScheme -> RedisStructure LIST a b
   RHSet :: RE a b -> HSET v -> RedisStructure (HSET v) a b
   RSet :: RE a b -> RedisStructure SET a b
@@ -29,6 +30,8 @@
 type RedisBasic k v = RedisStructure (BASIC ()) k v
 -- | Alias for simple Integer storage
 type RedisIntegral k v = RedisStructure (BASIC Integer) k v
+-- | Alias for indexable ByteString storage, allowing getting and setting ranges within the value. To enforce consistency across the low-level getrange/setrange commands, non-existent values are transparently rendered as empty ByteStrings. Note that the maximum size permitted in a single value by Redis is 512 MB.
+type RedisByteString k v = RedisStructure (BASIC ByteString) k ByteString
 -- | Alias for a Redis List
 type RedisList k v = RedisStructure LIST k v
 -- | Alias for a Redis HSet
diff --git a/src/HLRDB/Structures/Basic.hs b/src/HLRDB/Structures/Basic.hs
--- a/src/HLRDB/Structures/Basic.hs
+++ b/src/HLRDB/Structures/Basic.hs
@@ -6,6 +6,7 @@
 import HLRDB.Primitives.Aggregate
 import HLRDB.Primitives.Redis
 import HLRDB.Internal
+import Data.ByteString (ByteString)
 import Data.ByteString.Char8 (pack)
 import qualified Data.HashMap.Strict as HM
 
@@ -18,11 +19,15 @@
 get (RKeyValueInteger k _ d) a = liftRedis $ Redis.get (k a) >>= \case
   Left e -> fail (show e)
   Right r -> pure $ d . fromIntegral $ decodeMInteger r
+get (RKeyValueByteString k) a = liftRedis $ Redis.get (k a) >>= \case
+  Left e -> fail (show e)
+  Right r -> pure (maybe mempty id r)
 
 -- | Construct a query to be used with @mget@. You may combine many of these together to create complex queries. Use @mget@ to execute the query back in the Redis monad. Works on @RedisBasic a b@ and @RedisIntegral a b@.
 liftq :: RedisStructure (BASIC w) a b -> a ⟿ b
 liftq (RKeyValue (E k _ d)) = T $ \f -> fmap d . f . k
 liftq (RKeyValueInteger k _ d) = T $ \f -> fmap (d . fromIntegral . decodeMInteger) . f . k
+liftq (RKeyValueByteString k) = T $ \f -> fmap (maybe mempty id) . f . k
 
 -- | Reify a (⟿) query into the Redis monad via a single mget command.
 mget :: MonadRedis m => a ⟿ b -> a -> m b
@@ -39,6 +44,9 @@
   Just bs -> ignore $ Redis.set (k a) bs
   Nothing -> ignore $ del [ k a ]
 set (RKeyValueInteger k e _) a i = liftRedis $ ignore $ Redis.set (k a) (pack $ show (e i))
+set (RKeyValueByteString k) a b = liftRedis $ if b == mempty
+  then ignore $ Redis.del [ k a ]
+  else ignore $ Redis.set (k a) b
 
 -- | Convenient alias for setting a value for an optional path
 set' :: MonadRedis m => RedisBasic a (Maybe b) -> a -> b -> m ()
@@ -50,6 +58,7 @@
 liftqs :: RedisStructure (BASIC w) a b -> (a , b) -> MSET
 liftqs (RKeyValue (E k e _)) (a , b) = MSET $ (<>) [ (k a , e b) ]
 liftqs (RKeyValueInteger k e _) (a , b) = MSET $ (<>) [ (k a , Just $ pack (show (e b))) ]
+liftqs (RKeyValueByteString k) (a , b) = MSET $ (<>) [ (k a , Just b) ]
 
 -- | Execute a @MSET@ query.
 mset :: MonadRedis m => MSET -> m ()
@@ -75,6 +84,9 @@
   Just bs -> ignore $ Redis.setex (k a) t bs
   Nothing -> ignore $ del [ k a ]
 setex (RKeyValueInteger k e _) a t i = liftRedis $ ignore $ Redis.setex (k a) t (pack $ show (e i))
+setex (RKeyValueByteString k) a t b = liftRedis $ if b == mempty
+  then ignore $ Redis.del [ k a ]
+  else ignore $ Redis.setex (k a) t b
 
 -- | Increment an Integer in Redis. Empty values are treated as 0.
 incr :: MonadRedis m => RedisIntegral a b -> a -> m b
@@ -107,4 +119,33 @@
   . unwrap
   . Redis.decrby (p k)
   . e
+
+-- | Start and end indices are inclusive. Unlike @get@, the empty bytestring is returned if the key does not exist in Redis or if the specified range is out of range.
+getrange :: MonadRedis m => RedisByteString a ByteString -> a -> Integer -> Integer -> m ByteString
+getrange (RKeyValueByteString p) k start =
+    unwrap
+  . Redis.getrange (p k) start
+
+-- | The @Integer@ paramter is the offset. Returns the length of the string after the command has been executed.
+setrange :: MonadRedis m => RedisByteString a ByteString -> a -> Integer -> ByteString -> m Integer
+setrange (RKeyValueByteString p) k start =
+    unwrap
+  . Redis.setrange (p k) start
+
+-- | Get the bit stored at the specified offset. Note that if no value exists in Redis or if the specified range is outside the defined range, @False@ will be returned by default.
+getbit :: MonadRedis m => RedisByteString a ByteString -> a -> Integer -> m Bool
+getbit (RKeyValueByteString p) k =
+    fmap (1==)
+  . unwrap
+  . Redis.getbit (p k)
+
+-- | Set the bit at the specified offset. If the offset is outside the existing defined range of the value, 0s are implicitly inserted to fill the intermediate space. Returns the existing value of this bit, as defined by the @getbit@ semantics above.
+setbit :: MonadRedis m => RedisByteString a ByteString -> a -> Integer -> Bool -> m Bool
+setbit (RKeyValueByteString p) k i =
+    fmap (1==)
+  . unwrap
+  . Redis.setbit (p k) i
+  . \case
+       True -> "1"
+       False -> "0"
 
