Screenshot APIs are typically used for generating previews, monitoring websites, automating visual content. But there's one use case that keeps coming up in requests: monthly investor reports. You need to capture several dashboards, assemble them into a PDF or slide deck, and send it to the board. Doing it manually takes a couple of hours and requires no real skill — just dashboard access and patience.
An investor reporting screenshot API turns that monthly routine into a cron job. Point it at each dashboard URL, pass the login cookies, wait for the charts to render, get back a PNG or PDF. Same captures, same resolution, same schedule, with nobody manually resizing anything. Below we'll walk through the workflow, the code, and where common BI platforms make this harder than it should be.
Who actually needs automated investor report screenshots
Startup founders assembling monthly investor updates are the most obvious audience. Series A through C, using Stripe for MRR, Mixpanel for engagement, HubSpot for pipeline, Google Analytics for traffic, and an internal Metabase or Grafana for custom KPIs. Five dashboards, five logins, five screenshots, one slide deck. Every month.
FP&A analysts and controllers at growth-stage companies own the board deck process. They pull data from accounting, CRM, HR analytics, and product dashboards. Most of their time goes to pulling and formatting data, not analyzing it. The screenshot-paste-resize step is the dumbest part of the job, and it eats the most time.
RevOps leads building internal reporting pipelines are already technical enough to use an API. They're looking for the last-mile solution: get the dashboard visual into Slack or email without rebuilding the dashboard in Databox or Geckoboard. Agency teams sending white-labeled client reporting decks hit the same problem, just multiplied across accounts.
The 5-dashboard shuffle: why manual report screenshots break
I've seen this exact scenario play out. Forgot to update one chart and sent October's pipeline numbers in the November deck. Nobody caught it for two board meetings. Screenshots taken at different zoom levels, so the MRR chart is crisp and the retention chart is blurry. One dashboard was in dark mode, the rest in light. Cropped the wrong date range on the Mixpanel screenshot. The process lives in one person's head, and when they're on vacation, the report is late or wrong.
Smart people doing screenshot-paste-resize instead of analysis. It's demoralizing, and it happens because most BI tools have terrible image export options.
Why BI platform exports don't solve this
You'd think dashboard platforms would have "export as image" figured out by now. Most don't, or they hide it behind expensive tiers.
Grafana deprecated its Image Renderer plugin in September 2025 and removed it entirely in Grafana 13. The replacement requires deploying a separate rendering service. Grafana Enterprise includes built-in reporting with scheduled PDF, but that starts at $25,000 per year. Skedler, a third-party alternative, costs $2,388/year. For a startup sending monthly investor updates, those numbers don't make sense.
Metabase's PDF export is a frontend-only implementation. It literally screenshots the page in the browser. There's no API endpoint for it. Large dashboards with 40+ cards get cut off at a fixed height. No pagination, no card-level PDF export.
Power BI can't export dashboards (as opposed to reports) to PNG via API at all. Report exports require Premium, Embedded, or Fabric capacity. Pro workspaces don't support it. And the exports include all the UI chrome: navigation buttons, filter panels, slicers, with no crop option.
Looker Studio only added chart-level PNG export in March 2026 and still has no batch export and no scheduling. One chart at a time, manually. Not available for Google Maps, Sankey, or Timeline charts.
A screenshot API sidesteps all of this. It captures what the browser renders, regardless of platform. One API, not five export mechanisms. No enterprise license required.
Capturing dashboards for investor reports with the API
The pattern is the same for any dashboard: authenticate, wait for charts, capture. This Python script screenshots three dashboards and saves them as PNGs for a monthly board deck:
import requests, os
API_KEY = os.environ['SCREENSHOTRUN_API_KEY']
GRAFANA_SESSION = os.environ['GRAFANA_SESSION']
METABASE_SESSION = os.environ['METABASE_SESSION']
dashboards = [
{
'name': 'revenue',
'url': 'https://grafana.company.com/d/abc123/revenue?orgId=1',
'cookies': [{'name': 'grafana_session', 'value': GRAFANA_SESSION,
'domain': 'grafana.company.com'}],
'wait_for': '.react-grid-layout',
},
{
'name': 'product-metrics',
'url': 'https://metabase.company.com/dashboard/42',
'cookies': [{'name': 'metabase.DEVICE', 'value': METABASE_SESSION,
'domain': 'metabase.company.com'}],
'wait_for': '.DashCard',
},
{
'name': 'pipeline',
'url': 'https://analytics.company.com/dashboard/pipeline',
'cookies': [{'name': 'session', 'value': os.environ['ANALYTICS_SESSION'],
'domain': 'analytics.company.com'}],
'wait_for': '.chart-container',
},
]
for dash in dashboards:
params = {
'url': dash['url'],
'cookies': str(dash['cookies']),
'wait_for_selector': dash['wait_for'],
'full_page': 'true',
'width': '1920',
'retina': 'true',
'format': 'png',
'delay': '3',
'cache_ttl': '0',
}
resp = requests.get(
'https://api.screenshotrun.com/v1/screenshots/capture',
headers={'Authorization': f'Bearer {API_KEY}'},
params=params,
)
with open(f'board-deck/{dash["name"]}.png', 'wb') as f:
f.write(resp.content)
print(f'Captured {dash["name"]} ({len(resp.content) // 1024} KB)')
The cache_ttl=0 forces a fresh capture every time, because you don't want cached data in a board deck. wait_for_selector pauses the capture until a specific DOM element appears — in this case, the chart container. The extra 3-second delay covers the gap between the container existing and the actual data series populating inside it. Without both, you'll occasionally get charts with axes but no lines. And retina=true captures at double resolution — investors often view decks on Retina MacBooks or large conference displays, and 1x screenshots look noticeably soft at those sizes.
For the full platform-by-platform setup (Grafana selectors, Metabase cookies, Tableau auth), see the dashboard screenshots guide.
From screenshots to a finished board deck
Capturing the images is half the job. The other half is getting them into a format investors actually open.
The simplest output is PDF. Switch format=png to format=pdf and add pdf_landscape=true. Each dashboard renders as a single landscape page. Merge them with a tool like pdfunite or PyPDF2 into one board deck file. The PDF export feature handles page formatting.
If your team lives in Google Slides, use the API to capture PNGs, upload them to Google Drive, then insert them into a Slides template via Apps Script. The template has placeholder slides for each dashboard. The script replaces the images and updates the date label. Run it on a monthly trigger — or wire it through Zapier or n8n if you'd rather skip writing Apps Script.
For Slack or email delivery, capture the dashboards and POST the images to a Slack channel or attach them to an email via SendGrid/SES. The Python integration shows the full HTTP flow. Pass webhook_url to run captures in parallel — each finished screenshot gets POSTed to your server without blocking the rest of the batch.
Consistent screenshots across dashboards for investor decks
Investor decks look sloppy when each dashboard screenshot has different chrome, different themes, and different sizing. A few parameters fix that.
Inject custom CSS to hide navigation bars, sidebars, and headers that clutter the capture. For Grafana, css=.navbar,.sidemenu{display:none !important} strips the UI down to just the panels. For Metabase, hide the top nav with css=.Nav{display:none !important}.
Force a consistent theme with dark_mode=false across all captures, even if some team members have their dashboards set to dark mode. Set width=1920 on every request so charts render at the same scale. Use retina=true consistently — mixing 1x and 2x images in a deck is immediately visible.
Need a single panel instead of the full dashboard? The dashboard screenshots guide covers element-level capture with the selector parameter.
Where screenshot automation ends and investor narrative begins
Honestly, the screenshots are just the visual layer. A screenshot API doesn't write the narrative section of your investor update. It doesn't calculate burn rate or runway. It doesn't tell your investors what happened last month and what you plan to do about it.
Tools like Visible ($59-249/month) and Cabal ($19/month) handle the narrative and CRM side of investor updates: metric ingestion from Stripe and QuickBooks, templated email updates, investor engagement tracking. They're good at what they do. A screenshot API complements them — it captures the visual dashboards those tools don't have access to, because your internal Grafana or Metabase isn't connected to Visible's data pipeline.
Dashboards behind SSO or 2FA require extracting session cookies after authentication, which adds a step to the automation. The authenticated screenshots guide covers the cookie and header setup in detail.
Start automating your board report screenshots
Pick one dashboard — the one you screenshot most often for investor updates. Capture it with full_page=true, retina=true, and wait_for_selector targeting a chart element. Compare the result to your manual screenshot. The difference in deck quality is noticeable from the first capture.
The free plan covers 200 captures monthly — more than enough for board report screenshot automation. A typical investor deck captures 5-10 dashboards, well within that limit.
FAQ#
Pass session cookies or authorization headers in the API request. The screenshot API forwards them to the target URL, bypassing the login page. For Grafana, pass the grafana_session cookie. For Metabase, use the metabase.DEVICE cookie. The authenticated screenshots guide covers the full setup for cookies, headers, and localStorage-based auth.
Dashboard charts render asynchronously — the page shell loads first, then API calls fetch data, then the charting library draws visualizations. Use wait_for_selector to target a chart element that only exists after rendering completes, plus a delay of 2-5 seconds for the data series to populate. Without both, you get chart containers with axes but no data.
Yes. The screenshot API works with any dashboard accessible via URL: Grafana, Metabase, Looker Studio, Power BI, Tableau, or custom internal tools. Each capture is an independent API call with its own URL, cookies, and wait parameters. A single script can loop through all your dashboards regardless of platform.
Set width=1920 and retina=true on every request for uniform resolution. Use dark_mode=false to enforce a consistent theme. Inject custom CSS to hide navigation bars and sidebars that vary between platforms. These parameters produce clean, identically-sized captures that look professional in a board deck.
Significantly. Grafana Enterprise with built-in reporting starts at $25,000/year. Skedler costs $2,388/year. Dedicated investor update tools like Visible run $59-249/month. The ScreenshotRun free tier includes 200 captures per month at no cost, and paid plans start at $9/month. A typical board deck workflow uses 5-10 captures monthly.