Urlbox vs ApiFlash: Defaults, Pricing and a Test Script

Urlbox vs ApiFlash for website screenshots: default settings that change your output, prices at 1k, 10k and 100k a month, and a script to test both.

By Simon Chiu · Updated

Disclosure: I build RenderScreenshot, which competes with both services on this page. I have no affiliation with Urlbox or ApiFlash. Every fact about them below comes from their own documentation and pricing pages, checked on October 15, 2026, and the links are next to the claims.

Urlbox and ApiFlash both turn a URL into an image with one HTTP call, and both run Chrome. The decision is not about engine quality. It comes down to three things: what you pay at your volume, whether you need output other than images, and whether your screenshot URLs ever appear in public HTML. ApiFlash wins on price and simplicity. Urlbox wins on output formats, render controls and safe public links.

This page is part of our comparison hub. If you are leaving one of them, the migration guides for Urlbox and ApiFlash map every parameter to ours.

Last verified: October 15, 2026, against Urlbox render options, Urlbox pricing, the ApiFlash documentation and the pricing table on apiflash.com.

The short answer

Choose ApiFlash if Choose Urlbox if
You want the lowest list price at 1,000, 10,000 or 100,000 shots a month You need PDF, HTML, Markdown, SVG or video output from the same API
Every call is server-side, so a key in the query string is acceptable You embed screenshot URLs in public pages and need signed render links
A free tier of 100 screenshots a month is enough to evaluate You capture third-party sites that block simple headless browsers (Urlbox sells this as its Ultra tier)
You want HTML or text extraction in the same call (extract_html, extract_text) You need a long request timeout (Urlbox lists up to 300 s on Ultra and Business)
You upload results to S3 with per-request credentials You want click_accept (click the consent button) as well as hiding cookie banners

If none of the right-hand column applies to you, ApiFlash costs less and you will not miss anything.

Defaults that change your output

Most "Urlbox looks different from ApiFlash" reports are not rendering differences. They are default settings. If you send only a URL to each service, you compare different screenshots:

Setting ApiFlash default Urlbox default What to send to both
Viewport width 1920 1280 width=1280
Viewport height 1080 1024 height=800
Output format jpeg png format=png
Wait condition network_idle loaded (load event) ApiFlash wait_until=page_loaded, Urlbox wait_until=loaded
Cache 24 h (ttl=86400), max 30 days 30 days (ttl=2592000), also the max ApiFlash fresh=true, Urlbox force=true
Response body image bytes JSON with a renderUrl (sync API) read the bytes, or download renderUrl

The wait condition matters most. ApiFlash's network_idle waits for in-flight requests to finish, so on a page with analytics beacons or a chat widget it takes longer and captures more lazy content. Urlbox's loaded returns sooner and can miss images that load after the load event. Neither is wrong. Pick one condition and set it on both.

The cache matters for benchmarks. Both services return a cached image for an identical request, and a cached response is much faster than a render. ApiFlash says cached responses do not count against your quota. If you test without fresh=true / force=true, your second run measures their CDN, not their browsers.

Pricing at three volumes

List prices, monthly billing, checked on October 15, 2026. Taxes excluded.

Monthly volume ApiFlash Urlbox Notes
1,000 $7 (Lite, 1,000) $19 (Lo-Fi, 2,000) or $49 (Hi-Fi, 5,000) Urlbox Lo-Fi captures third-party sites as thumbnails only
10,000 $35 (Medium, 10,000) $99 (Ultra, 15,000) Hi-Fi stops at 5,000 renders
100,000 $180 (Large, 100,000) Business: $495 base, then $3 per 1,000 renders Read Urlbox's Business terms for how many renders the base includes
Free option 100 screenshots/month, no card 7-day trial, no card

Sources: the pricing table on apiflash.com and urlbox.com/pricing. Urlbox also lists an Enterprise plan from $3,000 a month, and both services offer custom plans.

Two things the table does not show. First, Urlbox's tiers gate capability, not only volume: third-party sites, "stealth" request privacy, request timeout (30 s, 90 s, 300 s) and file-size limits (2.5 MB, 10 MB, none) all change by plan. A full-page capture of a long marketing page can exceed 2.5 MB as PNG, so check the limit for your plan. Second, ApiFlash's lower price assumes you are fine with its feature set: no PDF, no video, and a 10 s maximum for delay.

Parameter mapping

Parameters that exist on both sides, with their names. Use it to port a request or to build the comparison below.

