Screenshot API Kotlin Guide 2026

Capture screenshots and generate PDFs from Kotlin JVM, Android, and Ktor server applications using coroutines and OkHttp.

Get Free API Key

Calling SnapAPI from Kotlin with OkHttp

Kotlin's coroutines and OkHttp's asynchronous HTTP client make calling SnapAPI clean and idiomatic. OkHttp's enqueue method provides async callback-based requests, while using it with coroutines and suspendCoroutine converts the callback interface to a clean suspend function. Add OkHttp to your build.gradle.kts dependencies along with the Kotlin coroutines library, and define a suspend function that builds the request, makes the call, and returns the response bytes.

// build.gradle.kts
dependencies {
    implementation("com.squareup.okhttp3:okhttp:4.12.0")
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.0")
    implementation("com.squareup.moshi:moshi-kotlin:1.15.0")
}

// SnapApiClient.kt
import okhttp3.*
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
import java.io.IOException
import java.net.URLEncoder

class SnapApiClient(private val apiKey: String) {
    private val client = OkHttpClient.Builder()
        .callTimeout(60, java.util.concurrent.TimeUnit.SECONDS)
        .build()

    suspend fun screenshot(
        url: String,
        format: String = "png",
        fullPage: Boolean = false,
        viewportWidth: Int = 1280
    ): ByteArray {
        val encodedUrl = URLEncoder.encode(url, "UTF-8")
        val request = Request.Builder()
            .url("https://api.snapapi.pics/screenshot?url=$encodedUrl&format=$format&full_page=$fullPage&viewport_width=$viewportWidth")
            .header("Authorization", "Bearer $apiKey")
            .get()
            .build()

        return suspendCancellableCoroutine { cont ->
            val call = client.newCall(request)
            cont.invokeOnCancellation { call.cancel() }
            call.enqueue(object : Callback {
                override fun onResponse(call: Call, response: Response) {
                    if (response.isSuccessful) {
                        cont.resume(response.body!!.bytes())
                    } else {
                        cont.resumeWithException(IOException("HTTP ${response.code}"))
                    }
                }
                override fun onFailure(call: Call, e: IOException) = cont.resumeWithException(e)
            })
        }
    }
}

// Usage in a coroutine scope
val client = SnapApiClient(System.getenv("SNAPAPI_KEY") ?: "")
val bytes = client.screenshot("https://example.com", format = "jpeg")
java.io.File("screenshot.jpg").writeBytes(bytes)

Android: Screenshot Display with Coil

In Android applications, load a screenshot asynchronously and display it in an ImageView or Jetpack Compose Image component using Coil or Glide. Rather than calling SnapAPI directly from the Android client (which would expose the API key in the APK), proxy the screenshot request through your backend and load the resulting image URL with Coil's AsyncImage. The backend generates a signed URL or a temporary proxy URL that Coil fetches, keeping the SnapAPI key server-side. For internal enterprise Android apps where key exposure risk is acceptable, use the SnapApiClient above directly with a ViewModel and LiveData or StateFlow to manage the coroutine lifecycle correctly.

// ScreenshotViewModel.kt (Android)
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch

class ScreenshotViewModel : ViewModel() {
    private val client = SnapApiClient(BuildConfig.SNAPAPI_KEY)

    sealed class UiState {
        object Loading : UiState()
        data class Success(val bytes: ByteArray) : UiState()
        data class Error(val message: String) : UiState()
    }

    private val _state = MutableStateFlow<UiState>(UiState.Loading)
    val state: StateFlow<UiState> = _state

    fun capture(url: String) {
        viewModelScope.launch {
            _state.value = UiState.Loading
            _state.value = try {
                val bytes = client.screenshot(url, format = "jpeg", viewportWidth = 1280)
                UiState.Success(bytes)
            } catch (e: Exception) {
                UiState.Error(e.message ?: "Unknown error")
            }
        }
    }
}

// Compose UI
@Composable
fun ScreenshotScreen(viewModel: ScreenshotViewModel = viewModel()) {
    val state by viewModel.state.collectAsState()
    when (val s = state) {
        is ScreenshotViewModel.UiState.Loading -> CircularProgressIndicator()
        is ScreenshotViewModel.UiState.Success -> {
            val bitmap = BitmapFactory.decodeByteArray(s.bytes, 0, s.bytes.size)
            Image(bitmap.asImageBitmap(), contentDescription = "Screenshot")
        }
        is ScreenshotViewModel.UiState.Error -> Text(s.message)
    }
}

Ktor Server: Screenshot Proxy Endpoint

Ktor is JetBrains' asynchronous web framework for Kotlin, designed for building server-side applications and microservices. Use Ktor's built-in HttpClient with the CIO engine for async HTTP calls, and define a route that proxies screenshot requests to SnapAPI with the stored API key. Ktor's routing DSL and coroutines make the screenshot proxy endpoint concise and readable.

// Application.kt — Ktor screenshot proxy
import io.ktor.client.*
import io.ktor.client.engine.cio.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.http.*
import io.ktor.server.application.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import java.net.URLEncoder

val httpClient = HttpClient(CIO) {
    engine { requestTimeout = 60_000 }
}
val apiKey = System.getenv("SNAPAPI_KEY") ?: ""

