diff --git a/Cargo.lock b/Cargo.lock
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -10,7 +10,7 @@
  "eyre",
  "http 1.2.0",
  "stable-eyre",
- "thiserror 2.0.4",
+ "thiserror 2.0.12",
 ]
 
 [[package]]
@@ -166,7 +166,7 @@
  "log",
  "pin-project-lite",
  "tokio",
- "tungstenite",
+ "tungstenite 0.24.0",
 ]
 
 [[package]]
@@ -271,7 +271,7 @@
  "sha1",
  "sync_wrapper 1.0.2",
  "tokio",
- "tokio-tungstenite",
+ "tokio-tungstenite 0.24.0",
  "tower 0.5.1",
  "tower-layer",
  "tower-service",
@@ -484,6 +484,7 @@
  "serde_json",
  "thiserror 1.0.69",
  "tokio",
+ "tokio-tungstenite 0.26.2",
 ]
 
 [[package]]
@@ -513,7 +514,7 @@
  "serde",
  "serde_json",
  "sha2",
- "thiserror 2.0.4",
+ "thiserror 2.0.12",
 ]
 
 [[package]]
@@ -569,7 +570,7 @@
  "itertools 0.10.5",
  "pretty_assertions",
  "serde",
- "thiserror 2.0.4",
+ "thiserror 2.0.12",
 ]
 
 [[package]]
@@ -706,7 +707,7 @@
  "seahash",
  "serde",
  "tempdir",
- "thiserror 2.0.4",
+ "thiserror 2.0.12",
  "topo_sort",
 ]
 
@@ -737,7 +738,7 @@
  "prost",
  "rustls",
  "serde_json",
- "thiserror 2.0.4",
+ "thiserror 2.0.12",
  "tokio",
  "tower-http 0.5.2",
  "tower-service",
@@ -765,7 +766,7 @@
  "prost",
  "prost-build",
  "tempdir",
- "thiserror 2.0.4",
+ "thiserror 2.0.12",
  "tokio",
 ]
 
@@ -809,7 +810,7 @@
  "pyo3",
  "serde",
  "tempdir",
- "thiserror 2.0.4",
+ "thiserror 2.0.12",
  "toml 0.8.19",
  "versions",
 ]
@@ -845,7 +846,7 @@
  "chronik-util",
  "cxx",
  "cxx-build",
- "thiserror 2.0.4",
+ "thiserror 2.0.12",
  "tokio",
 ]
 
@@ -1151,7 +1152,7 @@
  "ecash-secp256k1",
  "ripemd",
  "sha2",
- "thiserror 2.0.4",
+ "thiserror 2.0.12",
  "wasm-bindgen",
 ]
 
@@ -1162,7 +1163,7 @@
  "bincode 1.3.3",
  "bitcoin_hashes 0.14.0",
  "ecash-secp256k1-sys",
- "getrandom",
+ "getrandom 0.2.15",
  "hex_lit",
  "rand 0.8.5",
  "rand_core 0.6.4",
@@ -1279,7 +1280,7 @@
  "regex",
  "serde",
  "serde_json",
- "thiserror 2.0.4",
+ "thiserror 2.0.12",
  "tokio",
  "toml 0.5.11",
  "tower-http 0.3.5",
@@ -1488,10 +1489,22 @@
  "cfg-if",
  "js-sys",
  "libc",
- "wasi",
+ "wasi 0.11.0+wasi-snapshot-preview1",
  "wasm-bindgen",
 ]
 
+[[package]]
+name = "getrandom"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "r-efi",
+ "wasi 0.14.2+wasi-0.2.4",
+]
+
 [[package]]
 name = "gimli"
 version = "0.28.1"
@@ -2304,7 +2317,7 @@
 checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
 dependencies = [
  "libc",
- "wasi",
+ "wasi 0.11.0+wasi-snapshot-preview1",
  "windows-sys 0.52.0",
 ]
 
@@ -2571,7 +2584,7 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
 dependencies = [
- "zerocopy",
+ "zerocopy 0.7.35",
 ]
 
 [[package]]
@@ -2780,6 +2793,12 @@
  "proc-macro2",
 ]
 
+[[package]]
+name = "r-efi"
+version = "5.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
+
 [[package]]
 name = "radium"
 version = "0.5.3"
@@ -2806,10 +2825,21 @@
 checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
 dependencies = [
  "libc",
- "rand_chacha",
+ "rand_chacha 0.3.1",
  "rand_core 0.6.4",
 ]
 
+[[package]]
+name = "rand"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94"
+dependencies = [
+ "rand_chacha 0.9.0",
+ "rand_core 0.9.3",
+ "zerocopy 0.8.24",
+]
+
 [[package]]
 name = "rand_chacha"
 version = "0.3.1"
@@ -2820,6 +2850,16 @@
  "rand_core 0.6.4",
 ]
 
