ScreenshotOne vs ApiFlash: Options, Limits and Price

ScreenshotOne vs ApiFlash compared: parameter mapping, defaults, cache and quota rules, rate limits, prices at 1k, 10k and 100k a month, and a test script.

By Simon Chiu · Updated

Disclosure: I build RenderScreenshot, a third screenshot API, so I compete with both. I have not run paid accounts on either service for this page. Every fact below comes from their documentation and pricing pages as read on 2026-10-14 and 2026-10-15, with links, and the test script at the end is untested against live keys. Where one of them is the better choice, I say so.

Short answer: ApiFlash is the cheaper, simpler API: one endpoint, sensible defaults, caching on by default, and the lowest list price at every volume we checked. ScreenshotOne has the larger feature set: signed links for public embedding, async jobs with webhooks, S3 upload, HTML and Markdown input, PDF output, more blocking and wait controls, and metadata extraction. If you need screenshots of public pages from server code, start with ApiFlash's price. If you embed screenshot URLs in public HTML, render PDFs, or need fine control over waiting and blocking, ScreenshotOne earns its higher price.

This page is one of our screenshot API comparisons. If ApiFlash is on your shortlist next to Urlbox, see Urlbox vs ApiFlash.

The two APIs in one table

ScreenshotOne ApiFlash
Endpoint GET or POST https://api.screenshotone.com/take GET or POST https://api.apiflash.com/v1/urltoimage (POST takes form data)
Auth access_key parameter or X-Access-Key header access_key parameter
Public embedding Signed links: HMAC-SHA256 of the query string, GET only; can be required for every request No signing; the key is in the URL
Inputs URL, HTML, Markdown URL
Outputs Images (JPEG default, PNG, WebP and more), PDF, HTML, Markdown JPEG (default), PNG, WebP
Response Bytes by default; response_type=json or empty Bytes by default; response_type=json returns file URLs
Async + webhooks async=true with webhook_url No
Upload to your storage store=true to S3-compatible storage S3 export (listed on all plans)
Infrastructure (vendor's description) Not stated in the pages we read Chrome on AWS Lambda

Sources: ScreenshotOne options, ScreenshotOne signed links, ApiFlash documentation.

Defaults that make the first comparison unfair

If you call both with only a URL, you are not comparing like with like.

Setting ScreenshotOne default ApiFlash default
Viewport 1280 wide (viewport_width) 1920x1080
Format jpg jpeg
Wait condition load network_idle
Full-page lazy loading full_page_scroll turns on with full_page=true Separate scroll_page=true
Cache Off until cache=true; then 4 hours by default On; 24 hours by default (ttl=86400)
Timeout 60 s default, 90 s max wait_until_timeout 30 s max, then captures whatever loaded

The cache row is the one that surprises people in testing. With ApiFlash, your second request for the same URL and parameters returns the first image for 24 hours unless you pass fresh=true. With ScreenshotOne, every request renders unless you opt into caching.

The timeout rows differ in kind, too. ApiFlash's wait_until_timeout captures the page as it is when the time runs out. A ScreenshotOne timeout aborts the request. One gives you a possibly incomplete image; the other gives you an error you can retry.

Parameter mapping

For a 1280x800 PNG with ads and cookie banners blocked, waiting for the network to settle:

What you want ScreenshotOne ApiFlash
Target page url url
Viewport viewport_width=1280&viewport_height=800 width=1280&height=800
Pixel density device_scale_factor=2 (1 to 5) scale_factor=2 (1 or 2)
Format format=png format=png
Quality image_quality=80 quality=80
Full page full_page=true full_page=true (plus scroll_page=true for lazy images)
One element selector=.hero element=.hero
Wait for network wait_until=networkidle2 (or networkidle0) wait_until=network_idle
Wait for an element wait_for_selector=.ready wait_for=.ready
Extra delay delay (seconds) delay (seconds, max 10)
Block ads block_ads=true no_ads=true
Block cookie banners block_cookie_banners=true no_cookie_banners=true
Block trackers block_trackers=true no_tracking=true
Hide elements / inject CSS hide_selectors, styles css
Inject JavaScript scripts js
Transparent background omit_background=true (PNG only) transparent=true (PNG only)
Country of the request IP ip_country_code=gb ip_location=gb (custom enterprise plans)
Fail on bad status fail_if_* options fail_on_status=400,404,500-511
Fresh render omit cache fresh=true

Quota, caching and rate limits

What counts against your quota.

  • ScreenshotOne: only successful renders that were not served from cache. Its pricing FAQ says screenshots with visual issues still count, and that it reviews reported defects for refunds.
  • ApiFlash: requests served from cache do not count. Repeated failing calls with identical parameters are limited to 5 per hour.

Rate limits.

  • ScreenshotOne: requests per minute by plan (40, 80 and 150 on Basic, Growth and Scale). Its concurrency docs explain that each accepted request spends one start from a one-minute bucket; when the bucket is empty you get concurrency_limit_reached.
  • ApiFlash: a leaky bucket of 20 requests per second with a burst of 400. Above that, requests are delayed, then rejected with 429 once the burst is full.

For a batch job, that difference is large. ApiFlash will accept a burst of hundreds of URLs and pace them for you. ScreenshotOne's Basic plan starts 40 per minute, so you need your own queue.

Quota visibility. ApiFlash returns X-Quota-Limit, X-Quota-Remaining and X-Quota-Reset headers on successful renders and has a /quota endpoint. ScreenshotOne has a usage endpoint and email alerts at 90% and 100%.

Price at three volumes

List prices as shown on the vendors' pages on 2026-10-14 (ScreenshotOne) and 2026-10-15 (ApiFlash), monthly billing:

Screenshots per month ScreenshotOne ApiFlash
1,000 Basic, $17 (2,000 included) Lite, $7 (1,000)
10,000 Growth, $79 (10,000) Medium, $35 (10,000)
100,000 Scale, $259 for 50,000 + 50,000 extra at $0.004 = $459 Large, $180 (100,000)

Both have a free tier of 100 screenshots a month with no credit card. ScreenshotOne offers two months free on annual billing. For comparison, RenderScreenshot lists $10 for 2,500, $40 for 12,000 and $100 for 60,000 credits (pricing), with cached responses free when you request them.

At 100,000 a month, ApiFlash costs less than half of ScreenshotOne's list price. At that volume, the question is whether the ScreenshotOne features you would use are worth about $280 a month.

Test both on your own URLs

This Node 22 script sends the same request to both APIs with every default overridden, checks the image signature, and prints time, size and quota headers. Run it twice per URL: the first run with fresh renders, the second with caching on, to see each service's cache path. It has not been run against live keys for this page.

// Same capture on ScreenshotOne and ApiFlash with matched settings.
// Usage: node compare.mjs https://example.com [--cached]
// Env: SCREENSHOTONE_ACCESS_KEY, APIFLASH_ACCESS_KEY
import { writeFile } from 'node:fs/promises';

const target = process.argv[2] ?? 'https://example.com';
const cached = process.argv.includes('--cached');

const requests = {
  screenshotone: {
    url: 'https://api.screenshotone.com/take',
    headers: { 'X-Access-Key': process.env.SCREENSHOTONE_ACCESS_KEY },
    params: {
      url: target, format: 'png', viewport_width: '1280', viewport_height: '800',
      device_scale_factor: '1', wait_until: 'networkidle2', block_cookie_banners: 'true',
      ...(cached ? { cache: 'true', cache_ttl: '14400' } : {}),
    },
  },
  apiflash: {
    url: 'https://api.apiflash.com/v1/urltoimage',
    headers: {},
    params: {
      access_key: process.env.APIFLASH_ACCESS_KEY, // ApiFlash only accepts the key as a parameter
      url: target, format: 'png', width: '1280', height: '800',
      scale_factor: '1', wait_until: 'network_idle', no_cookie_banners: 'true',
      ...(cached ? { ttl: '14400' } : { fresh: 'true' }),
    },
  },
};

const PNG = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);

