Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.
To send a header to the screenshot service, set it on the Ruby HTTP request—typically Authorization: Bearer …. To send a header to the website being captured, put it in the screenshot API’s target-page header parameter instead. Those are different requests and different headers: confusing them is a common cause of authentication failures or captures that still show a login page.
Two requests, two places for headers
Your Ruby program calls the screenshot provider. The provider then loads the target website in a browser. A header added to the first request does not automatically become a header on the second.
- API request header: Ruby sends this to the screenshot API, usually to authenticate your account. For the API documented here, use
Authorization: Bearer YOUR_API_KEY. - Target-page header: The screenshot service sends this to the page it renders, for example a preview token required by a staging site. Configure it as a screenshot API parameter; setting it on Ruby’s request to the API will not make the browser send it to the target.
The examples below use the API endpoint and conventions in the cited provider documentation. Treat https://screenshot-api.net/v1/screenshot as that provider’s endpoint, not as a universal screenshot API URL. Other providers may name their parameters or authentication methods differently.
Send an API authentication header with Ruby
Ruby’s built-in Net::HTTP can set a request header directly. This GET example authenticates the API call with a bearer token, passes a target URL, checks the HTTP response, and writes the raw image response as binary data.
#1 Best Overall
require "net/http"
require "uri"
api_key = ENV.fetch("SCREENSHOT_API_KEY")
target_url = "https://example.com"
uri = URI("https://screenshot-api.net/v1/screenshot")
uri.query = URI.encode_www_form("url" => target_url)
request = Net::HTTP::Get.new(uri)
request["Authorization"] = "Bearer #{api_key}"
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https") do |http|
http.request(request)
end
unless response.is_a?(Net::HTTPSuccess)
raise "Screenshot API request failed: #{response.code} #{response.message}"
end
File.binwrite("shot.png", response.body)
puts "Rendered page status: #{response['X-Page-Status']}"
Set the key in the environment before running the script, rather than putting it directly in source code. For example, in a Unix-like shell, run export SCREENSHOT_API_KEY='your-key', then run the Ruby file. Keep the key secret and avoid printing it in logs.
The documented screenshot endpoint returns image bytes directly, not a JSON object containing an image URL. File.binwrite preserves those bytes; using text-mode handling can corrupt binary image data on platforms where text and binary modes differ. The response’s content type identifies the returned format, so choose an output extension that matches the requested format and the provider’s response.
Send a custom header to the rendered website
For the target website’s header, add the API’s header parameter. In the documented GET form, it is repeatable and takes a value in Name: value form. This example supplies a staging preview token while keeping the API bearer token on the Ruby-to-provider request.
require "net/http"
require "uri"
api_key = ENV.fetch("SCREENSHOT_API_KEY")
preview_token = ENV.fetch("PREVIEW_TOKEN")
params = [
["url", "https://example.com"],
["header", "X-Preview-Token: #{preview_token}"]
]
uri = URI("https://screenshot-api.net/v1/screenshot")
uri.query = URI.encode_www_form(params)
request = Net::HTTP::Get.new(uri)
request["Authorization"] = "Bearer #{api_key}"
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(request)
end
unless response.is_a?(Net::HTTPSuccess)
raise "Screenshot API request failed: #{response.code} #{response.message}"
end
File.binwrite("preview.png", response.body)
puts "Rendered page status: #{response['X-Page-Status']}"
Use an array of key-value pairs with URI.encode_www_form when a query parameter can occur more than once. A Ruby hash cannot represent duplicate keys, so a hash is not suitable for constructing multiple repeated header parameters. For example:
Rank #2
params = [
["url", "https://example.com"],
["header", "X-Preview-Token: #{preview_token}"],
["header", "X-Region: test"]
]
uri.query = URI.encode_www_form(params)
Header values and URLs may contain characters that must be encoded in a query string. Let URI.encode_www_form do that encoding; do not manually concatenate unescaped values into the URL.
Use POST when target headers contain credentials
Query parameters may be recorded in access logs. The provider recommends its POST form when parameters contain credentials, and documents a headers object for target-page headers. Keep the API key in its documented authorization header and send the destination header object in the JSON body.
The precise POST path and any additional required fields must match the provider’s documentation; the following Ruby pattern shows how to construct a JSON POST request with Net::HTTP. Confirm the endpoint path and accepted JSON fields for the API account you use before deploying it.
Recommended Free Tools
require "net/http"
require "uri"
require "json"
api_key = ENV.fetch("SCREENSHOT_API_KEY")
preview_token = ENV.fetch("PREVIEW_TOKEN")
uri = URI("https://screenshot-api.net/v1/screenshot")
request = Net::HTTP::Post.new(uri)
request["Authorization"] = "Bearer #{api_key}"
request["Content-Type"] = "application/json"
request["Accept"] = "image/png"
request.body = JSON.generate(
url: "https://example.com",
headers: { "X-Preview-Token" => preview_token }
)
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(request)
end
unless response.is_a?(Net::HTTPSuccess)
raise "Screenshot API request failed: #{response.code} #{response.message}"
end
File.binwrite("preview.png", response.body)
puts "Rendered page status: #{response['X-Page-Status']}"
This uses Ruby’s standard JSON library to serialize the body. The API documentation described here supports a POST form with a headers object, but implementations vary in route, content negotiation and response format. Do not assume every screenshot API accepts the illustrative route or returns an image for POST; follow the provider’s documented contract.
Rank #3
Know which target headers the API permits
The target-header mechanism is intentionally not a way to control every part of the browser request. The documented provider scopes those headers to the target host and does not forward them to a different host after a redirect. It also refuses Host, Cookie and hop-by-hop headers through this mechanism.
- Redirects: If the target redirects to another host, a target header may not reach the destination. Check the redirect destination and use a supported authentication method appropriate to that flow.
- Cookies: Do not try to pass a
Cookieheader through the target-header parameter. Use the provider’s separately documented cookie option. - Basic authentication: If the page requires HTTP basic authentication, use the provider’s basic-auth option rather than assuming a custom header will substitute.
- Hop-by-hop headers: These govern an individual transport connection and are not accepted as target-page headers by this service.
A header such as X-Preview-Token is a more typical custom target header. Check the provider’s allowed-header rules and the destination server’s expected name and value, including capitalization-independent header matching and any exact token requirements.
Check whether the captured page is actually the right one
A successful HTTP response from the screenshot API means the API request completed; it does not prove that the target page rendered the intended content. The rendered page may itself be a login or error page. The documented endpoint includes X-Page-Status, the final target document’s HTTP status. Check it alongside the API response status before treating the image as a successful capture.
page_status = response["X-Page-Status"]
warn "Target returned HTTP #{page_status}" if page_status == "401" || page_status == "403"
The provider notes that a target status of 401 or 403 may indicate that an error or login page was captured. Inspect the image when the page status is unexpected; an image response can still faithfully depict the wrong page. The provider’s alternate capture endpoint reports status in JSON, so adapt the parsing logic if using that endpoint rather than the raw-image endpoint.
Rank #4
Common errors and fixes
| Symptom | Likely cause | What to check |
|---|---|---|
| API responds with an authentication error | The bearer token is missing, malformed, expired or sent to the wrong endpoint. | Confirm SCREENSHOT_API_KEY is present and that the request uses Authorization: Bearer … as the provider documents. Do not put a target-page token in place of the API key. |
| Capture shows a login or access-denied page | The target header was sent to the API rather than configured as a target-page parameter, the value is wrong, or the page returned an authentication failure. | Pass the header as header on GET or in the POST headers object; inspect X-Page-Status and the captured image. |
| Header works on the original host but not after redirect | The provider does not forward target headers to a different host. | Check the redirect chain and destination host. Use a supported destination authentication mechanism instead of relying on cross-host forwarding. |
| Cookie-based session does not work | The service refuses Cookie in its custom target-header mechanism. |
Use the provider’s dedicated cookie option and follow its required format. |
| Image file is corrupt or unreadable | Text-mode output, an API error body saved as an image, or a mismatch between extension and returned format. | Check the API HTTP status and content type, then write successful response bytes with File.binwrite. |
| Credential is visible in a URL or logs | A secret was placed in a GET query string. | Use the documented POST form for credential-bearing parameters and keep API authentication in the authorization header. |
| Ruby raises for a missing environment variable | ENV.fetch correctly fails because the secret is unset. |
Set the required variable in the process environment, or handle the missing value explicitly before making the request. |
Timeouts, output and operational notes
The provider documentation lists a default viewport of 1280 by 800 CSS pixels, maximum width of 3840, maximum height of 4320 and default render timeout of 25 seconds. These are provider configuration values, not Ruby limits or independent performance guarantees. If a page needs longer to render, check the provider’s available timeout settings and limits rather than assuming that increasing Ruby’s network timeout alone extends the browser render window.
Likewise, network connection/read behavior in Net::HTTP and the screenshot provider’s browser-render timeout are separate concerns. A request can wait on the HTTP connection while the service is rendering, but a client-side timeout cannot force a provider to render past its own configured limit. Handle non-success HTTP responses before saving the body, and log status codes and request identifiers if the provider supplies them—never log API keys or page credentials.
For a production integration, consider retrying only transient network failures or retryable service errors, with a bounded retry count and backoff. Avoid blindly retrying a bad key, invalid parameters or target authentication failure; those require correcting the request. If captures are large, stream responses if supported by your chosen client/provider pattern rather than keeping many full image bodies in memory.
Quick wins for a faster PC:
Repair Windows errors before they cause bigger problemsFix Now →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Clear out junk files and repair common Windows errorsFree Scan →Or skip the browser setup
If you would rather call a screenshot endpoint than maintain a browser-rendering setup, ScreenshotNeo accepts a URL in one GET request and returns PNG, JPEG, WebP or PDF output. Its parameter names also work with those used by other screenshot APIs, which can make switching easier. See the ScreenshotNeo service and API documentation for current request details.
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
ScreenshotNeo removes cookie and consent banners, newsletter popups and chat widgets before capture; each of those steps can be turned off. Bot checks, blank pages, timeouts, failed loads and cache hits are not billed, and response headers identify the page verdict and billing status. Its MCP server offers take_screenshot, get_page_info and capture_pdf for Claude, Cursor and other MCP clients. The Free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots. Every feature is on every plan.
Sign up for ScreenshotNeo’s free plan to get 1,000 screenshots a month without a card.
Sources
- Screenshot API documentation for endpoint behavior, authentication, target-page headers, response headers and configuration values.
- Ruby Net::HTTP documentation for request-header support.
Frequently Asked Questions
Does a 200 response from the screenshot API mean the target page returned 200?
No. The API’s HTTP status describes the API request. Check the target status separately using the documented X-Page-Status response header.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Can I use the target-page header parameter to set a Cookie header?
No. The documented service refuses Cookie in that mechanism; use its separate cookie option.
Can I send a target header through a cross-host redirect?
The documented service says it does not forward target headers to a different host after a redirect.
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.

