diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
 # 0.1.2.0
 
+  * Add some higher-level functions for dealing with captures
+
+# 0.1.1.0
+
   * Add missing low-level functions
 
 # 0.1.0.3
diff --git a/regex-rure.cabal b/regex-rure.cabal
--- a/regex-rure.cabal
+++ b/regex-rure.cabal
@@ -1,6 +1,6 @@
 cabal-version:      1.18
 name:               regex-rure
-version:            0.1.1.0
+version:            0.1.2.0
 license:            AGPL-3
 license-file:       COPYING
 maintainer:         vamchale@gmail.com
diff --git a/src/Regex/Rure.chs b/src/Regex/Rure.chs
--- a/src/Regex/Rure.chs
+++ b/src/Regex/Rure.chs
@@ -12,7 +12,10 @@
                   , setMatches
                   , find
                   , matches
+                  , matches'
                   , mkIter
+                  , findCaptures
+                  , captures
                   -- * Types
                   , RureMatch (..)
                   -- ** Pointer types
@@ -47,6 +50,15 @@
 
 #include <rure.h>
 
+capturesAt :: RureCapturesPtr -> CSize -> IO (Maybe RureMatch)
+capturesAt rcp sz =
+    allocaBytes {# sizeof rure_match #} $ \matchPtr -> do
+    res <- rureCapturesAt rcp sz matchPtr
+    if res
+        then Just <$> rureMatchFromPtr matchPtr
+        else pure Nothing
+
+{-# DEPRECATED mkIter "This creates a stateful pointer in an otherwise pure API" #-}
 mkIter :: RurePtr -> IO RureIterPtr
 mkIter rePtr =
     castForeignPtr <$> (newForeignPtr rureIterFree . castPtr =<< rureIterNew rePtr)
@@ -94,6 +106,15 @@
         Left err -> pure (Left err)
         Right rp -> Right <$> ((\riPtr -> matches riPtr haystack) =<< mkIter rp)
 
+-- | @since 0.1.2.0
+matches' :: RurePtr
+         -> BS.ByteString
+         -> IO [RureMatch]
+matches' rp haystack = do
+    ri <- mkIter rp
+    matches ri haystack
+
+{-# DEPRECATED matches "Use matches', which is not stateful" #-}
 matches :: RureIterPtr
         -> BS.ByteString
         -> IO [RureMatch]
@@ -130,6 +151,58 @@
     case rePtr of
         Left err -> pure (Left err)
         Right rp -> Right <$> find rp haystack 0
+
+allocCapPtr :: RurePtr -> IO RureCapturesPtr
+allocCapPtr rp = do
+    capPtr <- rureCapturesNew rp
+    castForeignPtr <$> newForeignPtr rureCapturesFree (castPtr capPtr)
+
+-- | @since 0.1.2.0
+captures :: RurePtr
+         -> BS.ByteString
+         -> CSize -- ^ Index (for captures)
+         -> IO [RureMatch]
+captures re haystack ix = do
+    capPtr <- allocCapPtr re
+    reIPtr <- mkIter re
+    capturesLoop capPtr reIPtr haystack ix
+
+capturesLoop :: RureCapturesPtr -- ^ For results
+             -> RureIterPtr
+             -> BS.ByteString
+             -> CSize -- ^ Index (captures)
+             -> IO [RureMatch]
+capturesLoop capPtr reIPtr haystack ix = do
+    res <- iterNextCaptures capPtr reIPtr haystack ix
+    case res of
+        Nothing -> pure []
+        Just m -> (m :) <$> capturesLoop capPtr reIPtr haystack ix
+
+iterNextCaptures :: RureCapturesPtr -- ^ For results
+                 -> RureIterPtr
+                 -> BS.ByteString
+                 -> CSize -- ^ Index (captures)
+                 -> IO (Maybe RureMatch)
+iterNextCaptures capPtr reIPtr haystack ix = do
+    res <- BS.unsafeUseAsCStringLen haystack $ \(p, sz) ->
+        rureIterNextCaptures reIPtr (castPtr p) (fromIntegral sz) capPtr
+    if res
+        then capturesAt capPtr ix
+        else pure Nothing
+
+-- | @since 0.1.2.0
+findCaptures :: RurePtr
+             -> BS.ByteString
+             -> CSize -- ^ Index (captures)
+             -> CSize -- ^ Start
+             -> IO (Maybe RureMatch)
+findCaptures rp haystack ix start' = do
+    capFp <- allocCapPtr rp
+    res <- BS.unsafeUseAsCStringLen haystack $ \(p, sz) ->
+        rureFindCaptures rp (castPtr p) (fromIntegral sz) start' capFp
+    if res
+        then capturesAt capFp ix
+        else pure Nothing
 
 find :: RurePtr
      -> BS.ByteString -- ^ Unicode
diff --git a/test/Spec.hs b/test/Spec.hs
--- a/test/Spec.hs
+++ b/test/Spec.hs
@@ -3,6 +3,7 @@
 module Main (main) where
 
 import qualified Data.ByteString  as BS
+import           Foreign.C.Types  (CSize)
 import           Regex.Rure
 import           Test.Tasty
 import           Test.Tasty.HUnit
@@ -23,7 +24,44 @@
                 , RureMatch 11 21
                 , RureMatch 22 32
                 ]
+        , testCase "captures year" $
+            captures' "(\\d{4})-(\\d{2})-(\\d{2})" "2021-03-14" 1 (RureMatch 0 4)
+        , testCase "captures month" $
+            captures' "(\\d{4})-(\\d{2})-(\\d{2})" "2021-03-14" 2 (RureMatch 5 7)
+        , testCase "Matches captures" $
+            capturess "(\\d{4})-(\\d{2})-(\\d{2})" "2012-03-14 2013-01-01 2014-07-05" 1
+                [ RureMatch 0 4
+                , RureMatch 11 15
+                , RureMatch 22 26
+                ]
+        , testCase "Matches captures" $
+            capturess "(\\d{4})-(\\d{2})-(\\d{2})" "2012-03-14 2013-01-01 2014-07-05" 2
+                [ RureMatch 5 7
+                , RureMatch 16 18
+                , RureMatch 27 29
+                ]
         ]
+
+capturess :: BS.ByteString
+          -> BS.ByteString -- ^ Haystack
+          -> CSize -- ^ Index
+          -> [RureMatch]
+          -> Assertion
+capturess re haystack ix expected = do
+    (Right rp) <- compile rureDefaultFlags re
+    actual <- captures rp haystack ix
+    actual @?= expected
+
+captures' :: BS.ByteString
+          -> BS.ByteString -- ^ Haystack
+          -> CSize -- ^ Index
+          -> RureMatch
+          -> Assertion
+captures' re haystack ix expected = do
+    (Right rp) <- compile rureDefaultFlags re
+    actual <- findCaptures rp haystack ix 0
+    actual @?= Just expected
+
 
 matchesAt :: BS.ByteString
           -> BS.ByteString
