How to Use an AI Agent to Capture a Webpage Screenshot with Hover Content Visible
Make hover menus and tooltips visible before an AI agent captures a webpage. Use Playwright or Selenium, wait for the revealed content, then save the right screenshot bounds.
To capture a hover menu, tooltip, or preview, an AI agent must move the browser pointer over its trigger, wait until the revealed content is visible, and then take the screenshot. In Playwright, use a locator’s hover() method and then page.screenshot(). In Selenium, use Actions.moveToElement() and then WebDriver’s screenshot API.
A screenshot taken before the hover state appears will not contain that content. Prefer a visible-state wait over a guessed delay, and capture the whole page when the revealed panel may sit outside the trigger element.
1. Identify the hover trigger and the content
First determine which page element reveals the content: it might be a navigation link, button, image, or card. Use a stable locator, preferably one based on an accessible role and name. Then identify an observable state that means the content is ready, such as a visible menu or tooltip.
Hover content does not always appear immediately. A site may animate it, fetch data, or reveal it only after the pointer enters a particular area. The right wait condition depends on the page. Waiting for the actual menu or tooltip to become visible is generally more reliable than sleeping for an arbitrary number of milliseconds.
2. Capture hover content with Playwright
Use locator-based hover(). Playwright’s documentation recommends it over the older page-level hover method. Locator hover performs actionability checks, scrolls the target into view when needed, and moves the pointer to the element’s center by default.
import { chromium } from 'playwright';
const browser = await chromium.launch({ headless: true });
const page = await browser.newPage({ viewport: { width: 1440, height: 900 } });
try {
await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
const trigger = page.getByRole('link', { name: 'Products' });
await trigger.hover();
// Replace this with a locator for the actual revealed menu or tooltip.
await page.getByRole('menu').waitFor({ state: 'visible', timeout: 5000 });
// A page screenshot includes hover content rendered elsewhere on the page.
await page.screenshot({ path: 'hover-state.png' });
} finally {
await browser.close();
}
Install Playwright in your project and install its browser binaries if they are not already available. Replace the example URL, trigger locator, and menu locator with ones from the target page. If the page uses a tooltip rather than a menu, wait for that tooltip’s visible state instead.
Choose screenshot bounds
page.screenshot()captures the viewport by default. Use it when the hover content is visible in the current viewport.page.screenshot({ fullPage: true })captures the full page. It can help when the page is taller than the viewport, but does not guarantee that offscreen hover content will be laid out or included as intended.locator.screenshot()clips the capture to a particular element. Use it only when the hover content is within that element’s bounds. A menu rendered in a separate portal or elsewhere in the DOM can be clipped out.
For example, to save a full-page image after the menu is visible, replace the screenshot call with await page.screenshot({ path: 'hover-state.png', fullPage: true }). The exact result depends on how the site positions and renders the revealed content.
3. Capture hover content with Selenium
Selenium’s Actions API moves the pointer to the element’s in-view center. The target needs to be in the viewport for this hover operation. Scroll it into view if necessary, perform the action, wait for the site’s revealed state, and take a screenshot.
import org.openqa.selenium.By;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.interactions.Actions;
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.Path;
import java.time.Duration;
public class HoverScreenshot {
public static void main(String[] args) throws Exception {
WebDriver driver = new ChromeDriver();
try {
driver.get("https://example.com");
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(5));
WebElement trigger = wait.until(ExpectedConditions.elementToBeClickable(
By.cssSelector(".menu-trigger")));
new Actions(driver).moveToElement(trigger).perform();
// Replace this selector with the site's actual menu or tooltip selector.
wait.until(ExpectedConditions.visibilityOfElementLocated(
By.cssSelector(".menu-panel")));
File image = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
Files.copy(image.toPath(), Path.of("hover-state.png"));
} finally {
driver.quit();
}
}
}
Provide the WebDriver and browser driver through your project’s normal Selenium setup. The CSS selectors here are examples; use locators that match the actual page. Selenium’s WebDriver screenshot captures the browser’s current view. If you run a hosted browser service and need a local image file, take the screenshot explicitly in the test script.
4. Let an AI agent drive the capture
An AI agent does not change the browser requirement: the browser must enter the hover state before the capture step. Give the agent or its tool a sequence it can execute and verify:
- Open the intended URL and wait for the page to load enough to locate the target.
- Find the hover trigger using a stable locator.
- Move the pointer over the trigger.
- Wait for the expected menu, tooltip, label, or preview to become visible.
- Capture the page or the appropriate element.
- Check that the saved image actually includes the revealed content; report a failure if it does not.
If the agent can inspect the page, have it identify the trigger and the expected visible content before taking the screenshot. Avoid asking it to infer success from the hover action alone: the action may complete even when the page did not reveal the expected content.
Viewport and pointer details
Set a viewport that contains both the trigger and the expected content. If the target is below the fold, scroll it into view before hovering. The pointer location matters: APIs that hover at the center may not trigger a site whose active region is unusually small or whose layout changes around the target. In that case, inspect the page and choose a more precise target or pointer position supported by your automation framework.
Keep the pointer over the trigger while waiting. Moving it away can close a menu or tooltip before the screenshot is taken. For content that appears only after a deliberate pointer path, reproduce that path rather than assuming a single move will work.
5. Use cURL or another language when the capture is already available
cURL, Python, and Node.js can save an image returned by a screenshot API, but a plain screenshot request does not itself perform a browser hover interaction. For a hover-dependent page, first use browser automation such as Playwright or Selenium to enter and verify the state. If your capture service specifically supports the needed interaction, follow its documented interaction options.
cURL: save an existing screenshot response
curl -L 'https://example.com/hover-state.png' -o hover-state.png
This downloads an image from a URL that already serves the desired capture. It does not hover the page.
Python: save an existing screenshot response
import requests
response = requests.get('https://example.com/hover-state.png', timeout=30)
response.raise_for_status()
with open('hover-state.png', 'wb') as image:
image.write(response.content)
Node.js: save an existing screenshot response
import { writeFile } from 'node:fs/promises';
const response = await fetch('https://example.com/hover-state.png');
if (!response.ok) {
throw new Error(`Screenshot download failed: ${response.status} ${response.statusText}`);
}
await writeFile('hover-state.png', Buffer.from(await response.arrayBuffer()));
6. Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server. Its one-call screenshot request is useful when you need a page image without setting up a browser locally. A screenshot API call does not perform a custom pointer hover sequence, so use Playwright or Selenium when the page must enter a specific hover state first.
For ordinary page capture, get an API key and call the API as shown below. See the ScreenshotNeo API documentation for request options.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
- Cookie and consent banners, newsletter popups, and chat widgets are handled before the shot; each cleanup step can be turned off.
- Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed. Response headers say which page verdict applied and whether the request was billed.
- An MCP server provides
take_screenshot,get_page_info, andcapture_pdftools for Claude, Cursor, and other MCP clients. - The free plan includes 1,000 screenshots a month with no card. Paid plans start at $5 for 3,000 screenshots; every feature is on every plan.
Sign up for 1,000 free screenshots a month, with no card required.
7. Troubleshooting
| Symptom | Likely cause | What to try |
|---|---|---|
| The screenshot has no menu or tooltip | The pointer did not enter the trigger, the locator matched the wrong element, or the page had not revealed the content yet. | Check the locator, keep the pointer over the trigger, and wait for the actual revealed element to become visible before capturing. |
| The hover action times out | The target is missing, covered, disabled, or not actionable in its current state. | Confirm the page loaded, use a more stable locator, and scroll the element into view. Inspect the page if an overlay is intercepting it. |
| Selenium cannot hover the target | The target is outside the viewport or not interactable. | Scroll it into view and confirm it is displayed before calling moveToElement(). |
| The menu appears, then vanishes | The pointer moved away, the wait or capture step altered the pointer position, or the site closes the panel on a related event. | Keep the pointer over the trigger and capture as soon as the visible-state condition succeeds. |
| The trigger screenshot excludes the panel | The panel is outside the trigger element’s clipped bounds, possibly because it is rendered elsewhere in the page. | Capture the page viewport instead of taking a locator screenshot of only the trigger. |
| The saved file is blank or incomplete | The page or image may still be loading, or the capture bounds may not include the content. | Wait for the revealed content to be visible, check that it lies inside the viewport, and inspect the saved image. |
| The API example does not show hover content | A normal screenshot request captures a page without your custom pointer movement. | Use browser automation to establish the hover state, or confirm that your chosen service documents an interaction capability for it. |
8. Performance, reliability, and cost
Browser startup, page loading, animations, and network requests can all add time. Reuse a browser process for multiple captures when your automation setup permits it, while giving each capture its own page or context where isolation is needed. Wait on the specific visible state rather than adding a long fixed sleep to every run.
For reliable results, use stable locators, explicit timeouts, and a check of the output image. A site’s markup, accessibility names, animations, or hover behavior can change, so treat a missing menu as a capture failure rather than silently storing an image that does not meet the request. No single timeout or browser configuration works for every site.
With self-hosted Playwright or Selenium, account for the compute and maintenance of the browser environment. Hosted browser testing can help when you need browser or device coverage; the research sources do not establish that hosted execution is required for this workflow. ScreenshotNeo charges only for clean shots; its response headers identify the page verdict and billing status. Check its current plan details on the product site before estimating usage.
9. FAQ
Can an AI agent capture hover content without moving a pointer?
For content revealed by pointer hover, the browser needs to enter that hover state before capture. An agent may direct the automation, but the page still needs the pointer interaction or an equivalent site-specific mechanism.
Should I use a full-page screenshot?
Use a viewport screenshot when the trigger and revealed content are visible together. Use full-page capture when the relevant content extends down the page, and verify the result because offscreen hover behavior can depend on the site’s layout.
Can I use a locator screenshot?
Yes, if the locator’s bounds include the revealed content. If the panel is positioned elsewhere, use a page screenshot so it is not clipped out.
Does a screenshot API automatically reproduce my Playwright hover?
Do not assume so. A normal URL-based screenshot request does not execute your custom Playwright pointer sequence. Use browser automation for the interaction, and consult the service documentation for any supported interaction features.


