Screenshot -- Ruby Code Examples

HTTP-based examples for integrating the Nodium Screenshot API with Ruby using the net/http standard library, HTTParty, and Rails.

Basic Screenshot (GET)

The simplest way to capture a screenshot using the standard library.

ruby
require "net/http"
require "uri"
require "json"

api_key = "YOUR_API_KEY"

params = URI.encode_www_form(
  url: "https://example.com",
  format: "png",
  viewport_width: 1280,
  viewport_height: 1024
)

uri = URI.parse("https://api.nodium.io/api/v1/screenshot/take?#{params}")

request = Net::HTTP::Get.new(uri)
request["X-Access-Key"] = api_key

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
  http.request(request)
end

if response.code == "200"
  File.binwrite("screenshot.png", response.body)
  puts "Screenshot saved (#{response.body.bytesize} bytes)"
else
  error = JSON.parse(response.body)
  puts "Error: #{error['error_code']} - #{error['message']}"
end

POST with JSON Body

Use POST for complex configurations with a JSON request body.

ruby
require "net/http"
require "uri"
require "json"

api_key = "YOUR_API_KEY"
uri = URI.parse("https://api.nodium.io/api/v1/screenshot/take")

request = Net::HTTP::Post.new(uri)
request["Content-Type"] = "application/json"
request["X-Access-Key"] = api_key
request.body = JSON.generate(
  url: "https://example.com",
  format: "png",
  viewport_width: 1440,
  viewport_height: 900,
  full_page: true,
  image_quality: 90,
  block_ads: true,
  delay: 1000
)

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
  http.open_timeout = 10
  http.read_timeout = 60
  http.request(request)
end

if response.code == "200"
  File.binwrite("screenshot.png", response.body)

  puts "Screenshot saved"
  puts "Rendering time: #{response['x-nodium-rendering-seconds']}s"
  puts "File size: #{response['x-nodium-size-bytes']} bytes"
else
  error = JSON.parse(response.body)
  puts "Error [#{error['error_code']}]: #{error['message']}"
end

Reusable Screenshot Client

A helper class that wraps the API with error handling and retries.

ruby
require "net/http"
require "uri"
require "json"

class NodiumClient
  BASE_URL = "https://api.nodium.io/api/v1/screenshot"

  def initialize(api_key, max_retries: 3, timeout: 60)
    @api_key = api_key
    @max_retries = max_retries
    @timeout = timeout
  end

  def take(params)
    request_with_retry("#{BASE_URL}/take", params)
  end

  def animate(params)
    request_with_retry("#{BASE_URL}/animate", params)
  end

  def bulk(params)
    request_with_retry("#{BASE_URL}/bulk", params)
  end

  private

  def request_with_retry(url, params)
    uri = URI.parse(url)
    attempt = 0

    loop do
      request = Net::HTTP::Post.new(uri)
      request["Content-Type"] = "application/json"
      request["X-Access-Key"] = @api_key
      request.body = JSON.generate(params)

      response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
        http.open_timeout = 10
        http.read_timeout = @timeout
        http.request(request)
      end

      return response.body if response.code == "200"

      error = begin
        JSON.parse(response.body)
      rescue JSON::ParserError
        { "error_code" => "unknown", "message" => response.body }
      end

      # Do not retry client errors (4xx)
      if response.code.to_i >= 400 && response.code.to_i < 500
        raise "API error [#{error['error_code']}]: #{error['message']}"
      end

      # Retry server errors (5xx) with exponential backoff
      attempt += 1
      if attempt > @max_retries
        raise "Max retries (#{@max_retries}) exceeded. Last error: #{error['message']}"
      end

      delay = 2**attempt
      puts "Retry #{attempt}/#{@max_retries} after #{delay}s..."
      sleep(delay)
    end
  end
end

Using the Client

ruby
client = NodiumClient.new("YOUR_API_KEY")

data = client.take(
  url: "https://example.com",
  format: "png",
  viewport_width: 1280,
  viewport_height: 1024,
  full_page: true
)

File.binwrite("screenshot.png", data)
puts "Screenshot saved (#{data.bytesize} bytes)"

Error Handling

Handle specific error codes to take appropriate action.

ruby
require "net/http"
require "uri"
require "json"

