diff --git a/Changelog.md b/Changelog.md
--- a/Changelog.md
+++ b/Changelog.md
@@ -1,3 +1,7 @@
+# 0.3
+
+- Add `extractByteString` function to the `EmacsMonad` class
+
 # 0.2.1.1
 
 - Fix build with GHC 9.10.1
diff --git a/emacs-module.cabal b/emacs-module.cabal
--- a/emacs-module.cabal
+++ b/emacs-module.cabal
@@ -3,7 +3,7 @@
 name:
   emacs-module
 version:
-  0.2.1.1
+  0.3
 category: Foreign, Foreign binding
 
 synopsis:
@@ -29,11 +29,12 @@
 maintainer:
   Sergey Vinokurov <serg.foo@gmail.com>
 tested-with:
-  , GHC == 9.2.8
-  , GHC == 9.4.7
-  , GHC == 9.6.3
-  , GHC == 9.8.1
-  , GHC == 9.10.1
+  , GHC == 9.2
+  , GHC == 9.4
+  , GHC == 9.6
+  , GHC == 9.8
+  , GHC == 9.10
+  , GHC == 9.12
 
 extra-source-files:
   cbits/emacs-module.h
@@ -152,8 +153,8 @@
     , os-string
     , primitive
     , prettyprinter >= 1.7
-    , prettyprinter-combinators
-    , text >= 2
+    , prettyprinter-combinators >= 0.1.1
+    , text >= 2.0.1
     , template-haskell
     , transformers-base
     , tuples-homogenous-h98
diff --git a/src/Emacs/Module/Monad.hs b/src/Emacs/Module/Monad.hs
--- a/src/Emacs/Module/Monad.hs
+++ b/src/Emacs/Module/Monad.hs
@@ -331,6 +331,13 @@
       $   handleResultNoThrow
       =<< Common.extractShortByteString (coerceBuilderCache eArgsCache) eEnv eNonLocalState (getRawValue x)
 
+  extractByteString :: WithCallStack => Value s -> EmacsM s BS.ByteString
+  extractByteString x = EmacsM $ do
+    Environment{eEnv, eNonLocalState, eArgsCache} <- ask
+    liftBase
+      $   handleResultNoThrow
+      =<< Common.extractByteString (coerceBuilderCache eArgsCache) eEnv eNonLocalState (getRawValue x)
+
   makeString :: WithCallStack => BS.ByteString -> EmacsM s (Value s)
   makeString x = withEnv $ \env ->
     BSU.unsafeUseAsCStringLen x $ \(pStr, len) ->
diff --git a/src/Emacs/Module/Monad/Class.hs b/src/Emacs/Module/Monad/Class.hs
--- a/src/Emacs/Module/Monad/Class.hs
+++ b/src/Emacs/Module/Monad/Class.hs
@@ -171,6 +171,9 @@
   -- | Extract string contents from an Emacs value as utf8-encoded short bytestring.
   extractShortByteString :: WithCallStack => v s -> m s BSS.ShortByteString
 
+  -- | Extract string contents from an Emacs value as bytestring.
+  extractByteString :: WithCallStack => v s -> m s BS.ByteString
+
   -- | Convert a utf8-encoded ByteString into an Emacs value.
   makeString :: WithCallStack => BS.ByteString -> m s (v s)
 
diff --git a/src/Emacs/Module/Monad/Common.hs b/src/Emacs/Module/Monad/Common.hs
--- a/src/Emacs/Module/Monad/Common.hs
+++ b/src/Emacs/Module/Monad/Common.hs
@@ -22,6 +22,7 @@
   , nonLocalExitSignal
   , extractText
   , extractShortByteString
+  , extractByteString
   , checkNonLocalExitSignal
   , checkNonLocalExitFull
   , extractSignalInfo
@@ -30,6 +31,8 @@
 
 import Control.Exception
 import Control.Monad.Primitive
+import Data.ByteString qualified as BS
+import Data.ByteString.Internal qualified as BSI
 import Data.ByteString.Short (ShortByteString)
 import Data.ByteString.Short qualified as SBS
 import Data.Text (Text)
@@ -42,12 +45,12 @@
 import Foreign.Ptr
 import Foreign.Storable
 import GHC.Exts
+import GHC.ForeignPtr (ForeignPtr(..), ForeignPtrContents(PlainPtr))
 import GHC.IO
 import GHC.Stack (CallStack, callStack)
 import Prettyprinter
 
 #ifdef ASSERTIONS
-import Data.ByteString.Internal qualified as BSI
 import Data.Text.Encoding qualified as TE
 import Foreign.ForeignPtr qualified as Foreign
 #endif
@@ -195,7 +198,8 @@
       -- Should subtract 1 from size to avoid NULL terminator at the end.
       ptr <- Foreign.newForeignPtr_ (Ptr (mutableByteArrayContents# mbarr#))
       evaluate $ TE.decodeUtf8 $ BSI.BS ptr (I# (size# -# 1#))
-#else
+#endif
+#ifndef ASSERTIONS
     IO $ \s1 ->
       case unsafeFreezeByteArray# mbarr# s1 of
         (# s2, barr #) ->
@@ -220,6 +224,22 @@
           case unsafeFreezeByteArray# mbarr# s4 of
             (# s5, barr #) ->
               (# s5, SBS.SBS barr #)
+
+{-# INLINE extractByteString #-}
+extractByteString
+  :: WithCallStack
+  => BuilderCache (RawValue a)
+  -> Env
+  -> NonLocalState
+  -> RawValue p
+  -> IO (EmacsRes EmacsSignal Void BS.ByteString)
+extractByteString cache env nls x =
+  extractStringWith cache env nls x $ \size# mbarr# -> evaluate $ BSI.BS
+    (ForeignPtr
+      (mutableByteArrayContents# mbarr#)
+      (PlainPtr mbarr#))
+    -- Should subtract 1 from size to avoid NULL terminator at the end.
+    (I# (size# -# 1#))
 
 {-# INLINE checkNonLocalExitSignal #-}
 checkNonLocalExitSignal