+[[package]]
+name = "rand_chacha"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
+dependencies = [
+ "ppv-lite86",
+ "rand_core 0.9.3",
+]
+
 [[package]]
 name = "rand_core"
 version = "0.3.1"
@@ -2841,7 +2881,16 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
 dependencies = [
- "getrandom",
+ "getrandom 0.2.15",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
+dependencies = [
+ "getrandom 0.3.2",
 ]
 
 [[package]]
@@ -2868,7 +2917,7 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43"
 dependencies = [
- "getrandom",
+ "getrandom 0.2.15",
  "libredox",
  "thiserror 1.0.69",
 ]
@@ -2959,7 +3008,7 @@
 dependencies = [
  "cc",
  "cfg-if",
- "getrandom",
+ "getrandom 0.2.15",
  "libc",
  "spin",
  "untrusted",
@@ -3489,11 +3538,11 @@
 
 [[package]]
 name = "thiserror"
-version = "2.0.4"
+version = "2.0.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2f49a1853cf82743e3b7950f77e0f4d622ca36cf4317cba00c767838bac8d490"
+checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
 dependencies = [
- "thiserror-impl 2.0.4",
+ "thiserror-impl 2.0.12",
 ]
 
 [[package]]
@@ -3509,9 +3558,9 @@
 
 [[package]]
 name = "thiserror-impl"
-version = "2.0.4"
+version = "2.0.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8381894bb3efe0c4acac3ded651301ceee58a15d47c2e34885ed1908ad667061"
+checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -3586,7 +3635,19 @@
  "futures-util",
  "log",
  "tokio",
- "tungstenite",
+ "tungstenite 0.24.0",
+]
+
+[[package]]
+name = "tokio-tungstenite"
+version = "0.26.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a9daff607c6d2bf6c16fd681ccb7eecc83e4e2cdc1ca067ffaadfca5de7f084"
+dependencies = [
+ "futures-util",
+ "log",
+ "tokio",
+ "tungstenite 0.26.2",
 ]
 
 [[package]]
@@ -3780,6 +3841,23 @@
  "utf-8",
 ]
 
+[[package]]
+name = "tungstenite"
+version = "0.26.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4793cb5e56680ecbb1d843515b23b6de9a75eb04b66643e256a396d43be33c13"
+dependencies = [
+ "bytes",
+ "data-encoding",
+ "http 1.2.0",
+ "httparse",
+ "log",
+ "rand 0.9.0",
+ "sha1",
+ "thiserror 2.0.12",
+ "utf-8",
+]
+
 [[package]]
 name = "typenum"
 version = "1.17.0"
@@ -3900,6 +3978,15 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
 
+[[package]]
+name = "wasi"
+version = "0.14.2+wasi-0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
+dependencies = [
+ "wit-bindgen-rt",
+]
+
 [[package]]
 name = "wasm-bindgen"
 version = "0.2.92"
@@ -4220,6 +4307,15 @@
  "windows-sys 0.48.0",
 ]
 
+[[package]]
+name = "wit-bindgen-rt"
+version = "0.39.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
+dependencies = [
+ "bitflags 2.6.0",
+]
+
 [[package]]
 name = "write16"
 version = "1.0.0"
@@ -4275,7 +4371,16 @@
 checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
 dependencies = [
  "byteorder",
- "zerocopy-derive",
+ "zerocopy-derive 0.7.35",
+]
+
+[[package]]
+name = "zerocopy"
+version = "0.8.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879"
+dependencies = [
+ "zerocopy-derive 0.8.24",
 ]
 
 [[package]]
@@ -4289,6 +4394,17 @@
  "syn 2.0.90",
 ]
 
+[[package]]
+name = "zerocopy-derive"
+version = "0.8.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a996a8f63c5c4448cd959ac1bab0aaa3306ccfd060472f85943ee0750f0169be"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.90",
+]
+
 [[package]]
 name = "zerofrom"
 version = "0.1.5"
diff --git a/modules/bitcoinsuite-chronik-client/Cargo.toml b/modules/bitcoinsuite-chronik-client/Cargo.toml
--- a/modules/bitcoinsuite-chronik-client/Cargo.toml
+++ b/modules/bitcoinsuite-chronik-client/Cargo.toml
@@ -39,6 +39,7 @@
 
 # Serializes and deserializes Rust data
 serde = "1.0.217"
+tokio-tungstenite = "0.26.2"
 
 [build-dependencies]
 # Build Protobuf structs
