diff --git a/CHANGELOG.markdown b/CHANGELOG.markdown
--- a/CHANGELOG.markdown
+++ b/CHANGELOG.markdown
@@ -1,3 +1,8 @@
+1.5.0
+=====
+
+  * Streamlined API.
+
 1.4.3
 =====
 
diff --git a/aeson-match-qq.cabal b/aeson-match-qq.cabal
--- a/aeson-match-qq.cabal
+++ b/aeson-match-qq.cabal
@@ -5,7 +5,7 @@
 -- see: https://github.com/sol/hpack
 
 name:           aeson-match-qq
-version:        1.4.3
+version:        1.5.0
 synopsis:       Declarative JSON matchers.
 description:    See README.markdown
 category:       Web
diff --git a/src/Aeson/Match/QQ.hs b/src/Aeson/Match/QQ.hs
--- a/src/Aeson/Match/QQ.hs
+++ b/src/Aeson/Match/QQ.hs
@@ -1,5 +1,14 @@
 module Aeson.Match.QQ
-  ( Value(..)
+  ( match
+  , qq
+
+  , Error(..)
+  , Mismatch(..)
+  , MissingPathElem(..)
+  , ExtraArrayValues(..)
+  , ExtraObjectValues(..)
+
+  , Value(..)
   , Array
   , Object
   , Box(..)
@@ -8,14 +17,6 @@
   , Nullable(..)
   , Path
   , PathElem(..)
-  , parse
-  , qq
-  , match
-  , mismatch
-  , mistype
-  , missingPathElem
-  , extraArrayValues
-  , extraObjectValues
   ) where
 
 import           Data.String (IsString(..))
@@ -24,14 +25,14 @@
 import           Language.Haskell.TH.Syntax (Lift(..))
 
 import           Aeson.Match.QQ.Internal.Match
-  ( Path
+  ( match
+  , Error(..)
+  , Mismatch(..)
+  , MissingPathElem(..)
+  , ExtraArrayValues(..)
+  , ExtraObjectValues(..)
+  , Path
   , PathElem(..)
-  , match
-  , mismatch
-  , mistype
-  , missingPathElem
-  , extraArrayValues
-  , extraObjectValues
   )
 import           Aeson.Match.QQ.Internal.Parse (parse)
 import           Aeson.Match.QQ.Internal.Value
diff --git a/src/Aeson/Match/QQ/Internal/Match.hs b/src/Aeson/Match/QQ/Internal/Match.hs
--- a/src/Aeson/Match/QQ/Internal/Match.hs
+++ b/src/Aeson/Match/QQ/Internal/Match.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE CPP #-}
 {-# LANGUAGE ApplicativeDo #-}
 {-# LANGUAGE DuplicateRecordFields #-}
 {-# LANGUAGE LambdaCase #-}
@@ -5,16 +6,31 @@
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE RecordWildCards #-}
 {-# LANGUAGE ViewPatterns #-}
-module Aeson.Match.QQ.Internal.Match where
+module Aeson.Match.QQ.Internal.Match
+  ( match
+  , Error(..)
+  , Mismatch(..)
+  , MissingPathElem(..)
+  , ExtraArrayValues(..)
+  , ExtraObjectValues(..)
+  , Path
+  , PathElem(..)
+  ) where
 
 import           Control.Applicative (liftA2)
 import           Control.Monad (unless)
 import           Data.Aeson ((.=))
 import qualified Data.Aeson as Aeson
+#if MIN_VERSION_aeson(2,0,0)
 import qualified Data.Aeson.KeyMap as Aeson (toHashMapText)
+#endif
 import           Data.Bool (bool)
 import qualified Data.CaseInsensitive as CI
-import           Data.Either.Validation (Validation(..), eitherToValidation)
+import           Data.Either.Validation
+  ( Validation(..)
+  , eitherToValidation
+  , validationToEither
+  )
 import           Data.Foldable (for_, toList)
 import           Data.HashMap.Strict (HashMap)
 import qualified Data.HashMap.Strict as HashMap
@@ -37,12 +53,17 @@
   )
 
 
+-- | Test if a matcher matches a 'Aeson.Value'.
 match
   :: Value Aeson.Value
+     -- ^ A 'qq`-created 'Value'
   -> Aeson.Value
-  -> Validation (NonEmpty VE) (HashMap Text Aeson.Value)
-match =
-  go []
+     -- ^ A 'Value' from aeson
+  -> Either (NonEmpty Error) (HashMap Text Aeson.Value)
+     -- ^ Either a non-empty list of errors, or a mapping
+     -- from _holes to their values.
+match matcher0 given0 =
+  validationToEither (go [] matcher0 given0)
  where
   go path matcher given = do
     let mismatched = mismatch (reverse path) matcher given
@@ -103,7 +124,14 @@
       (ArrayUO _, _) -> do
         mistyped
         pure mempty
-      (Object Box {knownValues, extendable}, Aeson.Object (Aeson.toHashMapText -> o)) ->
+      ( Object Box {knownValues, extendable}
+#if MIN_VERSION_aeson(2,0,0)
+        , Aeson.Object (Aeson.toHashMapText -> o)
+#else
+        , Aeson.Object o
+#endif
+
+        ) ->
         let fold f =
               HashMap.foldrWithKey (\k v a -> liftA2 HashMap.union a (f k v)) (pure mempty)
             extraValues =
@@ -135,11 +163,11 @@
     (_, _) -> False
 
 matchArrayUO
-  :: Validation (NonEmpty VE) (HashMap Text Aeson.Value)
+  :: Validation (NonEmpty Error) (HashMap Text Aeson.Value)
   -> Path
   -> Box (Vector (Value Aeson.Value))
   -> Vector Aeson.Value
-  -> Validation (NonEmpty VE) (HashMap Text Aeson.Value)
+  -> Validation (NonEmpty Error) (HashMap Text Aeson.Value)
 matchArrayUO mismatched path Box {knownValues, extendable} xs = do
   -- Collect possible indices in `xs` for each position in `knownValues`.
   let indices = map (collectMatchingIndices (toList xs)) (toList knownValues)
@@ -169,10 +197,10 @@
    where
     matchingIndex i x =
       case match knownValue x of
-        Success vs ->
-          Just (i, vs)
-        Failure _ ->
+        Left _ ->
           Nothing
+        Right vs ->
+          Just (i, vs)
   allIndicesAssignments = map (map unI) . cleanUp . go Set.empty
    where
     go _ [] = [[]]
@@ -192,23 +220,23 @@
   I (a, _) `compare` I (b, _) =
     a `compare` b
 
-mismatch :: Path -> Value Aeson.Value -> Aeson.Value -> Validation (NonEmpty VE) a
+mismatch :: Path -> Value Aeson.Value -> Aeson.Value -> Validation (NonEmpty Error) a
 mismatch path matcher given =
   throwE (Mismatch MkMismatch {..})
 
-mistype :: Path -> Value Aeson.Value -> Aeson.Value -> Validation (NonEmpty VE) a
+mistype :: Path -> Value Aeson.Value -> Aeson.Value -> Validation (NonEmpty Error) a
 mistype path matcher given =
   throwE (Mistype MkMismatch {..})
 
-missingPathElem :: Path -> PathElem -> Validation (NonEmpty VE) a
+missingPathElem :: Path -> PathElem -> Validation (NonEmpty Error) a
 missingPathElem path missing =
   throwE (MissingPathElem MkMissingPathElem {..})
 
-extraArrayValues :: Path -> Vector Aeson.Value -> Validation (NonEmpty VE) a
+extraArrayValues :: Path -> Vector Aeson.Value -> Validation (NonEmpty Error) a
 extraArrayValues path values =
   throwE (ExtraArrayValues MkExtraArrayValues {..})
 
-extraObjectValues :: Path -> HashMap Text Aeson.Value -> Validation (NonEmpty VE) a
+extraObjectValues :: Path -> HashMap Text Aeson.Value -> Validation (NonEmpty Error) a
 extraObjectValues path values =
   throwE (ExtraObjectValues MkExtraObjectValues {..})
 
@@ -216,7 +244,7 @@
 throwE =
   eitherToValidation . Left . pure
 
-data VE
+data Error
   = Mismatch Mismatch
   | Mistype Mismatch
   | MissingPathElem MissingPathElem
@@ -224,7 +252,7 @@
   | ExtraObjectValues ExtraObjectValues
     deriving (Show, Eq)
 
-instance Aeson.ToJSON VE where
+instance Aeson.ToJSON Error where
   toJSON =
     Aeson.object . \case
       Mismatch v ->
diff --git a/src/Aeson/Match/QQ/Internal/Value.hs b/src/Aeson/Match/QQ/Internal/Value.hs
--- a/src/Aeson/Match/QQ/Internal/Value.hs
+++ b/src/Aeson/Match/QQ/Internal/Value.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE CPP #-}
 {-# LANGUAGE DeriveLift #-}
 {-# LANGUAGE LambdaCase #-}
 {-# LANGUAGE NamedFieldPuns #-}
@@ -20,7 +21,9 @@
 
 import           Data.Aeson ((.=))
 import qualified Data.Aeson as Aeson
+#if MIN_VERSION_aeson(2,0,0)
 import qualified Data.Aeson.KeyMap as Aeson (toHashMapText)
+#endif
 import qualified Data.Aeson.Encoding.Internal as Aeson (encodingToLazyByteString)
 import           Data.CaseInsensitive (CI)
 import qualified Data.CaseInsensitive as CI
@@ -212,5 +215,9 @@
     String n
   Aeson.Array xs ->
     Array Box {knownValues = fmap embed xs, extendable = False}
+#if MIN_VERSION_aeson(2,0,0)
   Aeson.Object (Aeson.toHashMapText -> o) ->
+#else
+  Aeson.Object o ->
+#endif
     Object Box {knownValues = fmap embed o, extendable = False}
diff --git a/test/Aeson/Match/QQSpec.hs b/test/Aeson/Match/QQSpec.hs
--- a/test/Aeson/Match/QQSpec.hs
+++ b/test/Aeson/Match/QQSpec.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE DuplicateRecordFields #-}
 {-# LANGUAGE GeneralisedNewtypeDeriving #-}
 {-# LANGUAGE OverloadedLists #-}
 {-# LANGUAGE OverloadedStrings #-}
@@ -6,6 +7,7 @@
 
 import qualified Data.Aeson as Aeson
 import           Data.Aeson.QQ (aesonQQ)
+import           Data.List.NonEmpty (NonEmpty)
 import qualified Data.HashMap.Strict as HashMap
 import           Test.Hspec
 
@@ -117,7 +119,11 @@
 
     it "paths" $ do
       match [qq| {foo: {bar: {baz: [1, 4]}}} |] [aesonQQ| {foo: {bar: {baz: [1, 7]}}} |] `shouldBe`
-        mismatch ["foo", "bar", "baz", Idx 1] [qq| 4 |] [aesonQQ| 7 |]
+        throwE (Mismatch MkMismatch
+          { path = ["foo", "bar", "baz", Idx 1]
+          , matcher = [qq| 4 |]
+          , given = [aesonQQ| 7 |]
+          })
 
     it "named holes" $ do
       match [qq| {foo: _hole} |] [aesonQQ| {foo: {bar: {baz: [1, 4]}}} |] `shouldBe`
@@ -133,9 +139,15 @@
     -- https://github.com/supki/aeson-match-qq/issues/7
     it "#7" $ do
       match [qq| {foo: _} |] [aesonQQ| {} |] `shouldBe`
-        missingPathElem [] "foo"
+        throwE (MissingPathElem MkMissingPathElem
+          { path = []
+          , missing = "foo"
+          })
       match [qq| [_] |] [aesonQQ| [] |] `shouldBe`
-        missingPathElem [] (Idx 0)
+        throwE (MissingPathElem MkMissingPathElem
+          { path = []
+          , missing = Idx 0
+          })
 
     -- https://github.com/supki/aeson-match-qq/issues/10
     it "#10" $ do
@@ -160,16 +172,32 @@
     it "#18" $ do
       -- string ~ string
       match [qq| "foo" |] [aesonQQ| "bar" |] `shouldBe`
-        mismatch [] (String "foo") (Aeson.String "bar")
+        throwE (Mismatch MkMismatch
+          { path = []
+          , matcher = String "foo"
+          , given = Aeson.String "bar"
+          })
       -- string !~ number
       match [qq| "foo" |] [aesonQQ| 4 |] `shouldBe`
-        mistype [] (String "foo") (Aeson.Number 4)
+        throwE (Mistype MkMismatch
+          { path = []
+          , matcher = String "foo"
+          , given = Aeson.Number 4
+          })
       -- string !~ null
       match [qq| "foo" |] [aesonQQ| null |] `shouldBe`
-        mistype [] (String "foo") Aeson.Null
+        throwE (Mistype MkMismatch
+          { path = []
+          , matcher = String "foo"
+          , given = Aeson.Null
+          })
       -- null !~ number
       match [qq| null |] [aesonQQ| 4 |] `shouldBe`
-        mistype [] Null (Aeson.Number 4)
+        throwE (Mistype MkMismatch
+          { path = []
+          , matcher = Null
+          , given = Aeson.Number 4
+          })
 
 newtype ToEncoding a = ToEncoding { unToEncoding :: a }
     deriving (Show, Eq, Num)
@@ -187,3 +215,7 @@
 shouldNotMatch :: Value Aeson.Value -> Aeson.Value -> Expectation
 shouldNotMatch a b =
   match a b `shouldNotBe` pure mempty
+
+throwE :: e -> Either (NonEmpty e) a
+throwE =
+  Left . pure
