How to Scrape Plot Data Revealed on Mouse Hover
Extract chart values revealed on hover by reading Plotly or Highcharts data first, then using browser automation when necessary.

To scrape values revealed when you hover over a chart, first identify its charting library and read the underlying point data or hover-event payload. That is usually more reliable and precise than scraping the tooltip’s formatted text. If the page does not expose usable chart data, automate a real browser: move the pointer over points, wait for the tooltip, and collect its rendered contents.
This guide covers Plotly and Highcharts, SVG and HTML tooltips, canvas charts, browser automation, validation, and common failure cases. Use these techniques only where you have permission to access and reuse the data; follow the site’s terms, authentication rules, rate limits, and applicable data licenses.
1. Inspect the chart before automating
Open the page in a browser and use developer tools to find how the chart is built. The aim is to discover whether data already exists in JavaScript, a chart object, a page script, or a network response. Pulling structured data avoids fragile coordinate-based scraping.
- Inspect the chart’s DOM. Look for SVG elements, canvas elements, graph containers, and attributes that identify a library.
- Review loaded script names and page globals. Search for names such as
Plotly,Highcharts, or library-specific graph containers. - Check the browser’s Network panel while reloading the page. A JSON response may contain the chart’s source values. Do not assume the plotted values are always embedded in the HTML.
- Inspect a tooltip manually. Note whether it is an SVG node, an HTML node, or drawn into a canvas, and whether it contains one point or several.
A tooltip is presentation output. It may round decimals, add units, rename series, or combine nearby points. Prefer raw values and labels in a chart object or event when available. Scraping rendered text is useful as a fallback, but preserve the distinction between a raw value and a display-formatted value.
2. Capture Plotly hover data
Plotly emits a plotly_hover event when a point is hovered. The event’s points array contains data for the point or points under the cursor. Plotly documents hover events alongside click, unhover, and selection events; the payload can include pixel coordinates that help associate an event with a location in the plot. See the Plotly JavaScript event documentation.

When you control the page or can run code in its context, attach a listener to the graph div. This small runnable example assumes Plotly has already rendered a graph into an element with id chart:
const graph = document.getElementById('chart');
if (!graph || typeof graph.on !== 'function') {
throw new Error('Plotly graph div was not found or is not ready');
}
graph.on('plotly_hover', (eventData) => {
const rows = (eventData.points || []).map((point) => ({
x: point.x,
y: point.y,
series: point.data?.name,
curveNumber: point.curveNumber,
pointNumber: point.pointNumber,
xPixel: point.xaxis?.p2c?.(point.x),
yPixel: point.yaxis?.p2c?.(point.y)
}));
console.log(JSON.stringify(rows));
});
The point fields are useful for identifying values and series. Treat optional properties as optional: chart types and versions may produce different point details. The pixel fields above are derived from the point’s axis objects when those objects are available; the event payload may also provide pixel coordinates. Keep the full event during discovery so you can see which fields this particular chart supplies, then retain only fields you need.
Collect a sample of events
If the chart is part of your own application, capture events as they occur and serialize them to a file or send them to a local collector. For a page you do not control, first determine whether its chart object is exposed. A listener will not help if you cannot execute code in the page context or the site recreates the graph after your listener attaches.
const samples = [];
const graph = document.getElementById('chart');
graph.on('plotly_hover', (eventData) => {
samples.push({
capturedAt: new Date().toISOString(),
points: (eventData.points || []).map((point) => ({
x: point.x,
y: point.y,
series: point.data?.name,
pointNumber: point.pointNumber
}))
});
});
// After interacting with the chart:
console.log(JSON.stringify(samples, null, 2));
Do not use this sample as a substitute for a point traversal strategy. Hover events report what is under the cursor; they do not promise to enumerate every datum automatically. If the plot contains thousands of points, inspect the chart’s data source or library object instead of trying to hover each one.
3. Read Highcharts series and point data
Highcharts ties tooltips to hovered points in a series. Its documentation describes tooltip behavior, shared and split tooltips, formatter control, and the useHTML option. Mouse tracking includes point tooltips and click events, and can affect whether interaction is available. See the Highcharts tooltip options and series mouse-tracking option.
If the page exposes a chart instance, reading its series and point data is generally steadier than scraping the tooltip element. In the browser console, inspect likely chart objects and verify the result before collecting data:
// Example when the page exposes a Highcharts chart instance.
const chart = window.Highcharts?.charts?.find(Boolean);
if (!chart) {
throw new Error('No exposed Highcharts chart instance found');
}
const rows = chart.series.flatMap((series) =>
series.points.map((point) => ({
series: series.name,
x: point.x,
y: point.y,
category: point.category
}))
);
console.log(JSON.stringify(rows, null, 2));
That access pattern is a discovery example, not a guarantee that every website exposes a global chart array. Some pages bundle the library privately, create multiple charts, or use processed point objects that do not include all original data. Confirm the instance corresponds to the intended chart and compare a few extracted points with visible labels.
Mouse tracking can be disabled for performance or to suppress interaction. In that case, the expected tooltip may not appear, and moving a pointer will not produce point-level interactions. Shared tooltips can return values from multiple series at one x position; split tooltips can render series values as separate fragments. A formatter can change the displayed text independently of the raw point values. Decide whether your target is one hovered point, all series at an x value, or exactly the rendered tooltip.
4. Use browser automation when chart data is not exposed
When no stable chart object or data request is available, use a real browser and interact with the chart. The essential loop is: locate the chart, move to a point, wait for the tooltip, read the rendered text, and record the coordinates and context. Coordinate-based scraping is sensitive to viewport size, responsive layout, zoom, and chart changes, so prefer locating actual point elements when the chart uses SVG.