for (const [name, req] of Object.entries(requests)) {
  const u = new URL(req.url);
  u.search = new URLSearchParams(req.params).toString();
  const t0 = performance.now();
  try {
    const res = await fetch(u, { headers: req.headers, signal: AbortSignal.timeout(120_000) });
    const body = Buffer.from(await res.arrayBuffer());
    const ms = (performance.now() - t0).toFixed(0);
    if (!res.ok) throw new Error(`HTTP ${res.status}: ${body.toString('utf8', 0, 300)}`);
    if (!body.subarray(0, 8).equals(PNG)) throw new Error(`not a PNG (${res.headers.get('content-type')})`);
    await writeFile(`${name}${cached ? '-cached' : ''}.png`, body);
    const quota = res.headers.get('x-quota-remaining');
    console.log(`${name}: ${ms} ms, ${body.length} bytes${quota ? `, quota left ${quota}` : ''}`);
  } catch (err) {
    console.error(`${name}: ${err.message}`);
    process.exitCode = 1;
  }
}

Do not log the full ApiFlash URL: it contains your key. For each URL, run the script without --cached at least five times for render latency, then with --cached twice and read the second result for cache latency. Compare the images at full size, not just the numbers.

Where each one is the better choice

ApiFlash fits when:

  • You capture public pages from your own servers and never expose request URLs.
  • Cost per screenshot is the main criterion.
  • You send bursts and want the API to pace them (20 per second, burst of 400).
  • You want caching on by default with a long TTL.