def capture_screenshot(url, api_key)
  uri = URI.parse("https://api.nodium.io/api/v1/screenshot/take")

  request = Net::HTTP::Post.new(uri)
  request["Content-Type"] = "application/json"
  request["X-Access-Key"] = api_key
  request.body = JSON.generate(url: url, format: "png")

  response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
    http.read_timeout = 60
    http.request(request)
  end

  if response.code == "200"
    return response.body
  end

  error = JSON.parse(response.body)

  case error["error_code"]
  when "access_key_required", "access_key_invalid"
    raise "Authentication failed: #{error['message']}"
  when "screenshots_limit_reached"
    raise "Monthly quota exceeded. Upgrade your plan."
  when "concurrency_limit_reached"
    raise "Too many simultaneous requests. Try again shortly."
  when "request_not_valid"
    raise "Invalid parameters: #{error['message']}"
  when "timeout_error", "network_error", "internal_application_error"
    raise "Server error (retryable): #{error['message']}"
  when "temporary_unavailable"
    raise "Service temporarily unavailable. Try again later."
  else
    raise "Unknown error [#{error['error_code']}]: #{error['message']}"
  end

rescue Net::OpenTimeout, Net::ReadTimeout
  raise "Request timed out"
rescue JSON::ParserError
  raise "Invalid response from API"
end

HTTParty Alternative

Using HTTParty for a more concise API.

Gemfile

ruby
gem "httparty"

Example

ruby
require "httparty"

class NodiumScreenshot
  include HTTParty
  base_uri "https://api.nodium.io/api/v1/screenshot"

  def initialize(api_key)
    @api_key = api_key
  end

  def take(params)
    response = self.class.post("/take",
      headers: {
        "Content-Type" => "application/json",
        "X-Access-Key" => @api_key,
      },
      body: params.to_json,
      timeout: 60
    )

    if response.success?
      response.body
    else
      error = response.parsed_response
      raise "API error [#{error['error_code']}]: #{error['message']}"
    end
  end
end

# Usage
client = NodiumScreenshot.new("YOUR_API_KEY")

data = client.take(
  url: "https://example.com",
  format: "png",
  viewport_width: 1280,
  viewport_height: 1024
)

File.binwrite("screenshot.png", data)
puts "Screenshot saved"

Rails Integration

Configuration

ruby
# config/initializers/nodium.rb
Rails.application.config.nodium_api_key = ENV.fetch("NODIUM_API_KEY")

Service Object

ruby
# app/services/screenshot_service.rb
class ScreenshotService
  def initialize
    @client = NodiumClient.new(
      Rails.application.config.nodium_api_key,
      max_retries: 3,
      timeout: 60
    )
  end

  def capture(url, format: "png", width: 1280, height: 1024, full_page: false)
    @client.take(
      url: url,
      format: format,
      viewport_width: width,
      viewport_height: height,
      full_page: full_page
    )
  end

  def capture_and_store(url, **options)
    data = capture(url, **options)
    format = options.fetch(:format, "png")

    blob = ActiveStorage::Blob.create_and_upload!(
      io: StringIO.new(data),
      filename: "screenshot-#{Time.current.to_i}.#{format}",
      content_type: "image/#{format}"
    )

    blob
  end
end

Controller

ruby
# app/controllers/screenshots_controller.rb
class ScreenshotsController < ApplicationController
  def capture
    url = params.require(:url)
    service = ScreenshotService.new

    data = service.capture(url)

    send_data data,
      type: "image/png",
      disposition: "inline",
      filename: "screenshot.png"

  rescue => e
    render json: { error: e.message }, status: :unprocessable_entity
  end
end

Route

ruby
# config/routes.rb
Rails.application.routes.draw do
  get "/screenshots/capture", to: "screenshots#capture"
end

Signed URL Generation

Generate a signed URL by computing an HMAC-SHA256 signature over the query parameters.

ruby
require "openssl"
require "uri"

def generate_signed_url(api_key, secret_key, params, expires_in: 3600)
  expires = (Time.now + expires_in).to_i

  all_params = params.merge(
    access_key: api_key,
    expires: expires
  )

  # Sort parameters for consistent signing
  sorted = all_params.sort_by { |k, _| k.to_s }.to_h
  string_to_sign = URI.encode_www_form(sorted)

  # Compute HMAC-SHA256 signature
  signature = OpenSSL::HMAC.hexdigest("SHA256", secret_key, string_to_sign)

  # Build the final URL
  query = URI.encode_www_form(sorted.merge(signature: signature))
  "https://api.nodium.io/api/v1/screenshot/take?#{query}"
end

# Usage
signed_url = generate_signed_url(
  "YOUR_API_KEY",
  "YOUR_SECRET_KEY",
  {
    url: "https://example.com",
    format: "png",
    viewport_width: 1280,
    viewport_height: 1024,
  },
  expires_in: 3600
)

puts signed_url

Next Steps