diff --git a/modules/bitcoinsuite-chronik-client/src/failover_proxy.rs b/modules/bitcoinsuite-chronik-client/src/failover_proxy.rs
new file mode 100644
--- /dev/null
+++ b/modules/bitcoinsuite-chronik-client/src/failover_proxy.rs
@@ -0,0 +1,173 @@
+// Copyright (c) 2023-2024 The Bitcoin developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+use abc_rust_error::Report;
+use bytes::Bytes;
+use reqwest::{Client, Method, Response};
+use tokio_tungstenite::WebSocketStream;
+
+const WEBSOCKET_TIMEOUT_MS: u64 = 5000;
+
+#[derive(Debug, Clone)]
+pub struct Endpoint {
+    pub url: String,
+    pub ws_url: String,
+}
+
+// Handles the networking to Chronik `Endpoint`s, including cycling
+// through both types of endpoints.
+pub struct FailoverProxy {
+    endpoint_array: Vec<Endpoint>,
+    working_index: usize,
+}
+
+impl FailoverProxy {
+    pub fn new(urls: impl Into<Vec<String>>) -> Result<Self, Report> {
+        let urls_vec = urls.into();
+
+        if urls_vec.is_empty() {
+            return Err(Report::msg("Url array must not be empty"));
+        }
+
+        // Validate each URL
+        for url in &urls_vec {
+            if url.ends_with('/') {
+                return Err(Report::msg(format!(
+                    "`url` cannot end with '/', got: {}",
+                    url
+                )));
+            }
+
+            if !url.starts_with("https://") && !url.starts_with("http://") {
+                return Err(Report::msg(format!(
+                    "`url` must start with 'https://' or 'http://', got: {}",
+                    url
+                )));
+            }
+        }
+
+        // Convert URLs to endpoints
+        let endpoint_array = Self::append_ws_urls(urls_vec);
+
+        // Create and return the FailoverProxy
+        Ok(Self {
+            endpoint_array,
+            working_index: 0,
+        })
+    }
+
+    pub fn get_endpoint_array(&self) -> &[Endpoint] {
+        &self.endpoint_array
+    }
+
+    // Derives the endpoint array index based on working_index
+    pub fn derive_endpoint_index(&self, loop_index: usize) -> usize {
+        (self.working_index + loop_index) % self.endpoint_array.len()
+    }
+
+    pub fn set_working_index(&mut self, new_index: usize) {
+        self.working_index = new_index;
+    }
+
+    // Converts an array of chronik http/https urls into websocket equivalents
+    pub fn append_ws_urls(urls: Vec<String>) -> Vec<Endpoint> {
+        urls.into_iter()
+            .map(|url| {
+                if url.starts_with("https://") {
+                    Endpoint {
+                        url: url.clone(),
+                        ws_url: format!(
+                            "wss://{}/ws",
+                            &url["https://".len()..]
+                        ),
+                    }
+                } else if url.starts_with("http://") {
+                    Endpoint {
+                        url: url.clone(),
+                        ws_url: format!("ws://{}/ws", &url["http://".len()..]),
+                    }
+                } else {
+                    panic!("Invalid url found in array: {}", url)
+                }
+            })
+            .collect()
+    }
+
+    // Makes a POST request to the Chronik API
+    pub async fn post(
+        &mut self,
+        path: &str,
+        data: Bytes,
+    ) -> Result<Bytes, Report> {
+        self.request(path, Method::POST, Some(data)).await
+    }
+
+    pub async fn get(&mut self, path: &str) -> Result<Bytes, Report> {
+        self.request(path, Method::GET, None).await
+    }
+
+    async fn request(
+        &mut self,
+        path: &str,
+        method: Method,
+        data: Option<Bytes>,
+    ) -> Result<Bytes, Report> {
+        // TODO: Implement request handling
+        unimplemented!()
+    }
+
+    async fn call_request(
+        &self,
+        client: &Client,
+        url: &str,
+        path: &str,
+        method: Method,
+        data: Option<Bytes>,
+    ) -> Result<Bytes, Report> {
+        // TODO: Implement HTTP request
+        unimplemented!()
+    }
+
+    async fn ensure_response_error_thrown(
+        &self,
+        response: Response,
+        path: &str,
+    ) -> Result<Bytes, Report> {
+        // TODO: Implement error handling
+        unimplemented!()
+    }
+
+    async fn websocket_url_connects(
+        &self,
+        ws_url: &str,
+    ) -> Result<bool, Report> {
+        // TODO: Implement WebSocket connection check
+        unimplemented!()
+    }
+
+    pub async fn connect_ws(
+        &mut self,
+        ws_endpoint: &mut WebSocketStream<tokio::net::TcpStream>,
+    ) -> Result<(), Report> {
+        // TODO: Implement WebSocket connection
+        unimplemented!()
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_append_ws_urls() {
+        // TODO: Implement URL conversion test
+        unimplemented!()
+    }
+
+    #[test]
+    fn test_derive_endpoint_index() {
+        // TODO: Implement endpoint index test
+        unimplemented!()
+    }
+}
diff --git a/modules/bitcoinsuite-chronik-client/src/lib.rs b/modules/bitcoinsuite-chronik-client/src/lib.rs
--- a/modules/bitcoinsuite-chronik-client/src/lib.rs
+++ b/modules/bitcoinsuite-chronik-client/src/lib.rs
@@ -12,6 +12,7 @@
 
 use crate::ChronikClientError::*;
 
+pub mod failover_proxy;
 pub mod handler;
 pub mod test_runner;