ScreenshotOne fits when:

  • You put screenshot URLs into public HTML and need signed links.
  • You render HTML or Markdown, or produce PDFs, in the same integration.
  • You want async jobs with webhooks and upload to your own S3 bucket.
  • You need more control: wait for multiple events, detailed blocking, metadata extraction, failure conditions.

A third option

If you want signed URLs for <img> tags and a cache you can serve publicly, RenderScreenshot returns an X-Cache-URL CDN link with every response and supports signed URLs with a required expiry of up to 30 days:

curl -o example.png -D headers.txt \
  -H "Authorization: Bearer $RS_API_KEY" \
  "https://api.renderscreenshot.com/v1/screenshot?url=https%3A%2F%2Fexample.com&width=1280&height=800&format=png&block_cookies=true"
grep -i x-cache-url headers.txt

Add it as a third entry in the script above to compare all three on the same URLs. Get a free API key to run it.

Last verified: 2026-10-15 (docs and pricing pages). Measured results pending.

Frequently asked questions

Is ApiFlash cheaper than ScreenshotOne?
Yes, at list price. In October 2026 ApiFlash listed $7 for 1,000, $35 for 10,000 and $180 for 100,000 screenshots a month. ScreenshotOne listed $17 for 2,000, $79 for 10,000 and $259 for 50,000, with $0.004 per extra screenshot on that plan.
Why do ScreenshotOne and ApiFlash give different images for the same URL?
Different defaults. ApiFlash uses a 1920x1080 viewport and waits for network idle; ScreenshotOne uses a 1280-pixel-wide viewport and waits for the load event. Both default to JPEG. Set viewport, format and wait condition explicitly before you compare.
Can I embed ScreenshotOne or ApiFlash URLs in img tags?
ScreenshotOne supports signed links for GET requests, so the URL cannot be reused with other parameters. ApiFlash puts the access key in the query string, so a public URL exposes the key.
Do cached screenshots count against the quota?
On both, a request served from cache does not count. ApiFlash caches by default for 24 hours; ScreenshotOne caches only when you set cache=true, with a 4-hour default TTL.

Related in this topic

Try RenderScreenshot free

150 free credits every month, no credit card required.