Purpose ApiFlash Urlbox
Target url (must be URL-encoded) url or html
Viewport width, height width, height
Full page full_page=true full_page=true
Scroll first (lazy images) scroll_page=true full-page captures scroll and stitch by default (full_page_mode=stitch)
One element element (+ element_overlap) selector (+ fail_if_selector_missing)
Format format = jpeg, png, webp format = png, jpeg, webp, avif, svg, pdf, html, mp4, webm, md
Quality quality (0 to 100) quality (JPEG/WebP)
Retina scale_factor=2 retina=true
Thumbnail thumbnail_width thumb_width
Wait for selector wait_for (15 s limit) wait_for
Wait condition wait_until = domloaded, pageloaded, network_idle wait_until = domloaded, mostrequestsfinished, requestsfinished, loaded
Fixed delay delay in seconds (max 10) delay in milliseconds
Ads no_ads block_ads
Cookie banners no_cookie_banners hide_cookie_banners or click_accept
Custom CSS / JS css, js css, js
Transparent background transparent (PNG only) transparent (PNG/WebP)
Dark mode not documented dark_mode
Cache ttl, fresh ttl, force, unique
JSON instead of bytes response_type=json default on the sync API; response_type for render links

Watch the delay units. delay=3 is three seconds on ApiFlash and three milliseconds on Urlbox.

Auth and response shape

ApiFlash is a single endpoint, GET or POST https://api.apiflash.com/v1/urltoimage. The access_key travels in the query string or the form body. The response is the image, with X-Quota-Limit, X-Quota-Remaining and X-Quota-Reset headers. Errors use plain HTTP codes: 402 when the monthly quota is used up, 403 when your plan lacks a feature you asked for, 429 above 20 requests per second (burst of 400).

