How to Capture Competitor SERP Screenshots for Indian Education Keywords
Capture and compare Indian education SERPs consistently. Set the query, location, language and device; keep a useful log; understand what a screenshot can prove.
Short answer: To capture a competitor SERP you can compare later, hold the query, search engine, target location, language, device, and capture time context constant. Capture the whole relevant results page, save the image with those settings in its filename, and keep a log beside it. A screenshot is visual evidence of one result page at one moment; it is not a complete ranking history or proof of what every student in India sees.
This workflow is for SEO practitioners and education marketers comparing results for queries such as course, exam, college, coaching, admissions, and scholarship searches. It covers a manual browser capture, a repeatable Playwright setup, what to record, and the limits of screenshots.
1. Define the comparison before you search
Write down the exact query and the audience context first. For example, decide whether you are looking at an India-wide result or a local education market such as Delhi or Bengaluru. An India-wide view should not stand in for every city. Treat mobile and desktop as separate views, and record the search language even when the query is written in English.
| Setting | What to record | Why it matters |
|---|---|---|
| Query | Exact spelling, script, word order, and punctuation as entered | Small query changes can change the result page. |
| Market | India-wide or named city/region | Local intent and location can change what appears. |
| Language | Search interface/result language, plus the query language when relevant | Do not assume an English interface means the same result set as a regional-language search. |
| Device | Desktop or mobile; record viewport or device preset if using one | Layout and visible modules differ by screen size. |
| Engine | Google, Bing, or the engine being compared | Never combine result pages from different engines as one comparison. |
| Time | Date, time, and time zone | A capture represents a moment; later checks may differ. |
| Method | Manual browser, extension, script, or capture service | Method and partial-page behavior affect what the image contains. |
Manual searches are affected by location, device, and search history, according to vendor-authored India rank-tracking guidance from SerpObserver. Treat that as vendor guidance rather than an independent study. Incognito mode can reduce some history-based personalization, but it does not set a target city or make a local result universal.
2. Capture the whole relevant results page
- Open the target search engine and enter the recorded query without silently changing spelling or language.
- Set the location and device context as closely as your chosen method allows. Record what you actually set; do not label a country-level setting as a city-level capture.
- Capture the full relevant results page. Include visible organic results, ads, and modules such as image or video results and question panels when present. If the page loads additional content as you scroll, note whether the capture includes it.
- Check that the image is legible and not cut off. Mark partial captures in the log instead of treating them as complete.
- Repeat each comparison under the same settings. If a setting changes, label the new run rather than attributing every visual difference to competitor movement.
A Chrome Web Store listing describes SERPshot as an extension for full-page Google and Bing captures, desktop/mobile screenshots, and capture metadata. Those are listing claims, not an independent evaluation. Before using any extension, verify its current listing status, requested permissions, available features, and behavior for the Indian locale you need: SERPshot listing.
3. Use a repeatable browser capture with Playwright
For a controlled local workflow, Playwright can open a search URL and save a full-page screenshot. This example records the query and settings in the filename and log. It sets the browser language and timezone, and adds Google’s language and country query parameters. gl=in is a country hint; it does not select an Indian city or guarantee that Google will return a particular location’s results. A viewport emulates screen dimensions, not a physical phone or a real user’s search context.
Use a normal browser session and follow the search engine’s terms and access controls. If a CAPTCHA or other challenge appears, stop and record that the run was blocked; do not attempt to bypass it. The script does not claim to extract structured rankings.
Install
mkdir serp-capture
cd serp-capture
npm init -y
npm install playwright
npx playwright install chromium
Save as capture-serp.mjs
import { chromium } from 'playwright';
import { appendFile, mkdir } from 'node:fs/promises';
const query = process.argv[2];
if (!query) {
console.error('Usage: node capture-serp.mjs "exact search query"');
process.exit(1);
}
const settings = {
market: process.env.MARKET ?? 'India-wide',
gl: process.env.GOOGLE_GL ?? 'in',
hl: process.env.GOOGLE_HL ?? 'en',
device: process.env.DEVICE ?? 'desktop',
timezone: process.env.TIMEZONE ?? 'Asia/Kolkata',
project: process.env.PROJECT ?? 'education-serp',
};
const mobile = settings.device === 'mobile';
const viewport = mobile
? { width: 390, height: 844 }
: { width: 1440, height: 1000 };
const now = new Date();
const stamp = now.toISOString().replaceAll(':', '-').replaceAll('.', '-');
const safe = (s) => s.normalize('NFKD').replace(/[^a-zA-Z0-9]+/g, '-').replace(/^-|-$/g, '').toLowerCase();
const filename = `${safe(settings.project)}_${safe(query)}_${safe(settings.market)}_${settings.device}_${stamp}.png`;
await mkdir('captures', { recursive: true });
const browser = await chromium.launch({ headless: true });
const context = await browser.newContext({
viewport,
deviceScaleFactor: mobile ? 2 : 1,
isMobile: mobile,
hasTouch: mobile,
locale: settings.hl,
timezoneId: settings.timezone,
});
const page = await context.newPage();
const url = new URL('https://www.google.com/search');
url.searchParams.set('q', query);
url.searchParams.set('gl', settings.gl);
url.searchParams.set('hl', settings.hl);
let status = 'captured';
let note = '';
try {
await page.goto(url.toString(), { waitUntil: 'domcontentloaded', timeout: 60000 });
await page.waitForTimeout(2500);
const pageText = (await page.locator('body').innerText().catch(() => '')).toLowerCase();
if (pageText.includes('unusual traffic') || pageText.includes('captcha')) {
status = 'blocked-or-challenged';
note = 'A challenge may be present; inspect the page and do not treat this as a valid SERP capture.';
}
await page.screenshot({ path: `captures/${filename}`, fullPage: true, animations: 'disabled', timeout: 60000 });
} catch (error) {
status = 'failed';
note = String(error);
throw error;
} finally {
const entry = {
query,
market: settings.market,
location_parameter: settings.gl,
language_parameter: settings.hl,
device: settings.device,
viewport,
engine: 'Google',
captured_at: now.toISOString(),
timezone: settings.timezone,
method: 'Playwright fullPage screenshot',
filename,
status,
note,
};
await appendFile('captures/capture-log.jsonl', `${JSON.stringify(entry)}\n`);
await browser.close();
}
console.log(`Saved captures/${filename}`);
Run one capture
PROJECT=admissions-audit MARKET=India-wide DEVICE=desktop node capture-serp.mjs "engineering entrance exam coaching"
PROJECT=admissions-audit MARKET=Delhi DEVICE=mobile GOOGLE_GL=in GOOGLE_HL=en node capture-serp.mjs "engineering entrance exam coaching"
The example’s city label is a note for your project, not proof that the request was geolocated to that city. If the browser or search page does not expose a reliable city control, record the run as country-level or use a location-aware rank-tracking workflow that explicitly supports the city. Do not infer city precision from a VPN, language setting, or filename.
4. Name files and keep a capture log
Use a filename that makes a folder of captures searchable. Keep a JSONL or spreadsheet log with one row per image. The script writes a JSONL record; for manual captures, use the same fields and record any unusual module, missing content, or page challenge.
client-query-market-device-YYYY-MM-DD-HH-mm-timezone.png
admissions-audit-engineering-entrance-exam-coaching-india-wide-desktop-2026-10-04-09-30-Asia-Kolkata.png
| Log field | Example or note |
|---|---|
| Query | Exact query text, including script and spelling |
| Market and location evidence | India-wide; or city plus the actual location mechanism used |
| Language and engine | Language setting and Google/Bing/etc. |
| Device and viewport | Desktop/1440×1000 or mobile/390×844 |
| Capture time | Timestamp plus explicit timezone |
| Image and method | Filename, browser/tool, full-page or partial |
| Notes | Ads, visible SERP features, challenge, truncation, or other anomaly |
5. Compare like with like
For each matched image, compare the visible competitor domains and landing-page URLs, their order, paid placements, and prominent SERP features. Record the position as visible in that capture and keep organic results distinct from ads and other modules. A comparison table can use these columns:
| Query | Market/language | Device | Capture time | Organic order and URL | Ads/features | Capture quality |
|---|---|---|---|---|---|---|
| Exact query | City or India-wide; language | Desktop or mobile | Date, time, zone | Visible domain and destination URL | Ad and module notes | Complete, partial, or challenged |
Use the same settings for each competitor or date in a comparison. A result’s visible order is a snapshot of one page at one time, not a permanent ranking or a universal view across Indian cities and students. If you need exact rank history or machine-readable URLs and positions, keep a structured tracking record alongside the screenshot and use an appropriate rank-tracking method.
6. Know what screenshots can and cannot establish
A screenshot records what was rendered in the captured browser view. It can document visible competitors, result order, ads, and SERP features, but it does not establish another site’s clicks, impressions, CTR, or private Search Console data.
Google’s URL Inspection documentation describes a screenshot of a page retrieved by Google in a crawl view or live test. That is a rendering of a web page, not a screenshot of a competitor’s Google SERP. Google’s Search Console performance reports describe a property’s search performance, including clicks, impressions, CTR, and some query or position information with country and device filters. They apply to the verified property; they do not disclose another site’s private performance data.
7. Troubleshooting
| Problem | Likely cause | What to do |
|---|---|---|
| The same query shows different results on repeat | Location, device, search history, timing, or other context changed. | Compare the logged settings first. Repeat with the same query and context, and describe the difference as observed rather than universal. |
| A city capture looks like a generic India result | The method only specified country or language, or did not actually set city location. | Record the actual location control used. Label it country-level if city targeting is unavailable; use a workflow that explicitly supports the required city. |
| Mobile image is just a narrow desktop page | Only viewport width changed; the browser context was not configured for mobile emulation. | Use the script’s mobile context or a real device, and record which method was used. Do not treat emulation as identical to every phone. |
| Screenshot ends before lower results | The page is long, content loads while scrolling, or full-page capture missed lazy content. | Inspect the saved image, scroll/load relevant content before capture when needed, or mark the capture partial. Never compare a partial page as complete. |
| CAPTCHA, unusual traffic, or interstitial appears | The search engine challenged the request or browser session. | Stop the run, record it as challenged, and use an allowed manual or supported workflow later. Do not bypass the challenge or count it as a SERP. |
| Playwright reports navigation timeout | Slow network, delayed page resources, or an interrupted request. | Check connectivity and whether the page loaded despite the timeout. Retry manually or increase the navigation timeout for a permitted workflow; retain a failed status in the log if no valid image exists. |
| Images differ because of ads or modules | Those parts of the page changed or were served differently between captures. | Keep the screenshots and annotate the change. Compare organic order separately from ad placements and SERP features. |
| Image is too large to share or review | Full-page output can be tall, especially on mobile. | Keep the original as evidence, make a clearly labeled review copy if needed, and do not crop away context without noting it. |
8. Performance, reliability, and cost
A manual capture has little setup cost, but repeating it across many keywords, cities, devices, and dates takes time and requires careful consistency. A browser script makes viewport, language, filename, and logging repeatable, but it cannot by itself guarantee city-level geolocation or identical search results. Search pages can change between runs, and a CAPTCHA or incomplete page makes an invalid comparison. Keep failed and partial runs visible in the log rather than silently omitting them.
For ongoing rank history or structured positions, use a method designed to retain those records; a screenshot remains useful as visual context. Search Console is useful for your own verified property, not competitor metrics. If considering a browser extension, check its current permissions and Indian locale support before relying on it.
Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server for developers. One GET request returns a screenshot or PDF. For a reproducible Google search URL, build the exact query and country/language parameters first; those parameters do not guarantee a city-specific SERP. Use a city-capable search workflow when city precision is required. See the ScreenshotNeo API documentation.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url="https://www.google.com/search?q=engineering+entrance+exam+coaching&hl=en&gl=in" -o shot.webp
import requests
url = "https://www.google.com/search?q=engineering+entrance+exam+coaching&hl=en&gl=in"
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": url},
timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
const target = 'https://www.google.com/search?q=engineering+entrance+exam+coaching&hl=en&gl=in';
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: target });
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(({ writeFile }) => writeFile('shot.webp', Buffer.from(await res.arrayBuffer())));
ScreenshotNeo removes cookie banners, newsletter popups, and chat widgets before the shot. Bot checks, blank pages, and failed loads are never billed; the response includes page-verdict and billing headers. 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. Start with 1,000 free screenshots a month, no card required.
FAQ
Should I use an India-wide capture or a city-specific capture?
Use the scope that matches the audience question. State it in the filename and log. Do not use an India-wide result as a proxy for a local market.
Does Google URL Inspection show a competitor SERP?
No. It can show a screenshot of a page Google retrieved; it is not an image of Google’s search results page.
Can Search Console tell me a competitor’s clicks or CTR?
No. Its performance reports are for the property you can access, not another site’s private data.
Is one screenshot enough to establish a ranking change?
It establishes what was visible in that capture. For rank history, retain structured records and compare repeat captures under matched conditions.


