How to Schedule Daily Screenshots of Indian Stock Market Websites Before Opening
Schedule browser captures of Indian stock market pages before NSE pre-open or regular trading, with Playwright, GitHub Actions, or a screenshot API.
To save a daily screenshot of an Indian stock market website before a chosen deadline, automate a browser capture and run it on a schedule. For control over the browser and files, use Playwright with a scheduler such as GitHub Actions. For a simpler setup, use a screenshot API. Choose the deadline carefully: NSE’s equity pre-open session starts at 9:00 a.m. IST and runs until 9:15 a.m.; a capture before regular trading is a different requirement from a capture before pre-open starts.
A screenshot records how a webpage looked when the browser captured it. It is not an official market data feed, and scheduling a screenshot does not guarantee the site has published or loaded its latest market data by that time.
1. Choose what “before opening” means
NSE’s official equity pre-open session is 9:00–9:15 a.m. IST. NSE describes the pre-open process as determining the equilibrium opening price, subject to cases where no price is discovered. If your goal is to record a page before this process begins, schedule the capture before 9:00 a.m. IST and leave time for the page to load. If you only need a capture before continuous trading, the deadline can be later, but a screenshot taken during pre-open may show information from that process.
See [NSE’s pre-open session details](https://www.nseindia.com/static/products-services/equity-market-pre-open). A clock schedule controls when your automation starts; it cannot guarantee when a site updates its data or finishes rendering it.
2. Decide what to capture and where to store it
- List the exact public URLs you need. Check that each page can be viewed without signing in; a scheduled browser or hosted screenshot service may see a login screen instead of the page you expect.
- Choose the capture scope: the visible viewport, the full page, or a specific element such as a market summary panel. Playwright supports all three. Full-page captures can be tall and larger to store; an element capture is useful when you only need one stable region.
- Use a consistent viewport, URL, and capture scope so images are easier to compare day to day. Give each file a date and time in its name.
- Choose a storage and failure-monitoring plan. A local job needs a machine that is on and able to launch a browser. A hosted workflow needs a place to save or download its artifacts and a way to notice failed runs.
- Decide whether you want every calendar day or only weekdays. A weekday cron does not automatically know NSE’s current trading holidays. Check the current official calendar if holiday-aware scheduling matters.
3. Capture pages with Playwright
The following Node.js script takes a full-page PNG of each URL, waits for the page’s load event and then for a short settling interval, and records the capture timestamp and URL in a JSON sidecar file. The settling interval is configurable; it is not proof that market data has finished updating. Replace the example URLs with the public pages you intend to archive.
// save as capture.mjs
import { chromium } from 'playwright';
import { mkdir, writeFile } from 'node:fs/promises';
const urls = [
'https://www.nseindia.com/',
'https://www.bseindia.com/'
];
const outputDir = 'captures';
const settleMs = 3000;
await mkdir(outputDir, { recursive: true });
const browser = await chromium.launch({ headless: true });
const context = await browser.newContext({
viewport: { width: 1440, height: 1000 },
deviceScaleFactor: 1
});
try {
for (const url of urls) {
const page = await context.newPage();
const startedAt = new Date();
try {
await page.goto(url, { waitUntil: 'load', timeout: 45000 });
await page.waitForTimeout(settleMs);
const stamp = startedAt.toISOString().replaceAll(':', '-');
const safeHost = new URL(url).hostname.replaceAll('.', '_');
const file = `${outputDir}/${stamp}_${safeHost}.png`;
await page.screenshot({ path: file, fullPage: true });
await writeFile(`${file}.json`, JSON.stringify({
url,
startedAt: startedAt.toISOString(),
capturedAt: new Date().toISOString(),
scope: 'full-page',
viewport: { width: 1440, height: 1000 }
}, null, 2));
console.log(`Saved ${file}`);
} catch (error) {
console.error(`Capture failed for ${url}:`, error);
process.exitCode = 1;
} finally {
await page.close();
}
}
} finally {
await context.close();
await browser.close();
}
Install and run it:
npm init -y
npm install playwright
npx playwright install chromium
node capture.mjs
Playwright’s [screenshot documentation](https://playwright.dev/docs/screenshots) covers viewport, full-page, and selected-element screenshots. To capture only the viewport, use await page.screenshot({ path: file });. To capture one element, use await page.locator('YOUR_CSS_SELECTOR').screenshot({ path: file });. Replace the selector with a real selector from the page and handle the case where it does not appear before the timeout.
Wait for a meaningful page condition
A fixed delay is easy to understand but can be too short on a slow day and waste time on a fast one. If the page has a stable element that indicates the relevant section rendered, wait for it:
await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 45000 });
await page.locator('YOUR_READY_SELECTOR').waitFor({ state: 'visible', timeout: 20000 });
await page.screenshot({ path: file, fullPage: true });
Use a selector that represents the content you care about, not a generic page shell. If the content is loaded into an iframe, locate it through the appropriate frame. Some pages update continuously or render data through scripts after the main document loads; inspect the resulting images periodically for consent dialogs, login prompts, stale panels, or missing data.
4. Schedule a daily run with GitHub Actions
GitHub Actions supports cron schedules. Scheduled workflows use UTC by default, with time-zone configuration available in current Actions documentation. For a pre-open capture, select an IST time with enough buffer before 9:00 a.m. and avoid relying on a run at the top of an hour for a deadline-sensitive capture. GitHub warns that schedules can be delayed under load, especially at the start of an hour, and queued runs may be dropped under sufficiently high load.
Create .github/workflows/daily-capture.yml:
name: Daily market screenshots
on:
schedule:
# 03:15 UTC is 08:45 IST. Check the current GitHub Actions
# time-zone syntax and your desired local deadline before relying on it.
- cron: '15 3 * * *'
workflow_dispatch:
jobs:
capture:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm ci
- run: npx playwright install --with-deps chromium
- run: node capture.mjs
- name: Save screenshots as workflow artifacts
if: always()
uses: actions/upload-artifact@v4
with:
name: market-captures-${{ github.run_id }}
path: captures/
if-no-files-found: warn
retention-days: 14
The example UTC cron corresponds to 08:45 IST (IST is UTC+5:30). Confirm the schedule and time-zone options against [GitHub’s workflow trigger documentation](https://docs.github.com/en/actions/reference/workflows-and-actions/events-that-trigger-workflows) before using it. Artifact retention is configured here as 14 days; change it to match your archive needs and the repository’s applicable limits. This workflow runs every calendar day. It does not skip exchange holidays.
For a local machine, run the same script from its operating-system scheduler at the chosen local time. Keep the machine awake, ensure the job’s working directory is correct, and direct output to storage that will not fill up. A missed run while the machine is off is not automatically recovered unless you arrange a catch-up job.
5. Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server. One GET request can return an image or PDF. Here is a cURL example that saves a WebP capture:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://www.nseindia.com -o shot.webp
Use the same endpoint from Python:
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://www.nseindia.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
Or from Node.js:
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://www.nseindia.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer())));
See the ScreenshotNeo API documentation for authentication, output formats, scheduling-related options, and the other capture parameters. Put the API key in a secret store when running scheduled jobs; do not commit it to a public repository. You still need a scheduler to make a daily request, or can use a supported scheduled workflow around the API.
- Cookie and consent banners, newsletter popups, and chat widgets are removed before the shot; each cleanup step can be turned off.
- Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed. Response headers report the page verdict and billing status.
- An MCP server provides
take_screenshot,get_page_info, andcapture_pdftools for AI agents and 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 to get 1,000 screenshots a month with no card.
6. Compare the scheduling approaches
| Approach | Control | Scheduling and reliability considerations | Storage and cost considerations |
|---|---|---|---|
| Local Playwright plus OS scheduler | Most control over browser, selectors, files, and network settings. | Your machine must be powered on and available. You manage retries and alerts. | Files stay where you configure them; you manage disk space and backups. |
| Playwright on GitHub Actions | Scriptable browser workflow without maintaining an always-on local machine. | Schedules can be delayed under load; do not treat cron as an exact-time guarantee. Holiday logic is yours to implement. | Artifacts have configured retention; long-term archives need another storage destination. |
| ScreenshotNeo API | HTTP capture with options documented by the service, without installing a browser in your own workflow. | You still need to trigger requests on a schedule and inspect result status and timestamps. | Free: 1,000 per month; paid plans start at $5 for 3,000. See current plans and docs for details. |
| Other hosted schedulers | Less browser infrastructure to maintain; features and controls differ by vendor. | Check time-zone behavior, retention, retries, and whether public pages are rendered logged out. | Confirm current plan requirements and archive limits in vendor documentation. |
Site-Shot documents daily or weekday scheduling using UTC times and says scheduling is part of paid plans; it describes its renderer as visiting public pages logged out. ScreenshotAPI.net describes recurring server-side captures and stored results. These are vendors’ own descriptions, not independent reliability assessments: [Site-Shot scheduling guide](https://www.site-shot.com/blog/automatically-screenshot-website-every-day/) and [ScreenshotAPI.net scheduling](https://www.screenshotapi.net/schedule-website-screenshot).
7. Reliability, performance, and cost
Time and page readiness
Leave a buffer between the scheduled start and the deadline. Navigation, scripts, consent handling, and dynamic data can take longer than expected. Capture time is not the same as data publication time. A selector-based readiness check is generally more informative than assuming a fixed delay means the page is ready, but no page condition can prove the correctness of market data.
Failures and evidence
Use timestamped filenames and retain the URL, run time, and capture status alongside the image. Preserve logs, make failures visible through workflow notifications or your scheduler’s logs, and periodically review samples. A successful image write can still contain a login screen, blocked-access message, stale content, or consent overlay.
Storage and capture cost
Full-page screenshots consume more storage than viewport captures, especially for long pages or high-resolution images. Estimate volume from the number of URLs, captures per month, and average file size; choose image format and retention accordingly. For hosted services, check current plan quotas and retention before relying on them as an archive. ScreenshotNeo’s stated plans are 1,000 free shots a month, then 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, and every feature is on every plan.
8. Troubleshooting
| Symptom | Likely cause | What to check or change |
|---|---|---|
| Capture runs at the wrong time | UTC/local-time conversion or scheduler time-zone assumptions. | Convert the intended IST time explicitly, verify the workflow’s time-zone setting, and inspect actual run timestamps. Remember GitHub schedule starts may be delayed. |
| Screenshot shows a blank page or spinner | Page scripts or data were still loading, or a request failed. | Inspect logs and network conditions; wait for a meaningful content selector, raise timeouts where appropriate, and keep the run well before the deadline. |
| Screenshot shows a consent prompt, login page, or CAPTCHA | The page requires interaction, authentication, or blocks automated access. | Confirm the URL is public and allowed to be captured. Avoid storing credentials in code. Do not treat a challenge page as a successful market-page capture. |
| Element screenshot times out | The selector changed, is hidden, or is inside a frame. | Inspect the current page structure, update the selector, wait for visibility, and handle a missing element as a failed capture. |
| No artifact or image appears | Wrong output path, job failure before upload, or files were written outside the workspace. | Check the job working directory and logs; upload the correct directory even on failure and verify artifact retention. |
| Capture misses a holiday or includes weekends | A generic daily or weekday schedule does not apply the exchange calendar. | Choose whether weekend captures are useful. For exchange holidays, consult the current official calendar or add maintained holiday logic. |
| Different pages look different day to day | Dynamic content, viewport changes, loading variation, or site redesign. | Keep URL, viewport, and capture scope stable; record timestamps and inspect layout changes before interpreting differences. |
| API request returns an error or unexpected image | Invalid key, unsupported parameter, inaccessible page, or capture result indicating a failed load. | Check the API response and its verdict/billing headers, verify the URL and key, and consult the current API docs. Do not assume every response represents a clean market page. |
9. FAQ
Should I schedule for every day or only trading days?
Daily captures are useful when you want a continuous visual archive. A weekday schedule is simpler but can still run on exchange holidays. Holiday-aware scheduling needs a current calendar source and maintained logic.
Can a screenshot prove the official opening price?
No. It records what a particular webpage displayed at a particular time. Use official exchange data sources for authoritative market information.
Can I save only the chart or market summary?
Yes. Use Playwright’s locator screenshot method with a selector for that element, or an API option that captures a CSS-selected element.
Will a hosted service see my signed-in page?
Do not assume so. The referenced hosted schedulers describe public, logged-out rendering. For pages requiring authentication, review the service’s current supported authentication and storage behavior before using it.


