Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/browserbase/stagehand/llms.txt

Use this file to discover all available pages before exploring further.

Overview

The Ruby SDK provides a Ruby-native interface to Stagehand’s browser automation capabilities. It follows Ruby conventions and idioms while communicating with the Stagehand API server.
The Ruby SDK is currently under development. This page describes the planned architecture and usage patterns. Check the GitHub repository for the latest status.

Architecture

The Ruby SDK follows the same client-server architecture:
┌─────────────────┐
│   Ruby Client   │
│   (Your Code)   │
└────────┬────────┘
         │ HTTP/REST

┌─────────────────┐
│  API Server     │
│  (TypeScript)   │
└────────┬────────┘
         │ CDP

┌─────────────────┐
│  Browser        │
│  (Browserbase)  │
└─────────────────┘

Installation

Add to your Gemfile:
gem 'stagehand'
Or install directly:
gem install stagehand

Repository

The Ruby SDK will be maintained in a dedicated repository:

Ruby SDK on GitHub

View the Ruby SDK repository (coming soon)

Quick Start

require 'stagehand'

# Initialize Stagehand
stagehand = Stagehand::Client.new(
  env: 'BROWSERBASE',
  api_key: ENV['BROWSERBASE_API_KEY'],
  project_id: ENV['BROWSERBASE_PROJECT_ID'],
  model: {
    model_name: 'openai/gpt-4o',
    api_key: ENV['OPENAI_API_KEY']
  },
  verbose: 1
)

begin
  # Initialize session
  stagehand.init
  
  # Navigate to a page
  page = stagehand.page
  page.goto('https://github.com/browserbase')
  
  # Perform an action
  result = stagehand.act('click on the stagehand repo')
  puts "Action: #{result.message}"
  
  # Extract data
  data = stagehand.extract(
    instruction: 'extract the repository details',
    schema: {
      description: 'string',
      stars: 'number'
    }
  )
  puts "Data: #{data}"
ensure
  stagehand.close
end

Core API

Client Initialization

config = {
  env: 'BROWSERBASE',
  api_key: ENV['BROWSERBASE_API_KEY'],
  project_id: ENV['BROWSERBASE_PROJECT_ID'],
  model: {
    model_name: 'openai/gpt-4o',
    api_key: ENV['OPENAI_API_KEY'],
    temperature: 0.7
  },
  verbose: 1,
  system_prompt: 'Custom AI instructions',
  self_heal: true,
  cache_dir: './cache'
}

client = Stagehand::Client.new(config)

Methods

act()

Execute actions with natural language:
# Simple action
result = stagehand.act('click the login button')

# With variables
result = stagehand.act(
  'fill the email field with {{email}}',
  variables: { email: 'user@example.com' },
  timeout: 5000
)

puts result.success
puts result.message

extract()

Extract structured data:
schema = {
  name: 'string',
  email: 'string',
  age: 'number'
}

data = stagehand.extract(
  instruction: 'get user information',
  schema: schema
)

puts "Name: #{data[:name]}"
puts "Email: #{data[:email]}"

observe()

Find elements without acting:
actions = stagehand.observe('find all product cards')

actions.each do |action|
  puts "Selector: #{action[:selector]}"
  puts "Description: #{action[:description]}"
end

agent.execute()

Run multi-step autonomous tasks:
agent = stagehand.agent

result = agent.execute(
  'Search for Ruby tutorials and extract the top 3 results',
  max_steps: 10
)

puts "Message: #{result[:message]}"
result[:actions].each do |action|
  puts "Step: #{action[:type]}"
end

Ruby Idioms

Blocks and Yield

Use blocks for automatic cleanup:
Stagehand::Client.open(config) do |stagehand|
  stagehand.init
  page = stagehand.page
  page.goto('https://example.com')
  
  result = stagehand.act('click button')
  puts result.message
end  # Automatically closes

Symbols vs Strings

Use Ruby conventions:
# Symbol keys for options
stagehand.act('click', timeout: 5000, variables: { user: 'john' })

# String keys for schemas (matches API)
schema = {
  'name' => 'string',
  'age' => 'number'
}

Method Chaining

result = stagehand
  .page
  .goto('https://example.com')
  .then { stagehand.act('click button') }
  .then { |_| stagehand.extract(instruction: 'get text', schema: schema) }

Examples

Web Scraping

require 'stagehand'

def scrape_news
  Stagehand::Client.open(config) do |stagehand|
    stagehand.init
    
    stagehand.page.goto('https://news.ycombinator.com')
    
    schema = {
      'articles' => [
        {
          'title' => 'string',
          'url' => 'string',
          'points' => 'number'
        }
      ]
    }
    
    data = stagehand.extract(
      instruction: 'extract the top 5 articles',
      schema: schema
    )
    
    data['articles'].each do |article|
      puts "#{article['title']} - #{article['points']} points"
    end
    
    data['articles']
  end
end

articles = scrape_news

Form Automation

