diff --git a/src/lib.rs b/src/lib.rs
index baaef7194d09247dd69551013dcec6792cd2976d..dbefb59c3366908a33d6e716adeb8b60f4d7c925 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,8 +1,9 @@
 #![doc = include_str!("../README.md")]
 
 pub mod query;
+pub mod result;
 
-use prometheus_http_query::{response::PromqlResult, Client, Error};
+use prometheus_http_query::{response::PromqlResult, Client};
 use query::IntoQuery;
 use reqwest::header::HeaderValue;
 use url::Url;
@@ -65,7 +66,7 @@ impl QueryManager {
     /// The trait is implemented for `&str`, `String`, `&String`, and __mquery's__
     /// internal types such as [`query::Metric`], [`query::Scalar`] and others that result
     /// from the query builder API.
-    pub async fn query<Q: IntoQuery>(&self, query: Q) -> Result<PromqlResult, Error> {
+    pub async fn query<Q: IntoQuery>(&self, query: Q) -> Result<PromqlResult, result::Error> {
         let mut builder = self.client.query(query.into_query().as_ref());
         if let Some((name, val)) = self.auth.get_header() {
             builder = builder.header(name, val);
@@ -74,8 +75,8 @@ impl QueryManager {
             builder = builder.timeout(*timeout);
         }
         match self.method {
-            Method::Get => builder.get().await,
-            Method::Post => builder.post().await,
+            Method::Get => builder.get().await.map_err(|e| e.into()),
+            Method::Post => builder.post().await.map_err(|e| e.into()),
         }
     }
 
@@ -91,7 +92,7 @@ impl QueryManager {
         start: i64,
         end: i64,
         step: f64,
-    ) -> Result<PromqlResult, Error> {
+    ) -> Result<PromqlResult, result::Error> {
         let mut builder = self
             .client
             .query_range(query.into_query().as_ref(), start, end, step);
@@ -102,8 +103,8 @@ impl QueryManager {
             builder = builder.timeout(*timeout);
         }
         match self.method {
-            Method::Get => builder.get().await,
-            Method::Post => builder.post().await,
+            Method::Get => builder.get().await.map_err(|e| e.into()),
+            Method::Post => builder.post().await.map_err(|e| e.into()),
         }
     }
 }
diff --git a/src/result.rs b/src/result.rs
new file mode 100644
index 0000000000000000000000000000000000000000..fac15e8d4d135b0cf4ae5d7a4c2453ae008fa62a
--- /dev/null
+++ b/src/result.rs
@@ -0,0 +1,189 @@
+//! `PromqlResult` and the `Error` types which are essentially clones of the types
+//! with the same name from the `prometheus-http-query` crate with slight modifications
+//! to suit the needs of `mqeury`.
+//!
+//! The reason why this crate maintains its own types which are the same (almost) as the ones
+//! provided by the other crate that is used internally, is to ensure that the public
+//! API remains stable even if the internal crate API changes.
+
+use prometheus_http_query::error;
+use std::fmt::{self, Display};
+
+/// A global error enum that contains all errors that are returned by this
+/// library. Some errors are wrappers for errors from underlying libraries.
+/// All errors (this enum as well as all contained structs) implement [`std::error::Error`].
+#[non_exhaustive]
+#[derive(Debug, Clone)]
+pub enum Error {
+    /// Wraps errors from the underlying [`reqwest::Client`] that cannot be mapped
+    /// to a more specific error type. Deserialization errors also fall into this
+    /// category.
+    Client(ClientError),
+    /// Occurs when Prometheus responds with e.g. HTTP 4xx (e.g. due to a syntax error in a PromQL query).<br>
+    /// Details on the error as reported by Prometheus are included in [`PrometheusError`].
+    Prometheus(PrometheusError),
+    /// Occurs when the [`Client::series`](crate::Client::series) method is called with an empty set of
+    /// series [`Selector`](crate::selector::Selector)s. According to the Prometheus API description at least one
+    /// [`Selector`](crate::selector::Selector) must be provided.
+    EmptySeriesSelector,
+}
+
+impl Display for Error {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            Self::Client(e) => e.fmt(f),
+            Self::Prometheus(e) => e.fmt(f),
+            Self::EmptySeriesSelector => f.write_str("at least one series selector must be provided in order to query the series endpoint"),
+        }
+    }
+}
+
+impl std::error::Error for Error {
+    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+        match self {
+            Self::Client(e) => e.source(),
+            Self::Prometheus(p) => p.source(),
+            Self::EmptySeriesSelector => None,
+        }
+    }
+}
+
+impl From<prometheus_http_query::Error> for Error {
+    fn from(value: prometheus_http_query::Error) -> Self {
+        match value {
+            prometheus_http_query::Error::Client(c) => Self::Client(c.into()),
+            prometheus_http_query::Error::Prometheus(p) => Self::Prometheus(p.into()),
+            prometheus_http_query::Error::EmptySeriesSelector => Self::EmptySeriesSelector,
+            _ => unreachable!(),
+        }
+    }
+}
+
+/// This error is thrown when the JSON response's `status` field contains `error`.<br>
+/// The error-related information from the JSON body is included in this error.
+#[derive(Debug, Clone, PartialEq)]
+pub struct PrometheusError {
+    pub(crate) error_type: PrometheusErrorType,
+    pub(crate) message: String,
+}
+
+impl std::error::Error for PrometheusError {}
+
+impl fmt::Display for PrometheusError {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "{}: {}", self.error_type, self.message)
+    }
+}
+
+impl From<error::PrometheusError> for PrometheusError {
+    fn from(value: error::PrometheusError) -> Self {
+        let error_type = PrometheusErrorType::from(value.error_type());
+        let message = value.message().to_owned();
+        PrometheusError {
+            error_type,
+            message,
+        }
+    }
+}
+
+impl PrometheusError {
+    /// Returns the parsed version of the error type that was given by the Prometheus API.
+    pub fn error_type(&self) -> PrometheusErrorType {
+        self.error_type
+    }
+
+    /// Returns the error message that was given by the Prometheus API.
+    pub fn message(&self) -> &str {
+        &self.message
+    }
+
+    pub fn is_timeout(&self) -> bool {
+        self.error_type == PrometheusErrorType::Timeout
+    }
+
+    pub fn is_canceled(&self) -> bool {
+        self.error_type == PrometheusErrorType::Canceled
+    }
+
+    pub fn is_execution(&self) -> bool {
+        self.error_type == PrometheusErrorType::Execution
+    }
+
+    pub fn is_bad_data(&self) -> bool {
+        self.error_type == PrometheusErrorType::BadData
+    }
+
+    pub fn is_internal(&self) -> bool {
+        self.error_type == PrometheusErrorType::Internal
+    }
+
+    pub fn is_unavailable(&self) -> bool {
+        self.error_type == PrometheusErrorType::Unavailable
+    }
+
+    pub fn is_not_found(&self) -> bool {
+        self.error_type == PrometheusErrorType::NotFound
+    }
+}
+
+/// The parsed error type as returned by the Prometheus API.
+#[non_exhaustive]
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub enum PrometheusErrorType {
+    Timeout,
+    Canceled,
+    Execution,
+    BadData,
+    Internal,
+    Unavailable,
+    NotFound,
+}
+
+impl From<error::PrometheusErrorType> for PrometheusErrorType {
+    fn from(value: error::PrometheusErrorType) -> Self {
+        match value {
+            error::PrometheusErrorType::Timeout => PrometheusErrorType::Timeout,
+            error::PrometheusErrorType::Canceled => PrometheusErrorType::Canceled,
+            error::PrometheusErrorType::Execution => PrometheusErrorType::Execution,
+            error::PrometheusErrorType::BadData => PrometheusErrorType::BadData,
+            error::PrometheusErrorType::Internal => PrometheusErrorType::Internal,
+            error::PrometheusErrorType::Unavailable => PrometheusErrorType::Unavailable,
+            error::PrometheusErrorType::NotFound => PrometheusErrorType::NotFound,
+            _ => unreachable!(),
+        }
+    }
+}
+
+impl fmt::Display for PrometheusErrorType {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            Self::Timeout => f.write_str("timeout"),
+            Self::Canceled => f.write_str("canceled"),
+            Self::Execution => f.write_str("execution"),
+            Self::BadData => f.write_str("bad_data"),
+            Self::Internal => f.write_str("internal"),
+            Self::Unavailable => f.write_str("unavailable"),
+            Self::NotFound => f.write_str("not_found"),
+        }
+    }
+}
+
+/// Is thrown when the [`Client`](crate::Client) or the underlying
+/// [`reqwest::Error`] fail to build or execute a request.
+#[derive(Debug, Clone)]
+pub struct ClientError(String);
+
+impl From<prometheus_http_query::error::ClientError> for ClientError {
+    fn from(value: prometheus_http_query::error::ClientError) -> Self {
+        let message = value.to_string();
+        ClientError(message)
+    }
+}
+
+impl fmt::Display for ClientError {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        f.write_str(&self.0)
+    }
+}
+
+impl std::error::Error for ClientError {}
diff --git a/tests/utils.rs b/tests/utils.rs
index dbaa3a86db7e4f85e5c5b53bf574c6e2e51ef0e0..12303de51edafc2c0e3f8d15f41f13717162b6f9 100644
--- a/tests/utils.rs
+++ b/tests/utils.rs
@@ -1,9 +1,9 @@
 use mquery::{query::IntoQuery, QueryManager};
-use prometheus_http_query::{response::PromqlResult, Error};
+use prometheus_http_query::response::PromqlResult;
 
 // local victoria metrics server
 pub static URL: &str = "http://localhost:8428";
 
-pub async fn send_query(query: impl IntoQuery) -> Result<PromqlResult, Error> {
+pub async fn send_query(query: impl IntoQuery) -> Result<PromqlResult, mquery::result::Error> {
     QueryManager::new(URL.parse().unwrap()).query(query).await
 }