diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/http/WebRequestBuilder.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/http/WebRequestBuilder.kt
index b33fa710367e3862882a3c9996551f5da374c2db..c7f8960056aa00d84f1b9f8631136a0ab23ba381 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/http/WebRequestBuilder.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/http/WebRequestBuilder.kt
@@ -27,6 +27,9 @@ import de.rki.coronawarnapp.exception.ApplicationConfigurationInvalidException
 import de.rki.coronawarnapp.http.requests.RegistrationTokenRequest
 import de.rki.coronawarnapp.http.requests.ReqistrationRequest
 import de.rki.coronawarnapp.http.requests.TanRequestBody
+import de.rki.coronawarnapp.http.service.DistributionService
+import de.rki.coronawarnapp.http.service.SubmissionService
+import de.rki.coronawarnapp.http.service.VerificationService
 import de.rki.coronawarnapp.server.protocols.ApplicationConfigurationOuterClass.ApplicationConfiguration
 import de.rki.coronawarnapp.service.diagnosiskey.DiagnosisKeyConstants
 import de.rki.coronawarnapp.service.submission.SubmissionConstants
@@ -41,19 +44,36 @@ import java.io.File
 import java.util.Date
 import java.util.UUID
 
-object WebRequestBuilder {
-    private val TAG: String? = WebRequestBuilder::class.simpleName
-
-    private const val EXPORT_BINARY_FILE_NAME = "export.bin"
-    private const val EXPORT_SIGNATURE_FILE_NAME = "export.sig"
-
-    private val serviceFactory = ServiceFactory()
-
-    private val distributionService by lazy { serviceFactory.distributionService() }
-    private val verificationService by lazy { serviceFactory.verificationService() }
-    private val submissionService by lazy { serviceFactory.submissionService() }
+class WebRequestBuilder(
+    private val distributionService: DistributionService,
+    private val verificationService: VerificationService,
+    private val submissionService: SubmissionService,
+    private val verificationKeys: VerificationKeys
+) {
+    companion object {
+        private val TAG: String? = WebRequestBuilder::class.simpleName
+        private const val EXPORT_BINARY_FILE_NAME = "export.bin"
+        private const val EXPORT_SIGNATURE_FILE_NAME = "export.sig"
+
+        @Volatile
+        private var instance: WebRequestBuilder? = null
+
+        fun getInstance(): WebRequestBuilder {
+            return instance ?: synchronized(this) {
+                instance ?: buildWebRequestBuilder().also { instance = it }
+            }
+        }
 
-    private val verificationKeys = VerificationKeys()
+        private fun buildWebRequestBuilder(): WebRequestBuilder {
+            val serviceFactory = ServiceFactory()
+            return WebRequestBuilder(
+                serviceFactory.distributionService(),
+                serviceFactory.verificationService(),
+                serviceFactory.submissionService(),
+                VerificationKeys()
+            )
+        }
+    }
 
     suspend fun asyncGetDateIndex(): List<String> = withContext(Dispatchers.IO) {
         return@withContext distributionService
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/service/applicationconfiguration/ApplicationConfigurationService.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/service/applicationconfiguration/ApplicationConfigurationService.kt
index 596ba0925338015533e0c50a5fd8e82cd3e1365a..137dc448bf064e38c726e9db5766356aa3b58265 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/service/applicationconfiguration/ApplicationConfigurationService.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/service/applicationconfiguration/ApplicationConfigurationService.kt
@@ -6,8 +6,7 @@ import de.rki.coronawarnapp.server.protocols.ApplicationConfigurationOuterClass
 
 object ApplicationConfigurationService {
     suspend fun asyncRetrieveApplicationConfiguration(): ApplicationConfigurationOuterClass.ApplicationConfiguration {
-        return WebRequestBuilder
-            .asyncGetApplicationConfigurationFromServer()
+        return WebRequestBuilder.getInstance().asyncGetApplicationConfigurationFromServer()
     }
 
     suspend fun asyncRetrieveExposureConfiguration(): ExposureConfiguration =
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/service/diagnosiskey/DiagnosisKeyService.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/service/diagnosiskey/DiagnosisKeyService.kt
index fc4cc03daa3f1b55fb7581869329283a6973a5bf..bf03586ffb9c53deec91b86317f40047bf3a4471 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/service/diagnosiskey/DiagnosisKeyService.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/service/diagnosiskey/DiagnosisKeyService.kt
@@ -48,7 +48,7 @@ object DiagnosisKeyService {
      */
     suspend fun asyncSubmitKeys(authCode: String, keysToReport: List<KeyExportFormat.TemporaryExposureKey>) {
         Log.d(TAG, "Diagnosis Keys will be submitted.")
-        WebRequestBuilder.asyncSubmitKeysToServer(
+        WebRequestBuilder.getInstance().asyncSubmitKeysToServer(
             authCode,
             false,
             keysToReport
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/service/submission/SubmissionService.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/service/submission/SubmissionService.kt
index 6cdd39fee0dc016c5cc81b382fc8a3da92d809f8..1f8f64b59edf6dbf3a5b9ed94a8d24de4a8bd004 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/service/submission/SubmissionService.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/service/submission/SubmissionService.kt
@@ -24,7 +24,7 @@ object SubmissionService {
 
     private suspend fun asyncRegisterDeviceViaGUID(guid: String) {
         val registrationToken =
-            WebRequestBuilder.asyncGetRegistrationToken(
+            WebRequestBuilder.getInstance().asyncGetRegistrationToken(
                 guid,
                 QR_CODE_KEY_TYPE
             )
@@ -35,7 +35,7 @@ object SubmissionService {
 
     private suspend fun asyncRegisterDeviceViaTAN(tan: String) {
         val registrationToken =
-            WebRequestBuilder.asyncGetRegistrationToken(
+            WebRequestBuilder.getInstance().asyncGetRegistrationToken(
                 tan,
                 TELE_TAN_KEY_TYPE
             )
@@ -45,7 +45,7 @@ object SubmissionService {
     }
 
     suspend fun asyncRequestAuthCode(registrationToken: String): String {
-        return WebRequestBuilder.asyncGetTan(registrationToken)
+        return WebRequestBuilder.getInstance().asyncGetTan(registrationToken)
     }
 
     suspend fun asyncSubmitExposureKeys() {
@@ -58,7 +58,7 @@ object SubmissionService {
         val registrationToken =
             LocalData.registrationToken() ?: throw NoRegistrationTokenSetException()
         return TestResult.fromInt(
-            WebRequestBuilder.asyncGetTestResult(registrationToken)
+            WebRequestBuilder.getInstance().asyncGetTestResult(registrationToken)
         )
     }
 
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/CachedKeyFileHolder.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/CachedKeyFileHolder.kt
index 9fe260c58c70dbc7a58117c63594756fba8d38ef..64b71b742bb75f97ab49e500079c237bd73b8c49 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/CachedKeyFileHolder.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/CachedKeyFileHolder.kt
@@ -76,7 +76,7 @@ object CachedKeyFileHolder {
                 return@withContext getLast3Hours(currentDate)
                     .map { getURLForHour(currentDate.toServerFormat(), it) }
                     .map { url -> async {
-                        return@async WebRequestBuilder.asyncGetKeyFilesFromServer(url)
+                        return@async WebRequestBuilder.getInstance().asyncGetKeyFilesFromServer(url)
                     } }.awaitAll()
             } else {
                 throw IllegalStateException(
@@ -152,7 +152,7 @@ object CachedKeyFileHolder {
      */
     private suspend fun String.createDayEntryForUrl() = keyCache.createEntry(
         this.generateCacheKeyFromString(),
-        WebRequestBuilder.asyncGetKeyFilesFromServer(this).toURI(),
+        WebRequestBuilder.getInstance().asyncGetKeyFilesFromServer(this).toURI(),
         DAY
     )
 
@@ -183,13 +183,13 @@ object CachedKeyFileHolder {
      * Get all dates from server based as formatted dates
      */
     private suspend fun getDatesFromServer() =
-        WebRequestBuilder.asyncGetDateIndex()
+        WebRequestBuilder.getInstance().asyncGetDateIndex()
 
     /**
      * Get all hours from server based as formatted dates
      */
     private suspend fun getHoursFromServer(day: Date) =
-        WebRequestBuilder.asyncGetHourIndex(day)
+        WebRequestBuilder.getInstance().asyncGetHourIndex(day)
 
     /**
      * TODO remove before release
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/http/WebRequestBuilderTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/http/WebRequestBuilderTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..305daf8a9ee5ecf9aa965165964ee2591a01a067
--- /dev/null
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/http/WebRequestBuilderTest.kt
@@ -0,0 +1,78 @@
+package de.rki.coronawarnapp.http
+
+import de.rki.coronawarnapp.http.service.DistributionService
+import de.rki.coronawarnapp.http.service.SubmissionService
+import de.rki.coronawarnapp.http.service.VerificationService
+import de.rki.coronawarnapp.service.diagnosiskey.DiagnosisKeyConstants
+import de.rki.coronawarnapp.util.TimeAndDateExtensions.toServerFormat
+import de.rki.coronawarnapp.util.security.VerificationKeys
+import io.mockk.MockKAnnotations
+import io.mockk.clearAllMocks
+import io.mockk.coEvery
+import io.mockk.coVerify
+import io.mockk.every
+import io.mockk.impl.annotations.MockK
+import io.mockk.mockkObject
+import io.mockk.unmockkAll
+import kotlinx.coroutines.runBlocking
+import org.junit.After
+import org.junit.Assert
+import org.junit.Before
+import org.junit.Test
+import java.util.Date
+
+class WebRequestBuilderTest {
+    @MockK private lateinit var verificationService: VerificationService
+    @MockK private lateinit var distributionService: DistributionService
+    @MockK private lateinit var submissionService: SubmissionService
+    @MockK private lateinit var verificationKeys: VerificationKeys
+
+    private lateinit var webRequestBuilder: WebRequestBuilder
+
+    @Before
+    fun setUp() = run {
+        MockKAnnotations.init(this)
+        webRequestBuilder = WebRequestBuilder(
+            distributionService,
+            verificationService,
+            submissionService,
+            verificationKeys
+        )
+    }
+
+    @After
+    fun tearDown() = unmockkAll()
+
+    @Test
+    fun retrievingDateIndexIsSuccessful() {
+        val urlString = DiagnosisKeyConstants.AVAILABLE_DATES_URL
+        coEvery { distributionService.getDateIndex(urlString) }
+            .returns(listOf("1900-01-01", "2000-01-01"))
+
+        runBlocking {
+            val expectedResult = listOf("1900-01-01", "2000-01-01")
+            Assert.assertEquals(webRequestBuilder.asyncGetDateIndex(), expectedResult)
+            coVerify {
+                distributionService.getDateIndex(urlString)
+            }
+        }
+    }
+
+    @Test
+    fun asyncGetHourIndex() {
+        val day = Date()
+        val urlString = DiagnosisKeyConstants.AVAILABLE_DATES_URL +
+                "/${day.toServerFormat()}/${DiagnosisKeyConstants.HOUR}"
+
+        coEvery { distributionService.getHourIndex(urlString) }
+            .returns(listOf("1", "2"))
+
+        runBlocking {
+            val expectedResult = listOf("1", "2")
+            Assert.assertEquals(webRequestBuilder.asyncGetHourIndex(day), expectedResult)
+            coVerify {
+                distributionService.getHourIndex(urlString)
+            }
+        }
+    }
+}
\ No newline at end of file