class FormFiller
  def initialize(stagehand)
    @stagehand = stagehand
  end
  
  def fill_and_submit(name:, email:)
    page = @stagehand.page
    page.goto('https://example.com/form')
    
    variables = { name: name, email: email }
    
    @stagehand.act(
      'fill the name field with {{name}}',
      variables: variables
    )
    
    @stagehand.act(
      'fill the email field with {{email}}',
      variables: variables
    )
    
    @stagehand.act('click the submit button')
    
    schema = { 'message' => 'string' }
    result = @stagehand.extract(
      instruction: 'get the success message',
      schema: schema
    )
    
    puts "Result: #{result['message']}"
    result
  end
end

Stagehand::Client.open(config) do |stagehand|
  stagehand.init
  filler = FormFiller.new(stagehand)
  filler.fill_and_submit(name: 'John Doe', email: 'john@example.com')
end

Error Handling

begin
  result = stagehand.act('click button', timeout: 5000)
rescue Stagehand::ActTimeoutError => e
  puts "Action timed out: #{e.message}"
rescue Stagehand::APIError => e
  puts "API error: #{e.message}"
  puts "Status code: #{e.status_code}"
rescue Stagehand::Error => e
  puts "Stagehand error: #{e.message}"
end

Rails Integration

Initializer

# config/initializers/stagehand.rb
Stagehand.configure do |config|
  config.env = 'BROWSERBASE'
  config.api_key = Rails.application.credentials.browserbase[:api_key]
  config.project_id = Rails.application.credentials.browserbase[:project_id]
  config.model = {
    model_name: 'openai/gpt-4o',
    api_key: Rails.application.credentials.openai[:api_key]
  }
  config.cache_dir = Rails.root.join('tmp', 'stagehand-cache')
  config.logger = Rails.logger
end

Service Object

# app/services/web_scraper_service.rb
class WebScraperService
  def self.scrape_product(url)
    Stagehand::Client.open do |stagehand|
      stagehand.init
      stagehand.page.goto(url)
      
      schema = {
        'name' => 'string',
        'price' => 'number',
        'description' => 'string'
      }
      
      stagehand.extract(
        instruction: 'extract product details',
        schema: schema
      )
    end
  end
end

# Usage in controller
class ProductsController < ApplicationController
  def scrape
    data = WebScraperService.scrape_product(params[:url])
    render json: data
  end
end

Background Job

# app/jobs/scrape_job.rb
class ScrapeJob < ApplicationJob
  queue_as :default
  
  def perform(url, user_id)
    Stagehand::Client.open do |stagehand|
      stagehand.init
      stagehand.page.goto(url)
      
      data = stagehand.extract(
        instruction: 'extract page content',
        schema: { 'content' => 'string' }
      )
      
      # Save to database
      ScrapedData.create!(
        user_id: user_id,
        url: url,
        content: data['content']
      )
    end
  end
end

Testing

RSpec

require 'spec_helper'

RSpec.describe 'Stagehand Integration' do
  let(:stagehand) do
    Stagehand::Client.new(
      env: 'BROWSERBASE',
      api_key: ENV['BROWSERBASE_API_KEY'],
      project_id: ENV['BROWSERBASE_PROJECT_ID']
    )
  end
  
  before { stagehand.init }
  after { stagehand.close }
  
  it 'extracts data from page' do
    stagehand.page.goto('https://example.com')
    
    data = stagehand.extract(
      instruction: 'get the title',
      schema: { 'title' => 'string' }
    )
    
    expect(data['title']).to be_a(String)
    expect(data['title']).not_to be_empty
  end
end

Minitest

require 'minitest/autorun'
require 'stagehand'

class StagehandTest < Minitest::Test
  def setup
    @stagehand = Stagehand::Client.new(config)
    @stagehand.init
  end
  
  def teardown
    @stagehand.close
  end
  
  def test_act_method
    @stagehand.page.goto('https://example.com')
    result = @stagehand.act('click button')
    
    assert result.success
    assert_kind_of String, result.message
  end
end

Async Support

For async operations (with concurrent-ruby):
require 'concurrent'

promises = urls.map do |url|
  Concurrent::Promise.execute do
    Stagehand::Client.open(config) do |stagehand|
      stagehand.init
      stagehand.page.goto(url)
      stagehand.extract(
        instruction: 'get title',
        schema: { 'title' => 'string' }
      )
    end
  end
end

results = promises.map(&:value)
results.each { |data| puts data['title'] }

Working with the API Server

The Ruby SDK requires the Stagehand API server:
# Use Browserbase hosted API
stagehand = Stagehand::Client.new(
  env: 'BROWSERBASE',
  api_key: ENV['BROWSERBASE_API_KEY'],
  project_id: ENV['BROWSERBASE_PROJECT_ID']
)

# Or custom endpoint
stagehand = Stagehand::Client.new(
  env: 'LOCAL',
  api_url: 'http://localhost:3000'
)

Resources

Ruby SDK Repository

Source code (coming soon)

TypeScript SDK

Reference implementation

RubyGems

Browse Ruby gems

API Documentation

Full API reference

Contributing

Interested in contributing to the Ruby SDK?

Build docs developers (and LLMs) love