What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.
The fastest way to capture a webpage from Ruby is to send an authenticated HTTP request to a screenshot API, check the HTTP status and JSON response, then download the returned image URL. Ruby’s standard library is sufficient; you do not need a browser installed in your application. Use a POST request when you need full-page capture, waiting rules, selectors, CSS, JavaScript, PDF settings, or caching.
Ruby screenshot API quick start
This example uses Ruby’s built-in Net::HTTP and JSON libraries. It keeps the bearer token in SCREENSHOT_API_KEY, requests a PNG, and prints the URL returned by the API.
- Set the key outside your source code. For example, in a Unix shell:
export SCREENSHOT_API_KEY='your_api_key'. Use your platform’s secret manager in production. - Save this as
screenshot.rb.
require "net/http"
require "json"
require "uri"
endpoint = URI("https://api.screenshot-api.org/api/v1/screenshot")
request = Net::HTTP::Post.new(endpoint)
request["Authorization"] = "Bearer #{ENV.fetch("SCREENSHOT_API_KEY")}"
request["Content-Type"] = "application/json"
request.body = {
url: "https://example.com",
viewport: { width: 1280, height: 720 },
format: "png",
fullPage: true,
blockAds: true
}.to_json
response = Net::HTTP.start(endpoint.hostname, endpoint.port, use_ssl: true) do |http|
http.request(request)
end
abort("screenshot failed: #{response.code} #{response.body}") unless response.is_a?(Net::HTTPSuccess)
data = JSON.parse(response.body)
puts data.fetch("screenshotUrl")
Run it with ruby screenshot.rb. A successful response is JSON containing screenshotUrl; open or download that URL to obtain the image. The request uses the documented Screenshot API endpoint. The status check is important: an error response is JSON, not an image, and should never be written blindly to shot.png.
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 & 11Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minuteDownload the returned image in Ruby
If your application needs a local file rather than a URL, make a second request and write the response body only after checking that it is successful. This also lets you stream the file to object storage or an HTTP response.
#1 Best Overall
require "net/http"
require "uri"
image_uri = URI(data.fetch("screenshotUrl"))
image_response = Net::HTTP.get_response(image_uri)
abort("image download failed: #{image_response.code}") unless image_response.is_a?(Net::HTTPSuccess)
File.binwrite("shot.png", image_response.body)
In a real script, place this block immediately after parsing the first response so that data is in scope. For large files, use Net::HTTP#get with a block and write chunks incrementally instead of holding the complete body in memory.
GET or POST: which Ruby request should you use?
| Method | Best for | How options are supplied | Trade-off |
|---|---|---|---|
GET /api/v1/screenshot |
Small, repeatable requests and quick experiments | Query parameters | Long or nested settings become difficult to encode and maintain |
POST /api/v1/screenshot |
Full-page, dynamic, localized, styled, or PDF captures | JSON request body | Slightly more code, but clearer and safer for complex options |
POST /api/v1/screenshot/batch |
Capturing several URLs together | A urls array plus shared options |
Work is asynchronous; you must poll or consume events |
GET also exposes redirect=1 when you want a 302 redirect to the image or PDF URL. Prefer POST for CSS, JavaScript, selectors, geolocation, locale, PDF controls, and cache settings because JSON preserves their structure and avoids URL-length surprises.
Rendering options that matter in Ruby
Viewport, format, and page length
urlis required.formatacceptspng,jpeg,webp, orpdf; PNG is the documented default.viewport.widthandviewport.heightset the browser viewport.fullPage: truecaptures the entire scrollable page rather than only the viewport.deviceScaleFactorincreases pixel density for retina-style output.
Use PNG for sharp UI and text, JPEG for photographic pages where a smaller file matters, WebP when your downstream systems support it, and PDF when the deliverable is a document rather than an image. A full-page capture can be substantially taller than the viewport; impose a maximum or post-process it if your storage or display pipeline has size limits.
Free tools Windows power users keep installed
One-click scans. No signup required.
Waiting for JavaScript and lazy content
waitUntilchooses a navigation-completion condition.waitForSelectorwaits for a particular element before capture.delayMsadds a fixed delay for animations or late network work.
Use a selector wait when the page has a reliable “ready” element; it is usually more deterministic than an arbitrary long delay. Keep a delay as a fallback for charts, transitions, or third-party widgets that appear after the main application has rendered. Waiting too long increases latency and can trigger your client timeout.
Rank #2
Selectors, ads, consent, and appearance
selectorcaptures one CSS-selected element. The documented API does not support this option for PDF output.hideSelectorshides matching elements without changing the source page.blockAdsandblockCookieBannersdefault totruein the reference table; set them explicitly when reproducibility matters.darkModedefaults tofalse.- POST-only
cssandjslet you inject presentation or behavior before the capture.
Hiding a selector is useful for a known newsletter modal; blocking a resource is better when an ad or tracker prevents the layout from stabilizing. Test injected JavaScript carefully: a script that throws or continually modifies the DOM can make a capture fail or produce nondeterministic images.
Locale, geography, and authentication
Use geolocation, timezoneId, and locale to reproduce regional layouts, date formats, and localized copy. If the target requires access, provide the documented request headers or cookies rather than embedding credentials in the URL. Treat captured pages and returned URLs as potentially sensitive; do not log authorization headers, session cookies, or private query strings.
PDF settings
When format is pdf, use the POST-only pdf object for paper size, margins, landscape orientation, and page ranges. Because element selection is not supported for PDF, create a print-specific route or use injected CSS to control what appears on the page.
Recommended Free Tools
Caching and timeouts
cache, cacheTTL, and staleTTL control reuse; timeoutMs limits navigation and rendering time. Cache stable documentation or marketing pages to reduce repeated work, but disable or shorten the TTL for dashboards and frequently changing data. Set your Ruby HTTP timeout at least as high as the API’s rendering timeout, while still enforcing an application-level upper bound.
Rank #3
Batch screenshots from Ruby
For many URLs, POST to /api/v1/screenshot/batch with an array and shared options:
require "net/http"
require "json"
require "uri"
endpoint = URI("https://api.screenshot-api.org/api/v1/screenshot/batch")
request = Net::HTTP::Post.new(endpoint)
request["Authorization"] = "Bearer #{ENV.fetch("SCREENSHOT_API_KEY")}"
request["Content-Type"] = "application/json"
request.body = {
urls: ["https://example.com", "https://example.org"],
options: { format: "webp", fullPage: true }
}.to_json
response = Net::HTTP.start(endpoint.hostname, endpoint.port, use_ssl: true) { |http| http.request(request) }
abort("batch failed: #{response.code} #{response.body}") unless response.is_a?(Net::HTTPSuccess)
puts JSON.parse(response.body).fetch("batchId")
The documented response contains a batch ID. Poll GET /api/v1/batch/:batchId for progress or consume its server-sent events endpoint when your worker can maintain a stream. Add retry and backoff around polling rather than issuing requests in a tight loop.
Ruby gem versus raw HTTP
| Approach | Dependencies | Output handling | When it fits |
|---|---|---|---|
| Standard library | None beyond Ruby | You parse JSON and download the URL yourself | Small services, jobs, and teams that want full request control |
screenshot-api gem |
gem install screenshot-api |
Gem abstractions around the documented API | Rails, Sinatra, or Ruby applications that prefer an SDK; verify the gem’s current API before upgrading |
screenshotone SDK pattern |
gem "screenshotone", then bundle install |
Can generate a take URL or retrieve image bytes | Projects that want fluent options such as full-page, delay, and geolocation |
The ScreenshotOne Ruby pattern uses separate access and secret keys, ScreenshotOne::Client, TakeOptions, generate_take_url, and take. The raw approach remains easier to audit because the HTTP method, headers, body, and error handling are visible in your code.
Error handling and operational limits
Parse the documented error envelope: success, error.code, error.message, optional details, and a request ID. Handle these cases explicitly:
Rank #4
- 401
unauthorized: the key is missing, malformed, revoked, or sent with the wrong scheme. Check the environment variable and the exactBearerheader. - 400
invalid_request: a required field or option has the wrong type or spelling. Log the sanitized request shape and validate URLs before sending. - 422
selector_not_found: the selector never appeared. Confirm it in the target page, increase the selector wait, or remove the selector requirement. - 429
rate_limited: slow down and retry with exponential backoff, honoring rate-limit headers. - 429
quota_exceeded: the account’s monthly allowance is exhausted; wait for renewal or change plan. - 502
render_failed: the remote browser could not load or render the page. Retry transient failures, then test the URL without blocking, custom scripts, or an overly short timeout.
The published free-plan figures are 60 requests per minute and 500 screenshots per month; these are service facts that can change, so verify the current limits in the provider’s documentation before designing capacity. Responses include rate-limit and quota headers. Record the request ID, status, elapsed time, and page URL (without secrets) so failures can be traced.
Reliability and security checklist
- Keep API keys in environment variables or a secret manager; never commit them or expose them to browser JavaScript.
- Validate and allow-list destination URLs if users can submit targets. Otherwise your screenshot worker can become a server-side request forgery (SSRF) proxy.
- Set connect, read, and overall job timeouts; do not let a hung page occupy a worker forever.
- Retry only transient network errors, 502 responses, and 429 responses after backoff. Do not blindly retry 401 or 422 errors.
- Use idempotent job identifiers or deduplicate URLs when a retry could create duplicate captures.
- Inspect the content type before treating a response as an image. Save JSON error bodies for diagnostics, not as
.pngfiles. - Limit image dimensions and file sizes before handing output to image processors.
Which screenshot API should you choose?
ScreenshotNeo is the first service to try when you want a Ruby-friendly HTTP endpoint without browser setup: it removes consent banners, newsletter popups, and chat widgets before capture, bills only clean shots, and its paid entry plan is $5 for 3,000 shots.
Use the documented Screenshot API workflow above when you specifically need its JSON screenshot URL, batch endpoint, or the listed rendering controls. A gem can reduce boilerplate, while raw Net::HTTP keeps your dependency footprint small. Compare services on authentication, whether they return bytes or a URL, full-page and dynamic-content controls, batch behavior, error semantics, and regional settings rather than on a single “screenshot” checkbox.
Or skip the browser setup
ScreenshotNeo is a GET-based website screenshot API and MCP server. It accepts a URL and returns PNG, JPEG, WebP, or PDF. Before capture it accepts cookie/consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be disabled. Bot checks/CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and the response identifies the page verdict and billing with X-Page-Verdict and X-Billed headers. Its MCP tools—take_screenshot, get_page_info, and capture_pdf—work with Claude, Cursor, and other MCP clients.
Ruby can call it with the standard library or any HTTP client:
require "net/http"
require "uri"
uri = URI("https://api.screenshotneo.com/v1/shot")
uri.query = URI.encode_www_form(access_key: ENV.fetch("SCREENSHOTNEO_API_KEY"), url: "https://stripe.com")
response = Net::HTTP.get_response(uri)
abort("capture failed: #{response.code}") unless response.is_a?(Net::HTTPSuccess)
File.binwrite("shot.webp", response.body)
Equivalent command-line, Python, and Node.js calls are useful for workers and CI jobs:
Best Value
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
See the full parameter reference and advanced examples in the ScreenshotNeo documentation. Every plan includes its features: 1,000 shots per month are free with no card; paid plans start at $5 for 3,000, with higher plans at $15 for 15,000, $39 for 60,000, $99 for 250,000, and $249 for 1,000,000. Yearly billing provides two months free. Create a free ScreenshotNeo account to start.
Frequently asked questions
Can Ruby capture a page without installing Chromium?
Yes. The Ruby process sends HTTPS requests; rendering happens on the screenshot provider’s infrastructure. A local browser is unnecessary for the API examples above.
Why did my saved PNG contain JSON?
Most likely the API returned an error status. Check response.is_a?(Net::HTTPSuccess) and inspect the JSON error envelope before writing bytes to a file.
When should I use a batch job?
Use the batch endpoint when many URLs share rendering options and you can process an asynchronous batch ID. For one-off captures, the single screenshot endpoint is simpler.
Frequently Asked Questions
Can Ruby capture a page without installing Chromium?
Yes. The Ruby process sends HTTPS requests; rendering happens on the screenshot provider’s infrastructure. A local browser is unnecessary for the API examples above.
Why did my saved PNG contain JSON?
Most likely the API returned an error status. Check the HTTP status and inspect the JSON error envelope before writing bytes to a file.
When should I use a batch job?
Use the batch endpoint when many URLs share rendering options and you can process an asynchronous batch ID. For one-off captures, the single screenshot endpoint is simpler.
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.

