Free tools Windows power users keep installed
One-click scans. No signup required.
Screenshot results usually appear out of order because captures run asynchronously. When several requests are in flight, a later request can finish before an earlier one. If your program appends results as responses arrive, it displays completion order rather than the order in which work was submitted. That is normally an application-ordering problem, not a damaged screenshot. The fix is to process captures sequentially or attach a stable index, request ID, or job ID to every result and reconstruct the intended order before rendering or storing it.
What “out of order” means
There are three different sequences in a screenshot workflow:
- Input order: the order URLs or capture jobs enter your queue.
- Start order: the order your program begins network or browser work.
- Completion order: the order providers finish and your callbacks receive responses.
With concurrent asynchronous work, only the first two are under your direct control. Network latency, DNS, redirects, JavaScript execution, lazy images, consent dialogs, bot checks and server load can make each page take a different amount of time. A request submitted second may therefore complete first. An array filled by results.push(response) records completion order unless you deliberately associate each response with its source.
This behavior is general asynchronous execution, not a promise that every screenshot provider reorders responses. Some APIs return a deliberately ordered batch, while others deliver independent jobs or webhooks. Check the provider’s contract before assuming that response-array position has meaning.
Recommended Free Tools
#1 Best Overall
The simplest fix: run captures sequentially
Playwright’s page.screenshot() API is asynchronous and returns a JavaScript Promise. Awaiting one capture before starting the next preserves sequential control flow. This is easiest to debug when order matters more than throughput.
import { chromium } from 'playwright';
const urls = [
'https://example.com/first',
'https://example.com/second',
'https://example.com/third'
];
const browser = await chromium.launch();
const page = await browser.newPage();
try {
for (let index = 0; index < urls.length; index++) {
const url = urls[index];
await page.goto(url, { waitUntil: 'networkidle' });
await page.screenshot({ path: `shot-${index}.png`, fullPage: true });
console.log({ index, url, completed: true });
}
} finally {
await browser.close();
}
The loop cannot start the next navigation until the previous screenshot finishes. The trade-off is throughput: a slow page delays every later page. Sequential execution is appropriate for small batches, deterministic reports, or workflows where downstream systems require strict order.
Keep concurrency, preserve order explicitly
For faster batches, start captures concurrently but carry the input index with each Promise. Sort completed pairs before writing files, displaying cards, or returning an API response.
import { chromium } from 'playwright';
const urls = [
'https://example.com/first',
'https://example.com/second',
'https://example.com/third'
];
const browser = await chromium.launch();
try {
const jobs = urls.map((url, index) => (async () => {
const page = await browser.newPage();
try {
await page.goto(url, { waitUntil: 'networkidle' });
const buffer = await page.screenshot({ fullPage: true });
return { index, url, buffer };
} finally {
await page.close();
}
})());
const completed = await Promise.all(jobs);
completed.sort((a, b) => a.index - b.index);
for (const item of completed) {
await Bun.write(`shot-${item.index}.png`, item.buffer);
console.log(item.index, item.url);
}
} finally {
await browser.close();
}
Promise.all() returns values in the order of the input Promise array, even though the underlying work completes at different times. The explicit index remains useful when jobs are retried, streamed, persisted, or handled by separate workers. In a distributed system, use a provider-issued job or request ID when available; store it alongside your own index.
Do not use arrival position as identity
This pattern is unsafe:
onResponse(response => results.push(response));
Instead, create a record before dispatch:
const job = { index, id: crypto.randomUUID(), url };
Log the same id at dispatch, response arrival, persistence and rendering. You can then sort by index for a user-facing list while using id for tracing and deduplication.
Rank #2
- Used Book in Good Condition
Diagnose the ordering bug methodically
- Log dispatch: record a monotonically increasing index, URL, timestamp and unique job ID before sending each request.
- Log arrival: record that same ID when the HTTP response, SDK callback or webhook arrives.
- Log storage and rendering: verify whether your database, queue, UI state or file naming logic appends by arrival time.
- Compare sequences: if dispatch is 0, 1, 2 but arrival is 1, 2, 0, the provider completed jobs normally; your consumer needs reordering.
- Check retries and callbacks: duplicate webhook delivery or a retry can make an item appear twice. Deduplicate by stable job ID.
- Check caching separately: a cache hit may complete almost immediately, but that is still a completion-time difference, not proof of corruption.
Do not assume an API’s batch array or webhook order is meaningful unless its documentation explicitly guarantees it. Preserve your own correlation data even when current tests happen to return ordered results.
Ordering is different from screenshot stability
An image can be returned in the expected position and still differ visually between runs. Playwright’s screenshot assertions wait for consecutive captures to match before comparing the final image; that behavior addresses visual stability, not network response order. Likewise, visual comparisons can vary with operating system, browser version, settings, hardware, power source and headless mode. Use the same environment as your baseline when comparing pixels.
Waiting for a stable image will not reorder API responses. Conversely, sorting results will not fix a page that captured before fonts, animations or lazy images finished loading. Treat these as separate investigations: first establish correct correlation and ordering, then tune page-readiness conditions.
Choosing sequential or concurrent execution
| Approach | Use it when | Advantages | Costs and safeguards |
|---|---|---|---|
Sequential await |
Strict order, small batches, simple scripts | Predictable control flow; straightforward logs | Lower parallelism; one slow page delays later work |
| Concurrent with correlation | Large batches or latency-sensitive jobs | Overlapping work and higher potential throughput | Requires IDs, bounded concurrency, sorting or keyed mapping, retry and deduplication logic |
Unlimited concurrency can exhaust browser memory, file descriptors or provider limits. Use a worker pool or semaphore, for example five to twenty simultaneous pages depending on your environment, and measure rather than assuming a safe value. Keep the original index on every task regardless of the pool size.
Provider-specific questions to ask
The phrase “screenshot API” is too broad for a provider-specific diagnosis. Collect:
Rank #3
- Provider name and API version.
- Language, SDK and runtime version.
- Whether requests are synchronous, queued, streamed or webhook-based.
- Your concurrency code and any retry middleware.
- A sample showing request IDs, dispatch order and response arrival order.
- The provider’s documented guarantee, if any, about batch or webhook ordering.
With that information, you can distinguish expected asynchronous completion from an SDK bug, an incorrect consumer assumption, duplicate delivery or a provider contract violation.
Or skip the browser setup
ScreenshotNeo provides a GET-based screenshot API and MCP server. It can accept consent banners before capture and remove more than 60 known consent platforms, newsletter popups and chat widgets; each cleanup step can be disabled. Only clean shots are billed: bot checks or CAPTCHAs, blank pages, timeouts, failed loads and cache hits cost nothing, and response headers identify the page verdict and billing status.
For an ordered batch, keep your index in your own request records and associate each returned file with that record:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Python:
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)
Node.js:
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`${res.status} ${res.statusText}`);
const body = Buffer.from(await res.arrayBuffer());
See the ScreenshotNeo documentation for parameters. It supports full-page captures with lazy images, CSS-selector elements, dark mode, 12 device presets or custom viewports, retina scale, PDF output, HTML/CSS rendering, custom JavaScript and CSS, clicks, selector waits, delays, network-idle waits, request and resource blocking, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, chosen cache TTLs, signed image links, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, a usage API and an OpenAPI specification. Parameter names used by other screenshot APIs also work, easing migration.
An MCP server exposes take_screenshot, get_page_info and capture_pdf to Claude, Cursor and other MCP clients, so an AI agent can request captures without you maintaining browser orchestration. Plans include 1,000 screenshots per month free with no card, Starter at $5 for 3,000, Growth at $15 for 15,000, Pro at $39 for 60,000, Scale at $99 for 250,000 and Business at $249 for 1,000,000; yearly billing provides two months free and every feature is on every plan.
Create a free ScreenshotNeo account to get 1,000 screenshots a month without a card.
Rank #4
Common failure modes
“My array is reversed or random.”
Likely cause: responses are appended on arrival. Attach and sort by input index; do not infer identity from array position.
“Sequential code is still visually inconsistent.”
Ordering is correct, but page readiness is not. Wait for a selector, network idle or a deliberate delay, and control animations and fonts. Keep the comparison environment consistent.
“A job appears twice.”
Inspect retries and webhook redelivery. Make storage idempotent using the provider job ID or your own unique ID.
“Parallel capture becomes unreliable.”
Reduce concurrency, close pages promptly, set explicit timeouts and record failures separately from successful results. Reordering cannot repair a timed-out or blank capture.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
“The provider’s batch order differs from mine.”
Unless ordering is documented, treat the batch as an unordered collection. Include your index in metadata or maintain a request-to-result map.
Best Value
Frequently Asked Questions
Does an out-of-order result mean the screenshot file is corrupt?
Usually no. It generally means your consumer observed completion order instead of input order; validate the file itself only after correlation is correct.
Should I always use sequential screenshots?
No. Use sequential execution for simplicity and strict control; use bounded concurrency with stable IDs when throughput matters.
Can screenshot comparison tools fix API response ordering?
No. Visual-stability assertions address whether pixels settle, not the order in which independent requests complete.
PC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchWhat information is needed for a provider-specific diagnosis?
The provider and API version, SDK/runtime, concurrency code, documented ordering contract, and logs containing dispatch and arrival IDs.
The Bottom Line
Concurrent screenshot requests finish when each page is ready, not when they were submitted. Preserve a stable index or job ID, then reorder results before presentation; choose sequential await only when its simplicity is worth the throughput cost.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

