How to Take a Screenshot of a Web Page with Selenium in Kotlin
Use Selenium’s Java API from Kotlin to capture a web page, save the image safely, and choose between viewport, element, and full-page screenshots.
To take a screenshot of a web page with Selenium in Kotlin, navigate to the page, cast the WebDriver to TakesScreenshot, and call getScreenshotAs<File>(OutputType.FILE). Selenium returns a temporary file, so copy it to your chosen destination before the JVM exits.
import org.openqa.selenium.OutputType
import org.openqa.selenium.TakesScreenshot
import org.openqa.selenium.chrome.ChromeDriver
import java.io.File
import java.nio.file.Files
import java.nio.file.StandardCopyOption
fun main() {
val driver = ChromeDriver()
try {
driver.get("https://www.example.com")
val temporaryScreenshot = (driver as TakesScreenshot)
.getScreenshotAs<File>(OutputType.FILE)
val destination = File("./image.png").toPath()
destination.parent?.let { Files.createDirectories(it) }
Files.copy(
temporaryScreenshot.toPath(),
destination,
StandardCopyOption.REPLACE_EXISTING
)
println("Saved screenshot to ${destination.toAbsolutePath()}")
} finally {
driver.quit()
}
}
This uses Selenium’s Java API, which is also used from Kotlin. The official Selenium Kotlin example follows the same sequence: navigate, capture to a temporary file, copy the file, and quit the driver. See Selenium’s window and tab documentation and the TakesScreenshot API.
1. Set up Selenium and the browser
Add Selenium WebDriver to the project and make a compatible browser and driver available in the execution environment. This example uses ChromeDriver. Selenium’s exact dependency coordinates and driver setup depend on the project’s build system and Selenium version, so use the version already selected by your project or the current Selenium installation instructions. Keep the browser and driver versions compatible.
In a Gradle Kotlin DSL project, the dependency is commonly declared in build.gradle.kts like this, with the version chosen for your project:
dependencies {
implementation("org.seleniumhq.selenium:selenium-java:<selenium-version>")
}
Replace <selenium-version> with a real version before building. The snippet is illustrative because this article’s source material does not specify a current version.
2. Save the screenshot to a file
OutputType.FILE is convenient when the result should be written as an image file. The returned file is temporary; copy it while the process is running. The destination’s parent directory should exist or be created, and the process must have permission to write there.
Use Apache Commons IO if it is already in your project
Selenium’s Kotlin example uses FileUtils.copyFile. If Apache Commons IO is already available, the persistence step can be written this way:
import org.apache.commons.io.FileUtils
import java.io.File
FileUtils.copyFile(temporaryScreenshot, File("./image.png"))
Use either the Java NIO copy in the complete example or the Commons IO copy, not both.
Choose the output representation
| Output type | Kotlin type | Use it when |
|---|---|---|
OutputType.FILE |
File |
You want Selenium to produce a temporary file that you copy to a durable path. |
OutputType.BYTES |
ByteArray |
You want to process or store the raw image bytes in memory. |
OutputType.BASE64 |
String |
You need an encoded string for a transport or interface that accepts base64. |
Selenium documents all three output forms in its OutputType API. With bytes, write them directly:
import org.openqa.selenium.OutputType
import org.openqa.selenium.TakesScreenshot
import java.nio.file.Files
import java.nio.file.Path
val bytes: ByteArray = (driver as TakesScreenshot)
.getScreenshotAs(OutputType.BYTES)
Files.write(Path.of("./image.png"), bytes)
val base64: String = (driver as TakesScreenshot)
.getScreenshotAs(OutputType.BASE64)
Use a file output for a straightforward saved image. Byte arrays avoid an intermediate screenshot file when the next step consumes bytes. Base64 is useful only when the receiving system expects encoded data; it takes more space than the original binary representation.
3. Decide what part of the page to capture
Current browsing context
A call on the driver captures the driver’s current browsing context. Navigate to the intended page and, if necessary, switch to the correct window or frame before capturing. The WebDriver screenshot behavior depends on the implementation; do not assume that a normal driver screenshot always includes the entire scrollable document.
A single element
For a specific element, locate it and call the screenshot API on the WebElement:
import org.openqa.selenium.By
import org.openqa.selenium.OutputType
import org.openqa.selenium.WebElement
import java.io.File
import java.nio.file.Files
import java.nio.file.StandardCopyOption
val element: WebElement = driver.findElement(By.cssSelector("main article"))
val temporaryElementShot = element.getScreenshotAs<File>(OutputType.FILE)
Files.copy(
temporaryElementShot.toPath(),
File("./article.png").toPath(),
StandardCopyOption.REPLACE_EXISTING
)
The element must be present and available to the driver. If the page renders it asynchronously, wait for the element before requesting its screenshot. Selenium documents screenshot capture for a driver or HTML element in the TakesScreenshot API.
The full scrollable page
A regular WebDriver screenshot is not a portable guarantee of a full-page image. Selenium’s API describes conforming implementations in terms of the WebDriver specification and notes browser-dependent behavior for implementations that do not conform. Selenium’s Java API index lists a distinct FirefoxDriver.getFullPageScreenshotAs(OutputType) method and HasFullPageScreenshot capability. That is an official Firefox-specific API path, not a universal cross-browser compatibility promise.
If full-page output is a requirement, check the exact browser, driver, Selenium version, and available API in your runtime. Verify the result with a page taller than the viewport and with content near the bottom. If your driver only captures the visible viewport, alternatives include a browser-specific full-page feature or capturing sections separately; stitching sections yourself can introduce seams, repeated sticky elements, and layout changes.
4. Wait for the page to be ready
driver.get(url) navigates before the capture call, but modern pages can continue loading images, fonts, or application content afterward. If the screenshot is blank or incomplete, wait for a meaningful page condition rather than adding an arbitrary long delay. For example, wait for a target element using Selenium’s explicit waits:
import org.openqa.selenium.By
import org.openqa.selenium.support.ui.ExpectedConditions
import org.openqa.selenium.support.ui.WebDriverWait
import java.time.Duration
val wait = WebDriverWait(driver, Duration.ofSeconds(15))
driver.get("https://www.example.com")
wait.until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector("main")))
Place the wait after navigation and before capture. Choose a selector that indicates the content you need is actually ready; an element being visible does not necessarily mean every image or animation has finished.
5. Complete Kotlin example with an explicit wait
This version combines navigation, a readiness condition, file persistence, and guaranteed driver cleanup:
import org.openqa.selenium.By
import org.openqa.selenium.OutputType
import org.openqa.selenium.TakesScreenshot
import org.openqa.selenium.chrome.ChromeDriver
import org.openqa.selenium.support.ui.ExpectedConditions
import org.openqa.selenium.support.ui.WebDriverWait
import java.io.File
import java.nio.file.Files
import java.nio.file.StandardCopyOption
import java.time.Duration
fun main() {
val driver = ChromeDriver()
try {
driver.get("https://www.example.com")
WebDriverWait(driver, Duration.ofSeconds(15)).until(
ExpectedConditions.visibilityOfElementLocated(By.cssSelector("body"))
)
val temporaryScreenshot = (driver as TakesScreenshot)
.getScreenshotAs<File>(OutputType.FILE)
val destination = File("./screenshots/example.png").toPath()
Files.createDirectories(destination.parent)
Files.copy(
temporaryScreenshot.toPath(),
destination,
StandardCopyOption.REPLACE_EXISTING
)
} finally {
driver.quit()
}
}
The body condition confirms that the document is visible, but replace it with a selector for the content your task depends on. The finally block closes the browser even if navigation, waiting, capture, or copying throws an exception.
6. Equivalent one-shot examples in other languages
If the same capture job is being implemented outside Kotlin, these examples show the basic Selenium flow in Java, cURL, Python, and Node.js. The Selenium examples also need a compatible browser and driver setup.
Java
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.chrome.ChromeDriver;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
public class ScreenshotPage {
public static void main(String[] args) throws Exception {
ChromeDriver driver = new ChromeDriver();
try {
driver.get("https://www.example.com");
File temp = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
Files.copy(temp.toPath(), new File("./image.png").toPath(),
StandardCopyOption.REPLACE_EXISTING);
} finally {
driver.quit();
}
}
}
cURL
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://www.example.com \
-o shot.webp
Python
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://www.example.com"},
timeout=90,
)
r.raise_for_status()
with open("shot.webp", "wb") as f:
f.write(r.content)
Node.js
const q = new URLSearchParams({
access_key: 'YOUR_API_KEY',
url: 'https://www.example.com'
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
const bytes = new Uint8Array(await res.arrayBuffer());
await (await import('node:fs/promises')).writeFile('shot.webp', bytes);
7. Or skip the browser setup
With ScreenshotNeo, one GET request returns a screenshot without installing Selenium, a browser, or a driver. See the ScreenshotNeo API documentation for request options and response details.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://www.example.com -o shot.webp
ScreenshotNeo accepts cookie or consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each cleanup step can be turned off. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers identify the page verdict and billing status. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots.
Create a free ScreenshotNeo account and get 1,000 screenshots a month with no card.
8. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
ClassCastException at the screenshot cast |
The driver implementation does not implement TakesScreenshot. |
Use a screenshot-capable driver implementation; Selenium documents supported implementations in its API. Check the specific driver rather than assuming every WebDriver supports screenshots. |
| Screenshot file disappears after the program ends | OutputType.FILE points to a temporary file that Selenium may delete when the JVM exits. |
Copy the file to the destination before the process ends, or use BYTES and write those bytes yourself. See the OutputType documentation. |
| Destination path is missing or copy fails | The parent directory does not exist, or the process lacks write permission. | Create the parent directories with Files.createDirectories; choose a writable path and check filesystem permissions. |
| Screenshot is blank or missing application content | The page or client-side application had not rendered the target content when capture ran. | Wait for a meaningful element or state before calling the screenshot method. Increase the explicit wait timeout only when the page reasonably needs more time. |
| Only part of a long page appears | The driver’s normal screenshot behavior may cover only a viewport or otherwise vary by implementation. | Confirm the requested scope and the driver’s supported full-page API. Selenium lists a distinct Firefox full-page screenshot API; support is not universal. |
| Element screenshot throws or clips unexpected content | The selector matched no element, the element is not ready, or its own dimensions and visibility affect the result. | Wait for the intended element, verify the selector, and capture the element itself only when element-level output is desired. |
| Browser fails to start or session creation fails | The browser/driver is unavailable, incompatible, or cannot run in the environment. | Install the browser and compatible driver, inspect the driver startup error, and configure the runtime for its environment. In containers or CI, confirm that the browser can launch with the available display/headless setup. |
| Old image remains after rerunning | The destination already exists and the copy operation may not replace it. | Use StandardCopyOption.REPLACE_EXISTING or choose a unique output name. |
9. Performance, reliability, and cost
Selenium launches and controls a browser, so the browser startup and page rendering are part of the job. Reuse a driver for multiple captures when appropriate, but keep each navigation and screenshot isolated enough that one page’s state cannot leak into the next. Always close the driver in finally so failures do not leave browser processes running.
Wait for a specific readiness condition to avoid both premature captures and needless fixed delays. Pages with animations, lazy-loaded images, or content that appears only after scrolling may require additional page-specific handling. A viewport screenshot and a full-page screenshot can have different work and output sizes; confirm the needed capture scope before processing or storing many images.
Selenium itself is browser automation rather than a per-screenshot service price in the cited material. Your operating cost comes from the infrastructure and time required to run browsers, plus storage and downstream processing. The sources do not provide performance benchmarks or a universal runtime figure, so measure with your target pages and environment.
10. FAQ
Does Selenium return PNG bytes?
The screenshot API offers file, byte-array, and base64 output representations. The examples save the browser screenshot with a .png filename; consult your driver’s behavior if you need a specific encoding guarantee.
Can I take a screenshot without saving it to disk?
Yes. Request OutputType.BYTES and pass the resulting ByteArray to the next in-memory processing step.
Does driver.quit() need to run after every screenshot?
It should run when the automation session is finished. If you intentionally reuse a session for multiple captures, quit after the batch, including on error.
Can I capture a page that requires authentication?
Selenium captures the state of the browser session you provide. Sign in or establish the required session before capture, and follow the target site’s access rules.
Sources
- Selenium: Working with windows and tabs (Kotlin ChromeDriver screenshot example).
- Selenium Java API: TakesScreenshot (capture support, driver and element scope, implementation behavior).
- Selenium Java API: OutputType (FILE, BYTES, BASE64 and temporary file behavior).
- Selenium Java API: Uses of OutputType (Firefox full-page screenshot API listing).