Urlbox has a synchronous API (POST https://api.urlbox.com/v1/render/sync with Authorization: Bearer <secret>), an async API with webhooks, and render links. The sync API answers with JSON: a renderUrl on Urlbox's storage plus size and dimensions. Your code needs a second request to fetch the bytes, or it can store the link.

The practical difference: a URL that carries an ApiFlash key must stay on your server. If you put it in an <img src> or an og:image tag, anyone can copy the key and spend your quota. Urlbox render links are signed with your secret, so the secret never appears in the URL. If your screenshots end up in public HTML, that alone can decide the comparison.

The head-to-head test

We have not published measured numbers for this pair yet. Both services have paid tiers that change behaviour, so a fair run needs a paid account on each, the same corpus and the same region. Until we have that, here is the method we use for every pair on this site and the script that runs it. Run it with your own keys on your own URLs. That result is worth more to you than ours.

Method

  • Corpus: 10 to 20 of your own URLs: a static page, a page with web fonts, a long page with lazy images, a page with a cookie banner, a single-page app.
  • Settings sent to both: 1280x800, PNG, the cache bypassed (fresh=true, force=true), no delay.
  • Runs: at least 3 per URL per provider, from one machine, sequentially, so neither service competes with the other for your bandwidth.
  • Report: p50 and p95 latency, success rate (a 200 response that is a valid PNG), and output bytes. Then open the renders side by side.
  • Count Urlbox's download: the script times the JSON call and the renderUrl fetch together, because your code waits for both.

The script (Node 22 or later, no dependencies):

// compare.mjs: render the same URLs with ApiFlash and Urlbox, then print latency and bytes.
// Node 22+. Usage: APIFLASH_KEY=... URLBOX_SECRET=... node compare.mjs urls.txt
import { readFile, writeFile, mkdir } from 'node:fs/promises';

const RUNS = Number(process.env.RUNS ?? 3);
const APIFLASH = process.env.APIFLASH_BASE ?? 'https://api.apiflash.com';
const URLBOX = process.env.URLBOX_BASE ?? 'https://api.urlbox.com';
const VIEWPORT = { width: 1280, height: 800 };

const providers = {
  // ApiFlash: GET, key in the query string, image bytes in the body.
  async apiflash(target) {
    const qs = new URLSearchParams({
      access_key: process.env.APIFLASH_KEY,
      url: target,
      width: String(VIEWPORT.width),
      height: String(VIEWPORT.height),
      format: 'png',
      fresh: 'true', // skip ApiFlash's cache so every run is a real render
    });
    const res = await fetch(`${APIFLASH}/v1/urltoimage?${qs}`, { signal: AbortSignal.timeout(60_000) });
    if (!res.ok) throw new Error(`apiflash ${res.status}: ${await res.text()}`);
    return Buffer.from(await res.arrayBuffer());
  },
  // Urlbox: POST /v1/render/sync with the secret as a Bearer token.
  // The default response is JSON with a renderUrl, so the image is a second request.
  async urlbox(target) {
    const res = await fetch(`${URLBOX}/v1/render/sync`, {
      method: 'POST',
      headers: { Authorization: `Bearer ${process.env.URLBOX_SECRET}`, 'Content-Type': 'application/json' },
      body: JSON.stringify({ url: target, width: VIEWPORT.width, height: VIEWPORT.height, format: 'png', force: true }),
      signal: AbortSignal.timeout(60_000),
    });
    if (!res.ok) throw new Error(`urlbox ${res.status}: ${await res.text()}`);
    const { renderUrl } = await res.json();
    const img = await fetch(renderUrl, { signal: AbortSignal.timeout(60_000) });
    if (!img.ok) throw new Error(`urlbox download ${img.status}`);
    return Buffer.from(await img.arrayBuffer());
  },
};

const isPng = (b) => b.length > 8 && b.readUInt32BE(0) === 0x89504e47;
const pct = (xs, p) => [...xs].sort((a, b) => a - b)[Math.max(0, Math.ceil((p / 100) * xs.length) - 1)];

const urls = (await readFile(process.argv[2] ?? 'urls.txt', 'utf8')).split('\n').map((s) => s.trim()).filter(Boolean);
await mkdir('renders', { recursive: true });
const rows = ['provider,url,runs,ok,p50_ms,p95_ms,bytes'];

for (const target of urls) {
  for (const [name, call] of Object.entries(providers)) {
    const times = [];
    let bytes = 0;
    for (let i = 0; i < RUNS; i++) {
      const t0 = performance.now();
      try {
        const buf = await call(target);
        if (!isPng(buf)) throw new Error('response is not a PNG');
        times.push(performance.now() - t0);
        bytes = buf.length;
        if (i === 0) await writeFile(`renders/${name}-${encodeURIComponent(target)}.png`, buf);
      } catch (err) {
        console.error(`${name} ${target}: ${err.message}`);
        process.exitCode = 1; // keep going, but do not report success
      }
    }
    rows.push([name, target, RUNS, times.length, Math.round(pct(times, 50) ?? 0), Math.round(pct(times, 95) ?? 0), bytes].join(','));
  }
}
await writeFile('results.csv', rows.join('\n') + '\n');
console.log(rows.join('\n'));

Tested on October 15, 2026 with Node 22.18.0 against a local mock of both endpoints (request shape, auth, PNG check, CSV output). It was not run against the live services, because that needs paid keys for both.

Three details in the script are deliberate. It checks the PNG magic bytes, because an error page delivered with HTTP 200 is a failure you would otherwise count as a success. It sets a non-zero exit code on any failure, so a CI job cannot pass by accident. And it saves the first render of each URL, because latency without the picture tells you nothing about quality.

When you read the results, look at the renders before the numbers. A provider that is 300 ms faster because it captured the page before the hero image loaded has not won.

Where each one is the better choice

ApiFlash is the better choice when you run a server-side job (thumbnails for a directory, previews in an admin panel, periodic captures of pages you own), your volume is predictable, and cost is the main constraint. Its parameter set covers the common needs: full page, element, scroll for lazy images, ad and cookie-banner blocking, custom CSS and JS, geolocation and time zone. The one-endpoint design is easy to call from any language without an SDK.

Urlbox is the better choice when the screenshot is part of a product feature that needs more than an image: PDF exports, Markdown or HTML snapshots for an AI pipeline, video of a scroll, or captures of sites that block ordinary headless traffic. Its option list is one of the longest we have read, and the signed render links make public embedding safe.

Neither is the better choice when you need a public, expiring link with a cache you can purge, or you do not want to pay for a capability tier to get it. That is the gap RenderScreenshot fills: signed URLs that expire after at most 30 days, a public X-Cache-URL CDN link on every response, presets such as og_card (1200x630), cache purge by key or URL pattern, and an SDK in six languages. Our full field comparison is in the best screenshot API comparison.

Run the comparison with your own URLs

Put 10 URLs you care about in urls.txt, run the script above with keys for both services, and open the renders folder. If you want a third column, create a free RenderScreenshot key and add one more provider function that calls POST https://api.renderscreenshot.com/v1/screenshot with your key as a Bearer token.

Frequently asked questions

Is ApiFlash cheaper than Urlbox?
Yes, by list price at every volume we checked on October 15, 2026. ApiFlash lists $7 for 1,000, $35 for 10,000 and $180 for 100,000 screenshots a month. Urlbox's lowest plans that cover those volumes are Lo-Fi ($19, 2,000 renders, third-party sites as thumbnails only) or Hi-Fi ($49, 5,000), Ultra ($99, 15,000) and Business ($495 base plus $3 per 1,000 renders).
Why do the same parameters give different images on Urlbox and ApiFlash?
Their defaults differ. ApiFlash uses a 1920x1080 viewport, JPEG output and waits for network idle. Urlbox uses a 1280x1024 viewport, PNG output and waits for the load event. Set width, height, format and the wait condition explicitly on both before you compare.
Can I put an Urlbox or ApiFlash URL in an img tag?
ApiFlash authenticates with an access key in the query string, so a public URL exposes the key. Urlbox has render links that are signed with your secret, which is the safer pattern for public HTML.
Which one renders PDFs or video?
Urlbox lists PDF, HTML, Markdown, SVG, MP4 and WebM output in addition to images. ApiFlash outputs JPEG, PNG and WebP images.

Related in this topic

Try RenderScreenshot free

150 free credits every month, no credit card required.