diff --git a/Sources/URLRouting/Client/Client.swift b/Sources/URLRouting/Client/Client.swift
index 7311545ddf..97c3b959a6 100644
--- a/Sources/URLRouting/Client/Client.swift
+++ b/Sources/URLRouting/Client/Client.swift
@@ -41,6 +41,20 @@ public struct URLRoutingClient<Route> {
       throw URLRoutingDecodingError(bytes: data, response: response, underlyingError: error)
     }
   }
+
+  /// Allows you to create a client that only handles a subset of routes.
+  ///
+  /// - Parameters:
+  ///    - toRoute: A closure that converts `ChildRoute` back to `Route`.
+  ///
+  /// - Returns: A client that can only request child routes.
+  public func scoped<ChildRoute>(
+    to toRoute: @escaping (ChildRoute) -> Route
+  ) -> URLRoutingClient<ChildRoute> {
+    .init { child in
+      try await self.request(toRoute(child))
+    }
+  }
 }
 
 public struct URLRoutingDecodingError: Error {
diff --git a/Tests/URLRoutingTests/ClientTests.swift b/Tests/URLRoutingTests/ClientTests.swift
new file mode 100644
index 0000000000..09d52287c2
--- /dev/null
+++ b/Tests/URLRoutingTests/ClientTests.swift
@@ -0,0 +1,58 @@
+import Parsing
+import URLRouting
+import XCTest
+
+#if canImport(FoundationNetworking)
+import FoundationNetworking
+#endif
+
+class ClientTests: XCTestCase {
+  enum TestRoute: Equatable {
+    case one
+    case child(ChildRoute)
+  }
+
+  enum ChildRoute: Equatable {
+    case one
+    case two
+  }
+
+  static let router = OneOf {
+    Route(.case(TestRoute.one)) {
+      Path { "one" }
+    }
+    Route(.case(TestRoute.child)) {
+      Path { "child" }
+      OneOf {
+        Route(.case(ChildRoute.one)) {
+          Path { "one" }
+        }
+        Route(.case(ChildRoute.two)) {
+          Path { "two" }
+        }
+      }
+    }
+  }
+
+  func testBasics() async throws {
+    let client = URLRoutingClient<TestRoute>.failing
+      .override(.one) { try .ok("result") }
+
+    let (value, response) = try await client.request(.one, as: String.self)
+
+    XCTAssertEqual("result", value)
+    XCTAssertEqual(200, (response as! HTTPURLResponse).statusCode)
+  }
+
+  func testScoped() async throws {
+    let client = URLRoutingClient<TestRoute>.failing
+      .override(.child(.one)) { try .ok("result") }
+
+    let scopedClient = client.scoped(to: TestRoute.child)
+
+    let (value, response) = try await scopedClient.request(.one, as: String.self)
+
+    XCTAssertEqual("result", value)
+    XCTAssertEqual(200, (response as! HTTPURLResponse).statusCode)
+  }
+}