How to Capture Bulk Screenshots of Indian Insurance Quote Pages
Use Playwright to capture consistent, full-page screenshots of authorized insurance quote views, with masking, careful file handling, and a review step.
For multiple insurance quote pages you are authorized to access, use a browser automation script to reach each quote result, wait for its contents to render, and save a full-page screenshot. Playwright’s page.screenshot({ fullPage: true }) captures the page’s full scrollable area rather than only the visible viewport. Use a consistent viewport, mask unnecessary personal fields, record failures, and review each image before sharing it.
This guide uses Node.js and Playwright. It shows a reusable bulk capture script, plus Python and cURL examples for the ScreenshotNeo API. A screenshot records what rendered at that moment; it does not prove that a quote is still valid, complete, or accurate.
1. Confirm scope and authorization
Write down the pages, quote scenarios, and result states you need. Use only pages and test or customer data you are authorized to access. Check each site’s terms and your organization’s policies before automating it. If the task is documentation, avoid automating purchase, submission, or lead-sharing steps.
Insurance quote pages can contain identifying or health-related details. Indian insurance web-aggregator materials discuss notices about sharing prospect particulars with insurers and the confidentiality and security of prospect information. Minimize the data captured, use synthetic data where practical, restrict access to the resulting files, and verify current rules and requirements before relying on regulatory material as legal guidance. See the [IRDAI web aggregator materials](https://irdai.gov.in/document-detail?documentId=385574) and its [insurance web aggregators overview](https://irdai.gov.in/insurance-web-aggregators).
2. Set up Playwright
Install Node.js, create a project, and install Playwright. The code below uses Chromium. Playwright’s documented screenshot options include full-page capture, element masking, clipping, formats, and scale. Read the [Playwright Page API documentation](https://playwright.dev/docs/api/class-page#page-screenshot) for the current options.
mkdir quote-captures
cd quote-captures
npm init -y
npm install playwright
npx playwright install chromium
Create capture-quotes.mjs with the following script. Set QUOTE_URLS to a JSON array of authorized result-page URLs. The script visits one URL at a time, waits for a chosen result selector, masks sensitive fields if the selectors exist, saves a full-page PNG, and writes a JSONL record for either success or failure.
import { chromium } from 'playwright';
import { mkdir, appendFile } from 'node:fs/promises';
const urls = JSON.parse(process.env.QUOTE_URLS ?? '[]');
const readySelector = process.env.READY_SELECTOR ?? 'body';
const maskSelectors = (process.env.MASK_SELECTORS ?? '')
.split(',')
.map((s) => s.trim())
.filter(Boolean);
const outputDir = process.env.OUTPUT_DIR ?? 'captures';
const timeoutMs = Number(process.env.TIMEOUT_MS ?? 30000);
if (!Array.isArray(urls) || urls.length === 0) {
throw new Error('Set QUOTE_URLS to a non-empty JSON array of authorized URLs.');
}
await mkdir(outputDir, { recursive: true });
const browser = await chromium.launch({ headless: true });
const context = await browser.newContext({
viewport: { width: 1440, height: 1000 },
deviceScaleFactor: 1,
});
const page = await context.newPage();
function safePart(value) {
return String(value).replace(/[^a-z0-9_-]+/gi, '-').replace(/^-+|-+$/g, '').slice(0, 80) || 'quote';
}
try {
for (let i = 0; i < urls.length; i++) {
const url = urls[i];
const capturedAt = new Date().toISOString();
const filename = `${String(i + 1).padStart(3, '0')}-${safePart(new URL(url).hostname)}-${capturedAt.replace(/[:.]/g, '-')}.png`;
const path = `${outputDir}/${filename}`;
try {
const response = await page.goto(url, { waitUntil: 'domcontentloaded', timeout: timeoutMs });
if (!response || !response.ok()) {
throw new Error(`Navigation returned ${response?.status() ?? 'no HTTP response'}`);
}
await page.locator(readySelector).waitFor({ state: 'visible', timeout: timeoutMs });
for (const selector of maskSelectors) {
const locator = page.locator(selector);
if (await locator.count()) {
await locator.evaluateAll((els) => els.forEach((el) => {
el.style.setProperty('visibility', 'hidden', 'important');
}));
}
}
await page.screenshot({ path, fullPage: true, type: 'png' });
await appendFile(`${outputDir}/manifest.jsonl`, JSON.stringify({
index: i + 1, url, capturedAt, path, status: 'captured', httpStatus: response.status(),
}) + '\n');
} catch (error) {
await appendFile(`${outputDir}/manifest.jsonl`, JSON.stringify({
index: i + 1, url, capturedAt, status: 'failed', error: String(error),
}) + '\n');
}
}
} finally {
await context.close();
await browser.close();
}
Example invocation:
QUOTE_URLS='["https://example.com/authorized-quote-result"]' \
READY_SELECTOR='[data-testid="quote-results"]' \
MASK_SELECTORS='input[name="email"],.customer-phone' \
node capture-quotes.mjs
Replace the example URL and selectors with values you are permitted to use on the target site. A body selector only confirms that the document body exists; it does not establish that quote results have loaded. Prefer a selector tied to the finished results state.
3. Choose the right wait and screenshot options
Use a readiness condition that matches the page rather than relying on a fixed delay alone. A fixed delay may be useful for a known animation or late widget, but it can be either wasteful or too short. Playwright documents navigation and locator waiting in its [Page API](https://playwright.dev/docs/api/class-page) and [Locator API](https://playwright.dev/docs/api/class-locator).
fullPage: truecaptures the full scrollable document. This is useful for long quote results, though exceptionally long pages can produce large images.locator.screenshot()captures a particular element, such as a comparison table. Check that the element is not internally scrollable or clipped before choosing this approach.maskcan visually cover matched elements in screenshots. For selector-dependent or sensitive fields, verify the output image. Hiding a field through page styles, as in the example, is a simple alternative, but neither method erases the underlying page data or secures the saved image.typesupports PNG, JPEG, or WebP where supported; choose a format based on downstream needs. PNG is lossless and often appropriate for text-heavy tables.scalecontrols whether output follows CSS pixels or device pixels. Keep viewport and scale consistent across a comparison set.clipcaptures a chosen rectangle instead of the entire page when a focused region is more useful.
For lazy-loaded content, first scroll through the page or wait for the relevant section to appear, then capture. Full-page mode describes the capture area; it does not guarantee that every lazy image or asynchronously loaded quote component has finished rendering. Inspect results for sticky controls repeated in odd positions, omitted table rows, and blank sections.
4. Make the batch comparable and auditable
Use the same browser, viewport dimensions, device scale, capture format, wait condition, and quote scenario for every page in a comparison set. Give each image a deterministic identifier that does not expose a customer’s identity. The script uses hostname, sequence number, and UTC time; you can add a synthetic scenario ID or product category if that is useful.
Keep any mapping between a scenario and a real person separately with access controls. Review the saved screenshot itself before distributing it. A useful review checks that the premium, policy term, eligibility details, benefits, exclusions, and conditions needed for the task are visible. Older IRDAI web-aggregator guidance discussed comparison fields such as premiums, policy terms, exclusions, and conditions; treat that 2011 guidance as historical context and verify current requirements before presenting it as a current legal checklist.
5. Python and cURL options with ScreenshotNeo
For a service-based workflow, ScreenshotNeo accepts a URL in one GET request and returns an image or PDF. Its parameters include full-page capture, element selection, masking by hiding selectors, wait conditions, viewport and device presets, image format, and more. The [ScreenshotNeo documentation](https://screenshotneo.com/docs/) lists the API options. Use only URLs you are authorized to capture, and do not pass personal quote URLs or credentials unless your handling rules permit it.
Python example:
import requests
url = "https://example.com/authorized-quote-result"
params = {
"access_key": "YOUR_API_KEY",
"url": url,
"full_page": "true",
"format": "png",
}
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params=params,
timeout=90,
)
r.raise_for_status()
with open("quote.png", "wb") as f:
f.write(r.content)
cURL example:
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://example.com/authorized-quote-result \
-d full_page=true \
-d format=png \
-o quote.png
For a batch, call the endpoint once per URL and give each output a scenario-specific filename. ScreenshotNeo also supports bulk capture of up to 100 URLs per call; consult its docs for the request shape and options. Do not assume that a service can access authenticated quote states unless you have configured supported authorization or session inputs appropriately.
6. Or skip the browser setup
ScreenshotNeo offers a one-call screenshot API and an MCP server for AI agents. For a quote page, adapt the target URL and requested output format:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com/authorized-quote-result -o quote.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://example.com/authorized-quote-result"}, timeout=90)
open("quote.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://example.com/authorized-quote-result' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await Bun.write('quote.webp', new Uint8Array(await res.arrayBuffer()));
See [ScreenshotNeo](https://screenshotneo.com) and the [API documentation](https://screenshotneo.com/docs/). Cookie banners, popups, and chat widgets are removed before the shot; bot checks, blank pages, and failed loads are never billed. Its MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card, and paid plans start at $5 for 3,000. [Create a free ScreenshotNeo account](https://screenshotneo.com/account/sign-up/) and capture up to 1,000 screenshots a month at no charge.
7. Troubleshooting
| Symptom | Likely cause | What to do |
|---|---|---|
| Screenshot contains a loading state or no quote rows | The readiness selector matches too early, or results load asynchronously. | Wait for a selector unique to completed results; inspect the page state before capture and record a failure when the expected state never appears. |
| Navigation times out | The page is slow, blocked, or waiting for a network state that never settles. | Use a deliberate timeout and a narrower readiness condition. Do not silently save the error page as a successful quote capture. |
| Quote URL redirects to sign-in or a different state | The page depends on a session, cookies, or a prior flow. | Reach the result through an authorized session workflow. Avoid embedding customer credentials in scripts or logs; follow the site’s terms and your organization’s controls. |
| Personal fields remain visible | The selector did not match the rendered field, or the field is drawn in a different component. | Inspect the DOM and screenshot using approved test data, correct the selector, then inspect the produced image before access or distribution. |
| Long table is clipped or hard to read | The table scrolls internally, content is wider than the viewport, or full-page capture is not the best view. | Capture the table element, adjust the viewport consistently, or save both a full page and a focused table image. Review the full output dimensions and content. |
| Lazy-loaded sections are absent | Those sections load only after scrolling or becoming visible. | Scroll the page or target section into view and wait for its content before capture; then verify those sections in the image. |
| One failed page stops the whole batch | The loop does not isolate per-URL errors. | Catch errors per page, write a failure record, and continue. Retry only failures after checking whether repeating the navigation is appropriate. |
| Image files overwrite one another | Filenames do not include a unique scenario or capture identifier. | Use a sequence or stable scenario key plus UTC timestamp, and keep the manifest alongside the files. |
| ScreenshotNeo request returns an error or unexpected file | The key, URL encoding, option name, or requested format may be wrong; the target may also return a bot check or blank page. | Check the response status and headers, confirm the current option names in the docs, URL-encode the target, and inspect the returned image before treating it as a quote capture. |
8. Performance, reliability, and cost
Serial capture is slower than running multiple browser contexts, but it limits concurrent sessions and simplifies per-page failure records. Start serially. If you increase concurrency, do so only when permitted by the target site and your own systems; keep a bounded number of pages, use timeouts, and avoid bursts that could interfere with service. Screenshot size and full-page height affect memory, storage, and transfer time.
For reliability, record the URL or scenario identifier, timestamp, HTTP status, and outcome; distinguish successful captures from navigation or readiness failures. Keep a small dry run before a large batch. Page layout, quote content, and site automation rules can change, so repeat review when the workflow or target changes.
With Playwright, the direct costs are the compute and storage resources used to run and retain captures; no fixed price is implied here. ScreenshotNeo pricing is Free for 1,000 shots per month, Starter $5 for 3,000, Growth $15 for 15,000, Pro $39 for 60,000, Scale $99 for 250,000, and Business $249 for 1,000,000; yearly billing gives two months free. Every feature is on every plan. Only clean shots are billed: bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing. Responses identify page verdict and billing information in headers. Confirm current options and billing details in the [documentation](https://screenshotneo.com/docs/).
9. Frequently asked questions
Does a screenshot prove the quote is still available?
No. It records the rendered page at capture time. Preserve the timestamp and the quote scenario, and confirm validity through the relevant insurer or authorized workflow.
Can I compare premiums from different screenshots directly?
Only if the underlying scenarios and assumptions match. Keep inputs such as product, cover, term, eligibility, and relevant conditions consistent, and document any differences.
Should I share the manifest with the images?
Share only the metadata needed by recipients. A manifest can contain URLs or scenario details that are sensitive too, so apply the same access controls as for the captures.
Is automating an insurer or aggregator site always allowed?
No. The available API documentation does not establish permission or compatibility for any specific insurer site. Check the applicable terms, authorization, and current policies first.


