Free Tier: 100 vs. 200
ScreenshotOne gives you 100 free screenshots per month. SnapRender gives you 200. Neither asks for a card.
That difference matters most during evaluation and prototyping. With 100 screenshots, you burn through your free tier during a single development sprint. You're paying before you've decided whether the API meets your needs.
With 200, you can:
- Build a complete proof of concept
- Test across different URL types (SPAs, static sites, pages with auth)
- Run through your edge cases
- Demo to stakeholders
- Still have headroom for iteration
For teams evaluating multiple screenshot APIs simultaneously, SnapRender's free tier is large enough to run a proper bake-off without paying for any provider.
Feature Gating: The Real Difference
This matters more than the price difference for a lot of teams. ScreenshotOne gates certain features behind higher-tier plans. Geolocation targeting and video capture are locked until you upgrade.
SnapRender takes a different approach: every feature is available on every plan, including free. Tiers are based purely on volume.
Here's what that means in practice:
| Feature |
ScreenshotOne |
SnapRender |
| Basic screenshots (PNG, JPEG, WebP) |
All plans |
All plans |
| PDF capture |
All plans |
All plans |
| Full-page screenshots |
All plans |
All plans (up to 32,768px) |
| Custom viewports |
All plans |
All plans |
| Device emulation |
All plans |
All plans |
| Dark mode |
Yes |
All plans |
| Ad blocking |
Yes |
All plans |
| Cookie banner removal |
Yes |
All plans |
| Custom CSS/JS injection |
All plans |
Not available |
| Hide selectors |
All plans |
All plans |
| Click selector |
Not listed |
All plans |
| Content extraction |
Markdown |
All plans (6 formats) |
| Batch screenshots |
Bulk endpoint |
All plans (50 URLs per request) |
| Webhooks |
Yes |
All plans |
| Signed URLs |
Yes |
All plans |
| HTML/Markdown rendering |
Yes |
All plans |
| Scheduled captures with change detection |
Not available |
All plans |
| Hosted MCP server |
Yes (OAuth) |
All plans (OAuth or API key) |
| Caching with configurable TTL |
All plans |
All plans |
| Geolocation targeting |
Higher tiers only |
Not available |
| Video capture |
Higher tiers only |
Not available |
The feature gating problem is insidious because you discover it mid-project. You build your screenshot pipeline on an entry plan, everything works, and then you need a capability that lives two tiers up. With SnapRender, every rendering feature is available on every plan including the free tier, so scaling up is purely a volume decision.
Response Times
Both APIs return screenshots in the same general range for fresh captures. For cached responses, SnapRender is significantly faster:
| Scenario |
ScreenshotOne |
SnapRender |
| Fresh screenshot |
2-8 seconds |
2-5 seconds |
| Cached screenshot |
Varies |
Under 200ms |
SnapRender's cache returns screenshots in under 200ms with configurable TTL. If you're serving screenshots in a user-facing context (link previews, social cards, directory thumbnails), that cache performance matters. The difference between 200ms and multi-second responses is the difference between a snappy UI and a loading spinner.
What ScreenshotOne Does Better
This wouldn't be an honest comparison without covering where ScreenshotOne wins. Geolocation and video are in the table above; the bigger difference is breadth.
Parameter and SDK breadth: ScreenshotOne exposes 200+ API parameters and ships seven SDKs (Node.js, Python, Ruby, PHP, Go, Java, C#). SnapRender covers the parameters developers actually use (around 30) and ships three SDKs (Node.js, Python, Go) plus an MCP server for AI agents. If you depend on an exotic rendering option, or you're building in Ruby, PHP, Java, or C# and insist on a first-party client library, ScreenshotOne earns its premium.
That said, screenshot APIs are simple REST services. The SDK saves you 10-15 lines of code, and going SDK-less with a direct HTTP call is trivial in any language (there's a complete Ruby example below).
For everything else, the features are equivalent and the price gap is hard to ignore.
AI-Native Features
Both providers now host an MCP (Model Context Protocol) server, so Claude, Cursor, and other MCP clients can take screenshots without glue code. SnapRender's server at https://app.snap-render.com/mcp accepts an OAuth sign-in or an API key and exposes 11 tools, including batch capture, content extraction, signed URLs, and webhook management. See SnapRender for AI agents.
Frameworks that do not speak MCP still need a tool definition. With ScreenshotOne, that is a custom function:
# Custom tool definition for your AI agent (ScreenshotOne)
def screenshot_tool(url: str) -> bytes:
"""Takes a screenshot of the given URL."""
response = requests.get(
"https://api.screenshotone.com/take",
params={
"access_key": os.environ["S1_ACCESS_KEY"],
"secret_key": os.environ["S1_SECRET_KEY"],
"url": url,
"format": "png"
}
)
return response.content
SnapRender publishes ready-made LangChain, CrewAI, and AutoGen tools for those frameworks, so the agent gets screenshots as a built-in capability. That's less plumbing to maintain.
Migration Code Examples
If you're switching from ScreenshotOne to SnapRender, here's exactly what changes in your code.
Node.js: Direct API Call
// BEFORE: ScreenshotOne
const screenshotOneUrl = new URL('https://api.screenshotone.com/take');
screenshotOneUrl.searchParams.set('access_key', process.env.S1_ACCESS_KEY);
screenshotOneUrl.searchParams.set('secret_key', process.env.S1_SECRET_KEY);
screenshotOneUrl.searchParams.set('url', 'https://example.com');
screenshotOneUrl.searchParams.set('format', 'png');
screenshotOneUrl.searchParams.set('full_page', 'true');
screenshotOneUrl.searchParams.set('viewport_width', '1280');
screenshotOneUrl.searchParams.set('block_ads', 'true');
const response = await fetch(screenshotOneUrl);
const imageBuffer = await response.arrayBuffer();
// AFTER: SnapRender
const snapRenderUrl = new URL('https://app.snap-render.com/v1/screenshot');
snapRenderUrl.searchParams.set('url', 'https://example.com');
snapRenderUrl.searchParams.set('format', 'png');
snapRenderUrl.searchParams.set('full_page', 'true');
snapRenderUrl.searchParams.set('width', '1280');
snapRenderUrl.searchParams.set('block_ads', 'true');
const response = await fetch(snapRenderUrl, {
headers: { 'X-API-Key': process.env.SNAPRENDER_API_KEY }
});
const imageBuffer = await response.arrayBuffer();
Three things changed: the base URL, the auth mechanism (query params to header), and viewport_width became width. Everything else is the same.
Node.js: SDK Migration
// BEFORE: ScreenshotOne SDK
import { Client, TakeOptions } from 'screenshotone-api-sdk';
const client = new Client(accessKey, secretKey);
const options = TakeOptions
.url('https://example.com')
.format('png')
.fullPage(true)
.viewportWidth(1280)
.blockAds(true);
const imageBlob = await client.take(options);
// AFTER: SnapRender SDK
import { SnapRender } from 'snaprender';
const client = new SnapRender({ apiKey: process.env.SNAPRENDER_API_KEY });
const imageBuffer = await client.capture({
url: 'https://example.com',
format: 'png',
fullPage: true,
width: 1280,
blockAds: true
});
Python: Direct API Call
# BEFORE: ScreenshotOne
import requests
response = requests.get(
"https://api.screenshotone.com/take",
params={
"access_key": os.environ["S1_ACCESS_KEY"],
"secret_key": os.environ["S1_SECRET_KEY"],
"url": "https://example.com",
"format": "png",
"full_page": "true",
"viewport_width": "1280",
"block_ads": "true",
}
)
image_bytes = response.content
# AFTER: SnapRender
import requests
response = requests.get(
"https://app.snap-render.com/v1/screenshot",
params={
"url": "https://example.com",
"format": "png",
"full_page": "true",
"width": "1280",
"block_ads": "true",
},
headers={"X-API-Key": os.environ["SNAPRENDER_API_KEY"]}
)
image_bytes = response.content
Python: SDK Migration
# BEFORE: ScreenshotOne SDK
from screenshotone import Client, TakeOptions
client = Client(access_key, secret_key)
options = (TakeOptions.url("https://example.com")
.format("png")
.full_page(True)
.viewport_width(1280)
.block_ads(True))
image = client.take(options)
# AFTER: SnapRender SDK
from snaprender import SnapRender
client = SnapRender(api_key=os.environ["SNAPRENDER_API_KEY"])
image = client.capture("https://example.com",
format="png",
full_page=True,
width=1280,
block_ads=True
)
cURL
# BEFORE: ScreenshotOne
curl "https://api.screenshotone.com/take?\
access_key=YOUR_KEY&\
secret_key=YOUR_SECRET&\
url=https://example.com&\
format=png&\
full_page=true&\
viewport_width=1280" \
-o screenshot.png
# AFTER: SnapRender
curl -H "X-API-Key: YOUR_KEY" \
"https://app.snap-render.com/v1/screenshot?\
url=https://example.com&\
format=png&\
full_page=true&\
width=1280" \
-o screenshot.png
Parameter Reference: ScreenshotOne to SnapRender
Full mapping for common parameters:
| ScreenshotOne Parameter |
SnapRender Parameter |
Notes |
access_key |
Header: X-API-Key |
Auth moves from URL to header |
secret_key |
(not needed) |
Single key auth |
url |
url |
Same |
format |
format |
Same (png, jpeg, webp, pdf) |
viewport_width |
width |
Renamed |
viewport_height |
height |
Renamed |
full_page |
full_page |
Same |
image_quality |
quality |
Renamed, same range (1-100) |
device_scale_factor |
device |
No separate scale parameter; a device preset sets the pixel ratio |
block_ads |
block_ads |
Same |
block_cookie_banners |
block_cookie_banners |
Same |
dark_mode |
dark_mode |
Same |
delay |
delay |
Check units (ms vs s) |
selector |
(none) |
Element-only capture is not offered; crop the image or hide surrounding elements |
click |
click_selector |
Renamed |
hide_selectors |
hide_selectors |
Same |
cache |
cache |
Boolean to enable caching |
cache_ttl |
cache_ttl |
TTL in seconds |
Cost Comparison at Real-World Volumes
Abstract pricing tiers don't always match how teams actually use screenshot APIs. Here's what the cost difference looks like for common use cases. ScreenshotOne bills extra screenshots past a plan's quota ($0.009 on Basic, $0.006 on Growth, $0.004 on Scale) when you enable extra usage, so the figures below use whichever is cheaper: the smaller plan plus extras, or the next plan. SnapRender stops at the quota, and sells one-off credit packs (5,000 for $25, valid for a year) for small overruns.
Link Preview Service (SaaS)
A SaaS product generating link previews for user-submitted URLs. Typical volume: 5,000-15,000 screenshots/month.
| Volume |
ScreenshotOne (plan needed) |
SnapRender (plan needed) |
Monthly savings |
| 5,000/mo |
$44 (Basic + 3,000 extra) |
$29 (Growth, 10K plan) |
$15 |
| 8,000/mo |
$71 (Basic + 6,000 extra) |
$29 (Growth, 10K plan) |
$42 |
| 12,000/mo |
$91 (Growth + 2,000 extra) |
about $39 (Growth + a credit pack every 2.5 months)* |
$52 |
*Or the Business plan at $79 for 50,000, if the volume keeps growing.
E-commerce Directory (Marketplace)
A marketplace that captures seller website screenshots for listings. Heavy initial capture, then maintenance. Typical: 20,000-40,000 screenshots/month.
| Volume |
ScreenshotOne |
SnapRender |
Annual savings |
| 20,000/mo |
$139/mo (Growth + 10,000 extra) |
$79/mo |
$720/year |
| 40,000/mo |
$259/mo |
$79/mo |
$2,160/year |
Monitoring Dashboard
A tool that captures daily screenshots of tracked websites for change detection. Typical: 1,000-3,000 screenshots/month.
| Volume |
ScreenshotOne |
SnapRender |
Annual savings |
| 1,500/mo |
$17/mo |
$9/mo |
$96/year |
| 3,000/mo |
$26/mo (Basic + 1,000 extra) |
about $14/mo (Starter + a credit pack every 5 months) |
$144/year |
For this use case there is a second difference beyond price. SnapRender runs scheduled captures itself: you register a URL with an hourly, daily, or weekly interval, and every capture is stored with its SHA-256 hash, compared with the previous one, and announced through capture.completed and change.detected webhooks. The Growth plan holds 25 schedules and Business holds 100, so a monitoring dashboard can drop its own cron and diff code. ScreenshotOne has no scheduler, so the loop stays in your code.
What You're Giving Up (And Why It Probably Doesn't Matter)
Switching from ScreenshotOne to SnapRender means:
Fewer SDKs. You go from seven to three (Node.js, Python, and Go). If you're building in Ruby, PHP, Java, or C#, you lose the first-party SDK. But the API is a single GET request. Here's the entire integration in Ruby without an SDK:
require 'net/http'
require 'uri'
def capture_screenshot(target_url)
uri = URI("https://app.snap-render.com/v1/screenshot")
uri.query = URI.encode_www_form(
url: target_url,
format: "png"
)
req = Net::HTTP::Get.new(uri)
req["X-API-Key"] = ENV["SNAPRENDER_API_KEY"]
Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http|
http.request(req).body
}
end
A few lines. No SDK needed. SnapRender has been in production since early 2026 and is used by 500+ developers, so what you're trading is SDK count, not reliability.
For most teams, that difference doesn't justify paying 2-3x more per month. For the full pricing breakdown, see our Screenshot API Pricing Compared.
Making the Switch
If you've read this far, you're probably ready to evaluate SnapRender as a screenshotone alternative. Here's the fast path:
- Sign up for a free key. The free tier gives you 200 screenshots. No credit card required.
- Grab your API key from the dashboard.
- Test with cURL to verify the API works for your URLs:
curl -H "X-API-Key: YOUR_KEY" \
"https://app.snap-render.com/v1/screenshot?url=https://your-site.com&format=png" \
-o test.png
- Compare output quality with your existing ScreenshotOne screenshots side-by-side.
- Run both providers in parallel for a week if you want extra confidence.
- Flip the switch when you're satisfied.
The migration is a few hours of work. The savings are roughly $100 to $2,160 per year depending on your volume. If you're new to screenshot APIs in general, our Screenshot API Complete Guide covers the fundamentals.