Here is a Playwright example for a page whose chart has a stable selector and whose tooltip appears as an element with a known selector. Replace the URL and selectors with ones you have inspected. Install Playwright in your project and install its browser as described in the Playwright documentation.
import { chromium } from 'playwright';
const url = 'https://example.com/chart';
const chartSelector = '#chart';
const tooltipSelector = '.chart-tooltip';
const browser = await chromium.launch({ headless: true });
const page = await browser.newPage({ viewport: { width: 1440, height: 1000 } });
try {
await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 30000 });
await page.locator(chartSelector).waitFor({ state: 'visible', timeout: 15000 });
const box = await page.locator(chartSelector).boundingBox();
if (!box) throw new Error('Chart has no visible bounding box');
const observations = [];
const positions = [
{ x: box.x + box.width * 0.25, y: box.y + box.height * 0.5 },
{ x: box.x + box.width * 0.50, y: box.y + box.height * 0.5 },
{ x: box.x + box.width * 0.75, y: box.y + box.height * 0.5 }
];
for (const position of positions) {
await page.mouse.move(position.x, position.y);
const tooltip = page.locator(tooltipSelector);
await tooltip.waitFor({ state: 'visible', timeout: 3000 });
observations.push({
x: position.x,
y: position.y,
text: (await tooltip.innerText()).trim()
});
}
console.log(JSON.stringify(observations, null, 2));
} finally {
await browser.close();
}
This example samples three positions for clarity; it does not claim to enumerate the chart’s data. Many charts snap the pointer to the nearest point, while others display values only when the pointer lands within a narrow hit region. To cover specific points, inspect SVG nodes for point centers or use the library’s own point data. For canvas-only plots, sample coordinates experimentally and validate each tooltip.
SVG, HTML, and canvas differences
- SVG: point marks and tooltip elements may exist in the DOM. Inspect the markup after hovering and use stable attributes if available.
- HTML tooltip: text can often be read from an ordinary DOM node after the hover. Highcharts supports HTML tooltip rendering through
useHTML. - Canvas: pixels do not expose point-level text nodes. Use chart objects or pointer automation that triggers the chart’s hit testing. OCR from a screenshot is a last resort because it can lose precision and formatting.
For a shared tooltip, capture all displayed series values as one observation and associate them with the shared x label. For a split tooltip, the parts may be separate DOM nodes; collect them together rather than assuming a single container. If tooltip text is generated by a custom formatter, its labels and number formatting may not match the underlying values.
5. Make extracted values reliable
Hover scraping can silently collect the wrong point while still producing plausible output. Build checks into the collection process:
- Record the chart identifier, series name, x value, y value, and screen coordinates when available.
- Revisit a sample of points and confirm that the tooltip corresponds to the intended series.
- Preserve raw numeric values separately from formatted tooltip strings. Do not parse away decimal precision or units without a documented rule.
- Compare several points against visible axes, a legend, a table, or a network response when available.
- Handle null, missing, and nonnumeric values explicitly. Some chart types use them to create gaps.
- Store capture time and page URL so later changes in live data can be distinguished from extraction errors.
Keep one output record per point or per shared cursor position, and document which meaning you chose. A shared tooltip can represent several series at the same x coordinate; flattening it into unrelated rows can lose that relationship.
6. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| No Plotly event fires | The listener was attached to the wrong element, before rendering, or outside the page context. | Wait for the graph div to exist, verify it has an on method, and attach to the graph that receives interaction. |
| Tooltip never appears | Wrong coordinates or selector, disabled mouse tracking, chart not loaded, or the pointer is outside a point’s hit area. | Confirm the chart is visible, inspect its bounds and tooltip markup manually, and try an SVG point center or library data access. |
| Tooltip text is empty | The tooltip is SVG text, split across nodes, rendered in a canvas, or still updating. | Inspect the post-hover DOM, wait for the visible tooltip state, collect split fragments together, or use the chart object. |
| Several values appear for one hover | The chart uses a shared tooltip or multiple nearby series points. | Preserve the event’s full points array and record the common x position rather than selecting the first entry blindly. |
| Values differ from the tooltip | The tooltip formatter rounds or transforms values, or the wrong series was read. | Compare raw and displayed representations, identify the series explicitly, and retain both if the formatted value matters. |
| Some points are skipped | Coordinate samples missed the hit region, points overlap, or the chart is responsive. | Use point geometry or library data; if coordinates are required, calculate them from the current chart bounds and validate coverage. |
| Chart instance is undefined | The library is bundled privately, the page has not initialized, or the chart is in another frame. | Inspect scripts and frames, wait for readiness, and fall back to browser interaction or an authorized data request. |
| Automation times out on navigation | The site keeps network connections open or loads third-party resources slowly. | Wait for the chart selector or a specific ready condition instead of requiring all network activity to stop. |
7. Performance, reliability, and access
Extracting a chart’s structured data is usually the most efficient approach because it avoids rendering and pointer movement. A hover-driven browser workflow has extra cost: page startup, scripts, network activity, and a delay for each tooltip. Keep a browser context alive for a batch on the same site, but avoid high concurrency that could overload the site or trigger rate limits.
Reliability depends on what you target. Library objects and event payloads are typically less sensitive to cosmetic redesigns than tooltip selectors. SVG point geometry can be useful but may change with chart redraws. Screen coordinates are most fragile because viewport, responsive breakpoints, scrolling, and device scale affect them. Use explicit timeouts and readiness conditions, retry transient page failures within reasonable limits, and record failed observations rather than silently dropping them.
Respect access constraints: use authorized sessions for authenticated data, obey site terms and rate limits, and do not bypass bot checks or other access controls. A publicly visible tooltip does not automatically grant permission to republish the underlying dataset. Check licensing and attribution requirements before reuse.
Or skip the browser setup
For a screenshot of the chart page or a visual record of a hovered state, ScreenshotNeo provides a website screenshot API and MCP server. It captures the page; it does not replace extracting structured chart values from a library event or data source. For an ordinary page screenshot, make one request:
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://stripe.com \
-o shot.webp
See the ScreenshotNeo API documentation for request options. ScreenshotNeo accepts cookie and consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each 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 whether the request was billed. 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 shots per month with no card. Paid plans start at $5 for 3,000 shots; every feature is available on every plan. Sign up for 1,000 free screenshots a month, with no card.
FAQ
Can I scrape hover values from a chart screenshot?
A static screenshot contains pixels, not the chart’s point objects or event payload. It can document what was displayed, but extracting exact values from it requires visual recognition and may be less precise than interacting with the page.
Does a Plotly hover event return every point in the chart?
No. It reports the point or points associated with the current hover position. Use the chart’s structured data when you need a complete series.
Why does the tooltip show a different number of values on different charts?
Tooltip behavior depends on chart configuration, including shared or split display, series proximity, and custom formatting. Inspect the payload and rendered tooltip for the specific chart.
Should I scrape the tooltip or the chart data?
Use chart data for accurate numeric analysis. Scrape the tooltip when the displayed wording or formatting itself is the thing you need to preserve.