fun main() {
    embeddedServer(Netty, port = 8080) {
        routing {
            get("/screenshot") {
                val url = call.request.queryParameters["url"]
                    ?: return@get call.respond(HttpStatusCode.BadRequest, "url required")
                val encoded = URLEncoder.encode(url, "UTF-8")
                val response = httpClient.get(
                    "https://api.snapapi.pics/screenshot?url=$encoded&format=png"
                ) {
                    header("Authorization", "Bearer $apiKey")
                }
                call.respondBytes(
                    response.readBytes(),
                    ContentType.Image.PNG,
                    HttpStatusCode.OK
                )
            }
        }
    }.start(wait = true)
}

Coroutines Best Practices for Screenshot APIs

Kotlin coroutines provide powerful concurrency primitives for screenshot generation workflows. Use supervisorScope when capturing multiple URLs concurrently to prevent one failing capture from cancelling the remaining captures: unlike a regular coroutineScope where any child failure cancels all siblings, supervisorScope allows each child to fail independently. Combine supervisorScope with async and awaitAll to collect results from all captures, with each failure represented as an exception rather than a cancelled task. Use Dispatchers.IO for OkHttp-based screenshot calls since OkHttp is a blocking IO library wrapped in a suspend function. For Ktor's async HttpClient, use Dispatchers.Default since the engine is non-blocking. Apply structured concurrency principles throughout: always launch coroutines in a scope that is tied to the lifecycle of the component (ViewModelScope on Android, requestScope in Ktor handlers) to ensure coroutines are cancelled automatically when the component is destroyed, preventing resource leaks from abandoned screenshot requests.

Spring Boot Integration for Kotlin

Kotlin is fully interoperable with Spring Boot, and Spring Boot's WebClient reactive HTTP client works naturally with Kotlin coroutines via the Spring Kotlin coroutines extensions library. Add spring-boot-starter-webflux and kotlinx-coroutines-reactor to your dependencies to use WebClient with suspend functions. Define a SnapApiService component that uses WebClient to call the screenshot endpoint and returns the image bytes via a suspend function. Inject the service into Spring MVC controllers or Spring WebFlux route handlers using constructor injection. The coroutines integration means your Spring Boot controllers can be suspend functions that call the screenshot service directly, without the boilerplate of Mono and CompletableFuture chains required in Java WebFlux applications.

// SnapApiService.kt — Spring Boot with Kotlin coroutines
import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Service
import org.springframework.web.reactive.function.client.WebClient
import org.springframework.web.reactive.function.client.awaitBody
import java.net.URLEncoder

@Service
class SnapApiService(
    private val webClient: WebClient,
    @Value("\${snapapi.key}") private val apiKey: String
) {
    suspend fun screenshot(url: String, format: String = "png"): ByteArray {
        val encoded = URLEncoder.encode(url, "UTF-8")
        return webClient.get()
            .uri("https://api.snapapi.pics/screenshot?url=$encoded&format=$format")
            .header("Authorization", "Bearer $apiKey")
            .retrieve()
            .awaitBody()
    }
}

// Controller.kt
@RestController
class ScreenshotController(private val service: SnapApiService) {
    @GetMapping("/screenshot", produces = ["image/png"])
    suspend fun screenshot(@RequestParam url: String): ByteArray =
        service.screenshot(url, "png")
}

Kotlin Multiplatform (KMP) Considerations

Kotlin Multiplatform projects that share business logic between Android, iOS, and desktop targets can include SnapAPI client code in the shared module using the ktor-client library, which has platform-specific engines for Android (OkHttp engine), iOS (Darwin engine), and JVM (CIO engine). Define the SnapApiClient interface and implementation in the commonMain source set using the Ktor HttpClient with common extensions, and configure the appropriate engine in each platform's main source set. The Kotlin coroutines library fully supports multiplatform, so the suspend function API works identically on all targets. Shared test code in the commonTest source set can use a mock engine to test the client logic without platform-specific setup. This approach is particularly well-suited for apps that need to call SnapAPI from both Android and iOS native code in a KMP architecture, allowing a single implementation to serve both platforms while respecting platform-specific deployment and key management requirements.

Getting Started with SnapAPI in Kotlin

Add OkHttp or Ktor HTTP client to your build.gradle.kts, store the API key in your environment or as a build config field, and make the first test call from a main function or a unit test to verify the connection and response format. The free tier at 200 requests per month is available immediately after registering at snapapi.pics/register with just an email address, with no credit card required. The SnapAPI documentation at snapapi.pics/docs includes Kotlin code examples for the screenshot, scrape, and extract endpoints. If you have questions during integration, the contact form at snapapi.pics/contact reaches the engineering team directly with same-day responses. Most Kotlin developers complete their first working integration within an hour of receiving the API key.

Scraping and Extraction in Kotlin

SnapAPI's scraping and extraction endpoints follow the same OkHttp request pattern as the screenshot endpoint, with JSON response bodies instead of binary image data. Add the scrape endpoint call to your SnapApiClient using the same suspendCancellableCoroutine pattern, parsing the JSON response body with Moshi or kotlinx.serialization. For structured data extraction, define a Kotlin data class matching the extraction schema, serialize the schema to JSON using Moshi or Gson, and POST it to the extract endpoint. Kotlinx.serialization is particularly well-suited for this use case since it can both serialize the schema request body and deserialize the extracted response into a typed data class with a single @Serializable annotation, eliminating the need for manual JSON parsing. This gives Kotlin applications the full SnapAPI feature set — screenshot, scraping, and extraction — through the same client class with idiomatic Kotlin coroutine-based async APIs.