Playwright Screenshot of a DigiLocker Page After Signing In
Capture an authorized DigiLocker page with Playwright after completing its current sign-in verification, then save and protect the resulting screenshot and session state.
To take a screenshot of a DigiLocker page after signing in, use Playwright to open DigiLocker in a browser context, complete the verification required for an account you are authorized to use, confirm that the expected signed-in page loaded, and then call page.screenshot(). DigiLocker currently starts sign-in with a registered mobile number and may ask for a six-digit OTP; its flow can vary by account and change over time. This is an implementation pattern, not a tested recipe or a promise that unattended login will work.
DigiLocker is a Government of India service for storing, sharing, and verifying documents and certificates. Its own site lists 70+ crore registered users and 900+ crore issued documents, updated September 28, 2026; these are service-published figures, not independently audited counts. DigiLocker’s official site.
1. Install Playwright
This example uses Playwright for Python and Chromium. Create a private working directory, install the package and browser, and keep authentication files out of source control.
python -m venv .venv
# Activate the virtual environment for your shell, then:
python -m pip install playwright
python -m playwright install chromium
Add these paths to .gitignore before you create them:
playwright/.auth/
artifacts/
Use a legitimate test account where possible, and get permission before accessing an account or capturing its pages. Do not put passwords, OTPs, mobile or Aadhaar identifiers, cookies, storage-state files, or private document content in source code, repositories, screenshots, or logs.
2. Sign in and capture the page
The official sign-in flow currently requests a registered mobile number. The verification screen asks for a six-digit OTP and offers resend and Aadhaar OTP alternatives. Follow the authorized flow shown to your account; do not assume that a password-only sequence or fixed selectors will work. The code below opens a visible browser for a person to complete verification, waits for an explicit confirmation in the terminal, checks a post-login condition, and saves a full-page PNG.
Before running it, set DIGILOCKER_URL to the current official sign-in URL you have verified. Set POST_LOGIN_SELECTOR to a stable element visible only on the intended signed-in page. These values are configurable because the live route and page structure may change.
import os
from pathlib import Path
from playwright.sync_api import sync_playwright, TimeoutError as PlaywrightTimeoutError
start_url = os.environ["DIGILOCKER_URL"]
post_login_selector = os.environ["POST_LOGIN_SELECTOR"]
output = Path("artifacts/digilocker-page.png")
output.parent.mkdir(parents=True, exist_ok=True)
with sync_playwright() as playwright:
browser = playwright.chromium.launch(headless=False)
context = browser.new_context(viewport={"width": 1440, "height": 1000})
page = context.new_page()
page.goto(start_url, wait_until="domcontentloaded", timeout=60000)
print("Complete the current DigiLocker sign-in and verification in the browser.")
input("When the intended signed-in page is visible, press Enter here: ")
try:
page.locator(post_login_selector).wait_for(state="visible", timeout=15000)
except PlaywrightTimeoutError:
raise RuntimeError(
"The expected signed-in marker did not appear. Check the account flow, "
"current page, and POST_LOGIN_SELECTOR before capturing."
)
print("Current page:", page.url)
page.screenshot(path=str(output), full_page=True, animations="disabled")
print("Saved screenshot:", output)
context.close()
browser.close()
Run it after setting the two environment variables. Use shell-specific syntax to avoid saving sensitive values in shared shell history. The selector is intentionally supplied by you: choose a stable, non-sensitive marker that demonstrates the intended page is loaded, such as a page heading, rather than relying on a guessed selector.
3. Save and reuse authenticated state
Playwright browser contexts isolate browser sessions. Its authentication guidance supports saving storage state and loading it into a later context. This avoids repeating the interactive sign-in for every run, but the saved state is sensitive and can expire. The standard storage-state mechanism does not persist session storage.
After completing a successful authorized sign-in and checking the page, save state to a private path:
auth_dir = Path("playwright/.auth")
auth_dir.mkdir(parents=True, exist_ok=True)
context.storage_state(path=str(auth_dir / "digilocker-state.json"))
For a later capture, create a context with that state. Keep the same post-login check; a state file existing on disk does not prove the session remains valid.
import os
from pathlib import Path
from playwright.sync_api import sync_playwright, TimeoutError as PlaywrightTimeoutError
start_url = os.environ["DIGILOCKER_PAGE_URL"]
post_login_selector = os.environ["POST_LOGIN_SELECTOR"]
state_path = Path("playwright/.auth/digilocker-state.json")
output = Path("artifacts/digilocker-page.png")
output.parent.mkdir(parents=True, exist_ok=True)
with sync_playwright() as playwright:
browser = playwright.chromium.launch()
context = browser.new_context(storage_state=str(state_path))
page = context.new_page()
page.goto(start_url, wait_until="domcontentloaded", timeout=60000)
try:
page.locator(post_login_selector).wait_for(state="visible", timeout=15000)
except PlaywrightTimeoutError:
raise RuntimeError("Login state may have expired, or the page marker changed.")
page.screenshot(path=str(output), full_page=True, animations="disabled")
context.close()
browser.close()
For Playwright Test, the equivalent setup is to create the authenticated state in a setup project and configure a later project to use that state. Follow the official Playwright authentication guide for the current fixture and project configuration. Treat the state file as a credential: restrict access, exclude it from version control and build artifacts, and refresh it through the authorized interactive flow when it expires.
4. Choose screenshot settings
| Need | Setting or approach | Trade-off |
|---|---|---|
| Entire page | full_page=True |
Captures beyond the viewport; very long pages can produce large images and trigger lazy content behavior. |
| Visible viewport only | Omit full_page or set it to False |
More predictable size, but content below the fold is absent. |
| Specific region | page.locator("your-selector").screenshot(path="region.png") |
Requires a stable selector and captures only that element. |
| Consistent layout | Set viewport when creating the context |
Responsive layout changes with viewport dimensions. |
| Reduce animation artifacts | animations="disabled" |
Changes animation state for the capture; use only if that is appropriate for the page. |
| Save authentication | context.storage_state(path=...) |
Convenient across runs, but the file grants access while the session remains valid. |
Playwright also offers screenshot assertions in its test runner for visual regression workflows. An assertion compares against a baseline; it is distinct from simply saving an output image. See the official screenshot assertion documentation. Do not baseline private document content or commit sensitive captures.
5. Wait for the right page
Waiting only for navigation is often insufficient: the page can load before account data renders, and a slow network can delay the marker. Prefer a locator that indicates the expected page is ready. Use a timeout that fits the workflow, and fail visibly if the marker never appears. Avoid arbitrary long sleeps; they make captures slower while still not proving the page is ready.
If content is loaded after scrolling, the page may need a deliberate scroll-and-wait routine before a full-page capture. Use this only when authorized and when the page behavior requires it. Check that the result contains the intended content without exposing private documents in debug output.
6. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| OTP prompt remains open | Verification was not completed, an OTP expired, or the account received a different verification path. | Complete the currently presented authorized flow manually. Do not automate around verification controls or assume a fixed OTP process. |
| Post-login selector times out | The page is not signed in, the marker is wrong, or the page structure changed. | Inspect the visible page manually, choose a stable marker for the expected destination, and verify the current URL. Do not capture on timeout. |
| Saved state redirects to sign-in | The session expired, state was not saved after login, or required session data was not persisted. | Sign in again through the authorized flow, save fresh storage state, and verify it in a new context. Session storage is not included by the standard storage-state mechanism. |
| Navigation timeout | Slow response, network issue, or a page that continues loading background requests. | Start with domcontentloaded, then wait for the specific signed-in marker. Adjust the navigation timeout for the environment and investigate connectivity. |
| Screenshot is blank or incomplete | Capture ran before meaningful content rendered, lazy content was not loaded, or the wrong page was open. | Assert the expected marker first, inspect the destination, and scroll if the authorized page loads content lazily. |
| Browser executable missing | The Playwright package is installed but its browser is not. | Run python -m playwright install chromium in the same environment. |
| Authentication file appears in Git | The ignore rule was added too late or the file was already tracked. | Remove it from tracking, rotate or revoke the affected session if exposed, and keep future state files private. |
7. Reliability, privacy, and cost
Authentication and page markup are controlled by DigiLocker and can change. Make the post-login assertion a hard prerequisite for capture, keep a human-visible sign-in option for account-dependent verification, and refresh expired state deliberately. Do not treat a successful screenshot as proof that a document is authentic or that account data is correct.
Screenshot cost in this DIY approach is the compute and storage you provide for the browser process and image files. Full-page images and repeated browser launches use more memory, time, and disk than viewport captures. Reuse a browser process where appropriate, close contexts after each job, use bounded timeouts, and apply a retention policy to screenshots and authentication files. The screenshot may contain highly sensitive personal information; limit access and delete it when no longer needed.
Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server. Its one-call URL capture is useful for public pages, but it does not sign in to DigiLocker or access a private authenticated account page. Do not send private credentials or private document URLs to an API unless you have confirmed the service and workflow are appropriate for that data. The example below captures a public page:
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
See the ScreenshotNeo API documentation for request options. 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; paid plans start at $5 for 3,000.
Sign up for ScreenshotNeo’s free plan.
FAQ
Can Playwright take a screenshot of a private DigiLocker document?
Technically, a signed-in browser can capture what its authorized account can see. Only capture data you are permitted to access, and handle the image as sensitive personal information.
Does Playwright bypass DigiLocker OTP verification?
No. The workflow here completes the account’s current authorized verification before capturing. DigiLocker may require a six-digit OTP or another account-dependent step.
Can I reuse the same login state indefinitely?
No. Sessions can expire or be invalidated. Check a signed-in page marker on each run and refresh the state through the authorized login flow when necessary.
Will ScreenshotNeo capture my signed-in DigiLocker page?
The one-call example captures a URL and does not perform DigiLocker sign-in. Use the Playwright workflow for an account-specific page, and keep private documents and authentication secrets protected.


