diff --git a/NEWS b/NEWS
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,16 @@
+# Version 0.2.0
+
+* API revamped in order to support `pipes-4.0.0` and `pipes-parse-2.0.0`.
+
+* Added functions that act directly upon `Put` and `Get` monads: `encodePut`,
+  `decodeGet`, `decodeGetMany`.
+
+* Decoding functions now report the number of bytes that were consumed in order
+  to decode a value.
+
+* Re-export `Get`, `Put`, `Binary` and `ByteOffset` from the `binary` package.
+
+
 # Version 0.1.0.0
 
 * Initial version.
diff --git a/PEOPLE b/PEOPLE
--- a/PEOPLE
+++ b/PEOPLE
@@ -3,3 +3,5 @@
 discussions about the library design.
 
 Renzo Carbonara
+Gabriel Gonzalez
+Kyle Van Berendonck
diff --git a/pipes-binary.cabal b/pipes-binary.cabal
--- a/pipes-binary.cabal
+++ b/pipes-binary.cabal
@@ -1,12 +1,12 @@
 name:               pipes-binary
-version:            0.1.0.0
+version:            0.2.0
 license:            BSD3
 license-file:       LICENSE
 copyright:          Copyright (c) Renzo Carbonara 2013
 author:             Renzo Carbonara
 maintainer:         renzocarbonaraλgmail.com
 stability:          Experimental
-tested-with:        GHC == 7.4.1
+tested-with:        GHC == 7.6.3
 homepage:           https://github.com/k0001/pipes-binary
 bug-reports:        https://github.com/k0001/pipes-binary/issues
 category:           Pipes
@@ -15,7 +15,7 @@
 cabal-version:      >=1.8
 extra-source-files: README.md PEOPLE NEWS
 description:
-  Encode and decode binary streams using the @Pipes@ and @binary@ libraries.
+  Encode and decode binary Pipes streams using the @binary@ library.
   .
   See the @NEWS@ file in the source distribution to learn about any
   important changes between version.
@@ -29,8 +29,9 @@
     build-depends:     base           (==4.*)
                      , binary         (>=0.7 && <0.8)
                      , bytestring     (>=0.9.2.1)
-                     , pipes          (>=3.3 && <3.4)
-                     , pipes-parse    (>=1.0 && <1.1)
-    exposed-modules:   Control.Proxy.Binary
-    other-modules:     Control.Proxy.Binary.Internal
+                     , pipes          (>=4.0 && <4.1)
+                     , pipes-parse    (>=2.0 && <2.1)
+                     , transformers   (>=0.2 && <0.4)
+    exposed-modules:   Pipes.Binary
+    other-modules:     Pipes.Binary.Internal
     ghc-options: -Wall -O2
