What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

  1. 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.
  2. 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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Download 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

  • url is required.
  • format accepts png, jpeg, webp, or pdf; PNG is the documented default.
  • viewport.width and viewport.height set the browser viewport.
  • fullPage: true captures the entire scrollable page rather than only the viewport.
  • deviceScaleFactor increases 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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Waiting for JavaScript and lazy content

  • waitUntil chooses a navigation-completion condition.
  • waitForSelector waits for a particular element before capture.
  • delayMs adds 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.

Selectors, ads, consent, and appearance

  • selector captures one CSS-selected element. The documented API does not support this option for PDF output.
  • hideSelectors hides matching elements without changing the source page.
  • blockAds and blockCookieBanners default to true in the reference table; set them explicitly when reproducibility matters.
  • darkMode defaults to false.
  • POST-only css and js let 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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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:

  • 401 unauthorized: the key is missing, malformed, revoked, or sent with the wrong scheme. Check the environment variable and the exact Bearer header.
  • 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 .png files.
  • Limit image dimensions and file sizes before handing output to image processors.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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:

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

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.