diff --git a/src/Control/Proxy/Binary.hs b/src/Control/Proxy/Binary.hs
deleted file mode 100644
--- a/src/Control/Proxy/Binary.hs
+++ /dev/null
@@ -1,122 +0,0 @@
--- | This module exports facilities that allows you to encode and decode
--- streams of 'Bin.Binary' values using the @pipes@ and @pipes-parse@ libraries.
-
-module Control.Proxy.Binary
-  ( -- * Decoding
-    -- $decoding
-    decode
-  , decodeD
-    -- * Encoding
-    -- $encoding
-  , encode
-  , encodeD
-   -- * Types
-  , I.DecodingError(..)
-  ) where
-
--------------------------------------------------------------------------------
-
-import qualified Data.ByteString               as BS
-import qualified Data.ByteString.Lazy.Internal as BLI
-import           Control.Monad                 (unless)
-import qualified Control.Proxy                 as P
-import qualified Control.Proxy.Binary.Internal as I
-import qualified Control.Proxy.Parse           as Pa
-import qualified Control.Proxy.Trans.Either    as P
-import qualified Control.Proxy.Trans.State     as P
-import qualified Data.Binary                   as Bin
-import           Data.Foldable                 (mapM_)
-import           Data.Function                 (fix)
-import           Prelude                       hiding (mapM_)
-
---------------------------------------------------------------------------------
--- $decoding
---
--- There are two different 'Bin.Binary' decoding facilities exported by this
--- module, and choosing between them is easy: If you need to interleave decoding
--- with other stream effects you must use 'decode', otherwise you may use the
--- simpler 'decodeD'.
-
--- | Decodes one 'Bin.Binary' instance flowing downstream.
---
--- * In case of decoding errors, a 'I.DecodingError' exception is thrown in the
--- 'Pe.EitherP' proxy transformer.
---
--- * Requests more input from upstream using 'Pa.draw' when needed.
---
--- * /Do not/ use this proxy if 'Control.Proxy.ByteString.isEndOfBytes' returns
--- 'True', otherwise you may get unexpected decoding errors.
-decode
-  :: (P.Proxy p, Monad m, Bin.Binary r)
-  => P.EitherP I.DecodingError (P.StateP [BS.ByteString] p)
-     () (Maybe BS.ByteString) y' y m r
-decode = do
-    (er, mlo) <- P.liftP (I.parseWith Pa.draw Bin.get)
-    P.liftP (mapM_ Pa.unDraw mlo)
-    either P.throw return er
-{-# INLINABLE decode #-}
-
-
--- | Decodes 'Bin.Binary' instances flowing downstream until end of input.
---
--- * In case of decoding errors, a 'I.DecodingError' exception is thrown in the
--- 'Pe.EitherP' proxy transformer.
---
--- * Requests more input from upstream using 'Pa.draw', when needed.
---
--- * Empty input chunks flowing downstream will be discarded.
-decodeD
-  :: (P.Proxy p, Monad m, Bin.Binary b)
-  => ()
-  -> P.Pipe (P.EitherP I.DecodingError (P.StateP [BS.ByteString] p))
-     (Maybe BS.ByteString) b m ()
-decodeD = \() -> loop where
-    loop = do
-        eof <- P.liftP isEndOfBytes
-        unless eof $ decode >>= P.respond >> loop
-{-# INLINABLE decodeD #-}
-
---------------------------------------------------------------------------------
--- $encoding
---
--- There are two different 'Bin.Binary' encoding facilities exported by this
--- module, and choosing between them is easy: If you need to interleave encoding
--- with other stream effects you must use 'encode', otherwise you may use the
--- simpler 'encodeD'.
-
--- | Encodes the given 'Bin.Binary' instance and sends it downstream in
--- 'BS.ByteString' chunks.
-encode
-  :: (P.Proxy p, Monad m, Bin.Binary x)
-  => x -> p x' x () BS.ByteString m ()
-encode = \x -> P.runIdentityP $ do
-    BLI.foldrChunks (\e a -> P.respond e >> a) (return ()) (Bin.encode x)
-{-# INLINABLE encode #-}
-
-
--- | Encodes 'Bin.Binary' instances flowing downstream, each in possibly more
--- than one 'BS.ByteString' chunk.
-encodeD
-  :: (P.Proxy p, Monad m, Bin.Binary a)
-  => () -> P.Pipe p a BS.ByteString m r
-encodeD = P.pull P./>/ encode
-{-# INLINABLE encodeD #-}
-
-
---------------------------------------------------------------------------------
--- XXX: this function is here until pipes-bytestring exports it
-
--- | Like 'Pa.isEndOfInput', except it also consumes and discards leading
--- empty 'BS.ByteString' chunks.
-isEndOfBytes
-  :: (Monad m, P.Proxy p)
-  => P.StateP [BS.ByteString] p () (Maybe BS.ByteString) y' y m Bool
-isEndOfBytes = fix $ \loop -> do
-    ma <- Pa.draw
-    case ma of
-      Just a
-       | BS.null a -> loop
-       | otherwise -> Pa.unDraw a >> return False
-      Nothing      -> return True
-{-# INLINABLE isEndOfBytes #-}
-
diff --git a/src/Control/Proxy/Binary/Internal.hs b/src/Control/Proxy/Binary/Internal.hs
deleted file mode 100644
--- a/src/Control/Proxy/Binary/Internal.hs
+++ /dev/null
@@ -1,55 +0,0 @@
-{-# LANGUAGE DeriveDataTypeable #-}
-
--- | This module provides low-level integration with the @binary@ package and is
--- likely to be modified in backwards-incompatible ways in the future.
---
--- Use the "Control.Proxy.Binary" module instead.
-
-module Control.Proxy.Binary.Internal
-  ( DecodingError(..)
-  , parseWith
-  ) where
-
--------------------------------------------------------------------------------
-
-import qualified Data.ByteString              as BS
-import qualified Data.Binary                  as Bin
-import qualified Data.Binary.Get              as Bin
-import Control.Exception                      (Exception)
-import Data.Data                              (Data, Typeable)
-
--------------------------------------------------------------------------------
-
-data DecodingError = DecodingError
-  { peConsumed :: Bin.ByteOffset -- ^Number of bytes consumed before the error.
-  , peMessage  :: String         -- ^Error message.
-  } deriving (Show, Eq, Data, Typeable)
-
-instance Exception DecodingError
-
--------------------------------------------------------------------------------
-
--- | Run a parser drawing input from the given monadic action as needed.
-parseWith
-  :: (Monad m, Bin.Binary r)
-  => m (Maybe BS.ByteString)
-  -- ^An action that will be executed to provide the parser with more input
-  -- as needed. If the action returns 'Nothing', then it's assumed no more
-  -- input is available.
-  -> Bin.Get r
-  -- ^Parser to run on the given input.
-  -> m (Either DecodingError r, Maybe BS.ByteString)
-  -- ^Either a parser error or a parsed result, together with any leftover.
-parseWith refill g = step $ Bin.runGetIncremental g
-  where
-    step (Bin.Partial k)   = step . k =<< refill
-    step (Bin.Done lo _ r) = return (Right r, mayInput lo)
-    step (Bin.Fail lo n m) = return (Left (DecodingError n m), mayInput lo)
-{-# INLINABLE parseWith #-}
-
--- | Wrap @a@ in 'Just' if not-null. Otherwise, 'Nothing'.
-mayInput :: BS.ByteString -> Maybe BS.ByteString
-mayInput x | BS.null x = Nothing
-           | otherwise = Just x
-{-# INLINE mayInput #-}
-
diff --git a/src/Pipes/Binary.hs b/src/Pipes/Binary.hs
new file mode 100644
--- /dev/null
+++ b/src/Pipes/Binary.hs
@@ -0,0 +1,154 @@
+{-# LANGUAGE RankNTypes #-}
+
+-- | This module exports facilities that allow you to encode and decode Pipes
+-- streams of binary values. It builds on top of the @binary@, @pipes@ and
+-- @pipes-parse@ libraries, and assumes you know how to use those libraries.
+
+module Pipes.Binary
+  ( -- * @Binary@ instances
+    encode
+  , decode
+  , decodeMany
+    -- * @Get@ monad
+  , decodeGet
+  , decodeGetMany
+    -- * @Put@ monad
+  , encodePut
+    -- * Types
+  , I.DecodingError(..)
+    -- * Exports
+    -- $exports
+  , isEndOfBytes
+  , module Data.Binary.Get
+  ) where
+
+-------------------------------------------------------------------------------
+
+import qualified Data.ByteString               as B
+import qualified Data.ByteString.Lazy.Internal as BLI
+import           Pipes
+import           Pipes.Core
+import qualified Pipes.Binary.Internal         as I
+import qualified Pipes.Lift                    as P
+import qualified Pipes.Parse                   as Pp
+import qualified Data.Binary                   as Bin (get, put)
+import qualified Data.Binary.Put               as Put (runPut)
+--------------------------------------------------------------------------------
+import           Data.Binary                   (Binary)
+import           Data.Binary.Get               (ByteOffset, Get)
+import           Data.Binary.Put               (Put)
+
+-- $exports
+--
+-- The following types are re-exported on this module for your convenience:
+--
+-- [From "Data.Binary"] 'Binary'.
+--
+-- [From "Data.Binary.Get"] 'Get', 'ByteOffset'.
+--
+-- [From "Data.Binary.Put"] 'Put'.
+
+--------------------------------------------------------------------------------
+
+-- | Try to decode leading output from the underlying 'Producer' into a
+-- 'Bin.Binary' instance, returning either a 'I.DecodingError' on failure, or a
+-- pair with the decoded entity together with the number of bytes consumed in
+-- order to produce it.
+--
+-- /Do not/ use this function if 'isEndOfBytes' returns 'True', otherwise you
+-- may get unexpected decoding errors.
+decode
+  :: (Monad m, Binary b)
+  => Pp.StateT (Producer B.ByteString m r) m
+               (Either I.DecodingError (ByteOffset, b)) -- ^
+decode = decodeGet Bin.get
+{-# INLINABLE decode #-}
+
+-- | Like 'decode', except it takes an explicit 'Bin.Get' monad.
+decodeGet
+  :: Monad m
+  => Get b  -- ^
+  -> Pp.StateT (Producer B.ByteString m r) m
+               (Either I.DecodingError (ByteOffset, b))
+decodeGet get = do
+    (er, mlo) <- I.parseWithDraw get
+    case mlo of
+      Just lo -> Pp.unDraw lo
+      Nothing -> return ()
+    return er
+{-# INLINABLE decodeGet #-}
+
+-- | Continuously decode output from the given 'Producer' into a 'Bin.Binary'
+-- instance, sending downstream pairs of each successfully decoded entity
+-- together with the number of bytes consumed in order to produce it.
+--
+-- This 'Producer' runs until it either runs out of input or a decoding
+-- failure occurs, in which case it returns 'Left' with a 'I.DecodingError' and
+-- a 'Producer' with any leftovers. You can use 'P.errorP' to turn the 'Either'
+-- return value into an 'Control.Monad.Trans.Error.ErrorT' monad transformer.
+decodeMany
+  :: (Monad m, Binary b)
+  => Producer B.ByteString m r  -- ^Producer from which to draw input.
+  -> Producer' (ByteOffset, b) m
+               (Either (I.DecodingError, Producer B.ByteString m r) r)
+decodeMany src = decodeGetMany Bin.get src
+{-# INLINABLE decodeMany #-}
+
+-- | Like 'decodeMany', except it takes an explicit 'Bin.Get' monad.
+decodeGetMany
+  :: Monad m
+  => Get b
+  -> Producer B.ByteString m r  -- ^Producer from which to draw input.
+  -> Producer' (ByteOffset, b) m
+               (Either (I.DecodingError, Producer B.ByteString m r) r)
+decodeGetMany get src = do
+    (me, src') <- P.runStateP src go
+    return $ case me of
+      Left  e -> Left  (e, src')
+      Right r -> Right r
+  where
+    go = do
+        eof <- lift isEndOfBytes
+        if eof
+          then do
+            ra <- lift Pp.draw
+            case ra of
+              Left  r -> return (Right r)
+              Right _ -> error "Pipes.Binary.decodeGetMany: impossible!"
+          else do
+            eb <- lift (decodeGet get)
+            case eb of
+              Left  e -> return (Left e)
+              Right b -> yield b >> go
+{-# INLINABLE decodeGetMany #-}
+
+--------------------------------------------------------------------------------
+
+-- | Encodes the given 'Bin.Binary' instance and sends it downstream in
+-- 'BS.ByteString' chunks.
+encode :: (Monad m, Binary x) => x -> Producer' B.ByteString m ()
+encode = \x -> encodePut (Bin.put x)
+{-# INLINABLE encode #-}
+
+-- | Like 'encode', except it takes an explicit 'Bin.Put' monad.
+encodePut :: Monad m => Put -> Producer' B.ByteString m ()
+encodePut = \put -> do
+    BLI.foldrChunks (\e a -> respond e >> a) (return ()) (Put.runPut put)
+{-# INLINABLE encodePut #-}
+
+--------------------------------------------------------------------------------
+-- XXX: this function is here until pipes-bytestring exports it
+
+-- | Checks if the underlying 'Producer' has any bytes left.
+-- Leading 'BS.empty' chunks are discarded.
+isEndOfBytes :: Monad m => Pp.StateT (Producer B.ByteString m r) m Bool
+isEndOfBytes = do
+    ma <- Pp.draw
+    case ma of
+      Left  _      -> return True
+      Right a
+       | B.null a  -> isEndOfBytes
+       | otherwise -> Pp.unDraw a >> return False
+{-# INLINABLE isEndOfBytes #-}
+{-# DEPRECATED isEndOfBytes
+    "Will be removed as soon as the `pipes-bytestring` library exports it" #-}
diff --git a/src/Pipes/Binary/Internal.hs b/src/Pipes/Binary/Internal.hs
new file mode 100644
--- /dev/null
+++ b/src/Pipes/Binary/Internal.hs
@@ -0,0 +1,79 @@
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE FlexibleInstances #-}
+
+-- | This module provides low-level integration with the @binary@ package and is
+-- likely to be modified in backwards-incompatible ways in the future.
+--
+-- Use the "Pipes.Binary" module instead.
+
+module Pipes.Binary.Internal
+  ( DecodingError(..)
+  , parseWithDraw
+  ) where
+
+-------------------------------------------------------------------------------
+
+import           Control.Exception            (Exception)
+import           Control.Monad.Trans.Error    (Error)
+import qualified Data.ByteString              as B
+import qualified Data.Binary.Get              as Get
+import           Data.Data                    (Data, Typeable)
+import           Pipes                        (Producer)
+import qualified Pipes.Parse                  as Pp
+
+-------------------------------------------------------------------------------
+
+-- | A 'Get.Get' decoding error, as provided by 'Get.Fail'.
+data DecodingError = DecodingError
+  { peConsumed :: Get.ByteOffset -- ^Number of bytes consumed before the error.
+  , peMessage  :: String         -- ^Error message.
+  } deriving (Show, Read, Eq, Data, Typeable)
+
+instance Exception DecodingError
+instance Error     DecodingError
+
+-------------------------------------------------------------------------------
+
+instance Monad m => Error (DecodingError, Producer B.ByteString m r)
+
+-------------------------------------------------------------------------------
+
+-- | Run a 'Get.Get' drawing input from the given monadic action as needed.
+parseWith
+  :: Monad m
+  => m (Maybe B.ByteString)
+  -- ^An action that will be executed to provide the parser with more input
+  -- as needed. If the action returns 'Nothing', then it's assumed no more
+  -- input is available.
+  -> Get.Get r
+  -- ^Parser to run on the given input.
+  -> m (Either DecodingError (Get.ByteOffset, r), Maybe B.ByteString)
+  -- ^Either a decoding error or a pair of a result and the number of bytes
+  -- consumed, as well as an any leftovers.
+parseWith refill = \g -> step (Get.runGetIncremental g)
+  where
+    step (Get.Partial k)   = refill >>= \a -> step (k a)
+    step (Get.Done lo n r) = return (Right (n, r), mayInput lo)
+    step (Get.Fail lo n m) = return (Left (DecodingError n m), mayInput lo)
+{-# INLINABLE parseWith #-}
+
+-- | Run a parser drawing input from the underlying 'Producer'.
+parseWithDraw
+  :: Monad m
+  => Get.Get b -- ^Parser to run on the given input.
+  -> Pp.StateT (Producer B.ByteString m r) m
+               (Either DecodingError (Get.ByteOffset, b), Maybe B.ByteString)
+parseWithDraw = parseWith $ do
+    ea <- Pp.draw
+    return (case ea of
+      Left  _ -> Nothing
+      Right a -> Just a)
+{-# INLINABLE parseWithDraw #-}
+
+--------------------------------------------------------------------------------
+
+-- | Wrap @a@ in 'Just' if not-null. Otherwise, 'Nothing'.
+mayInput :: B.ByteString -> Maybe B.ByteString
+mayInput x | B.null x = Nothing
+           | otherwise = Just x
+{-# INLINE mayInput #-}
