Ruby SDK
Create sessions, interact with pages, and extract structured data.
Installation #
Install the gem from RubyGems:
gem install browserbeam
Or add it to your Gemfile:
gem "browserbeam"
Then run:
bundle install
Quick Start #
Create a client, open a session, and interact with a page:
require "browserbeam"
client = Browserbeam::Client.new(api_key: "bb_your_key")
session = client.sessions.create(url: "https://example.com")
session.click(text: "More information")
result = session.extract(title: "string", paragraphs: ["string"])
puts result.extraction
session.close
Configuration #
Pass options when you create the client:
client = Browserbeam::Client.new(
api_key: "bb_your_key", # or set BROWSERBEAM_API_KEY env var
base_url: "https://api.browserbeam.com", # default
timeout: 120 # request timeout in seconds, default 120
)
| Option | Default | Description |
|---|---|---|
api_key |
ENV["BROWSERBEAM_API_KEY"] |
Your API key. The param takes priority over the env var. |
base_url |
https://api.browserbeam.com |
API base URL. |
timeout |
120 |
HTTP request timeout in seconds. |
api_key, the SDK reads BROWSERBEAM_API_KEY from the environment. If neither is set, it raises an ArgumentError.
Creating Sessions #
Call client.sessions.create to launch a browser session. It returns a Session object you use for all subsequent actions.
session = client.sessions.create(
url: "https://example.com",
viewport: { width: 1280, height: 720 },
locale: "en-US",
timezone: "America/New_York",
user_agent: "MyBot/1.0",
proxy: { kind: "residential", country: "us" },
block_resources: ["image", "font"],
auto_dismiss_blockers: true,
cookies: [{ name: "session", value: "abc123", domain: ".example.com" }],
idempotency_key: "unique-request-id"
)
puts session.page.title
| Parameter | Type | Description |
|---|---|---|
url |
String | URL to navigate to after launch. |
viewport |
Hash | { width:, height: } — browser viewport size. |
locale |
String | Browser locale, e.g. "en-US". |
timezone |
String | IANA timezone, e.g. "America/New_York". |
user_agent |
String | Custom user-agent string. |
proxy |
Hash | String | Managed proxy { kind: "datacenter"|"residential", country: "us"|"auto" } or BYO proxy URL. Default: datacenter, auto. |
block_resources |
Array | Resource types to block, e.g. ["image", "font"]. |
auto_dismiss_blockers |
Boolean | Auto-dismiss cookie banners and overlays. |
cookies |
Array | Cookies to set before navigation. |
idempotency_key |
String | Prevents duplicate session creation. |
Session Methods #
Every method below sends a step to the browser and returns the updated session envelope.
| Method | Description |
|---|---|
goto(url) | Navigate to a URL. |
observe | Get page content and interactive elements. Supports mode: "full" for all page sections. |
click | Click an element by ref, text, or label. |
type | Type text character by character. |
select | Select a dropdown option. |
check | Toggle a checkbox. |
fill | Set the value of an input field; target by ref, text, or label. |
fill_form | Fill multiple form fields at once. |
extract | Extract structured data matching a schema. |
screenshot | Take a screenshot of the page. |
scroll_collect | Scroll the page and collect content. |
scroll | Scroll the page or to an element. |
wait | Wait for time, selector, text, or JS expression. |
upload | Upload files to a file input. |
pdf | Generate a PDF of the page. |
execute_js | Run JavaScript in the browser. |
close | Close the browser session. |
goto(url) #
Navigate the browser to a new URL.
session.goto("https://example.com/about")
# Wait for a CSS selector or JS expression
session.goto("https://example.com", wait_for: ".loaded", wait_timeout: 5000)
session.goto("https://example.com", wait_until: "document.readyState === 'complete'")
observe #
Read the page content and discover interactive elements. Use scope to limit to a CSS container. Switch to HTML format when you need tag/class names. Use mode: "full" to get content from all page sections (nav, aside, footer, etc.) organized by region. The first observe auto-includes a section map showing available page regions.
result = session.observe(scope: "main", format: "markdown")
puts result.page.markdown.content
result.page.interactive_elements.each do |el|
puts "#{el.ref} — #{el.tag} — #{el.label}"
end
# Page map is auto-included on first observe
result.page.map.each do |entry|
puts "#{entry.section}: #{entry.hint}"
end
# Re-request the map on subsequent calls
session.observe(include_page_map: true)
# Get content from all page sections
full = session.observe(mode: "full", max_text_length: 20_000)
puts full.page.markdown.content
# ## [nav]
# Home · Docs · Pricing
# ## [main]
# ...article content...
# ## [aside]
# Related posts · ...
| Parameter | Type | Description |
|---|---|---|
scope |
String | CSS selector to scope observation to a page section. |
format |
String | "markdown" (default) or "html". |
mode |
String | "main" (default) returns main content only. "full" returns all page sections organized by region headers. |
include_page_map |
Boolean | Include a section map with hints. Auto-included on first observe; set true to request again. |
include_links |
Boolean | Include links in the markdown output. |
max_text_length |
Integer | Max content length in chars (default: 12000, or 20000 for mode "full"). |
click #
Click an element. Target by ref (from observe), visible text, or label.
# By ref from observe results
session.click(ref: "a7")
# By visible text
session.click(text: "Sign in")
# By label (for buttons, links, inputs)
session.click(label: "Submit")
fill #
Set the value of an input field. Target by ref, text, or label. Clears existing content first.
# By ref
session.fill("hello@example.com", ref: "input3")
# By label
session.fill("hello@example.com", label: "Email")
# By visible text
session.fill("hello@example.com", text: "Email")
fill_form #
Fill multiple form fields in a single step. Optionally submit the form.
session.fill_form([
{ label: "Email", value: "hello@example.com" },
{ label: "Password", value: "s3cret" }
], submit: true)
extract #
Extract structured data from the page. Define a schema using Ruby keyword arguments.
# Mix CSS, JS, and AI selectors in a single extraction
result = session.extract(
products: [{
"_parent": ".product-card",
"_limit": 3,
"name": "h2 >> text", # CSS selector
"price": ".price >> text", # CSS selector
"url": "a >> href", # CSS attribute
"rating": "ai >> the star rating out of 5", # AI selector
"in_stock": "js >> el.querySelector('.stock')?.textContent.includes('In stock')", # JS selector
}]
)
result.extraction["products"].each do |product|
puts "#{product['name']}: #{product['price']} (#{product['rating']})"
end
Three selector types work together in the same schema:
- CSS —
"h2 >> text","a >> href",".price >> text" - AI —
"ai >> the star rating"— resolves to a CSS selector via AI, cached after first call - JS —
"js >> expression"— runs JavaScript against each matched element
screenshot #
Capture a screenshot. The image is returned as base64 in media.
result = session.screenshot(full_page: true)
# Save to file
require "base64"
File.write("page.png", Base64.decode64(result.media.first.data))
scroll_collect #
Scroll through the page and collect all content. Useful for infinite-scroll pages.
result = session.scroll_collect(max_scrolls: 10)
puts result.page.markdown.content
close #
Close the browser session and release resources.
session.close
type #
Type text character by character. Useful for inputs with autocomplete or client-side filtering. Target by ref, text, or label.
session.type("search query", ref: "input5", delay: 50)
session.type("hello", label: "Search")
select #
Select a dropdown option by value. Target the select element by ref, text, or label.
session.select("us", label: "Country")
check #
Toggle a checkbox or radio button. Target by ref, text, or label.
session.check(label: "I agree to the terms")
session.check(ref: "cb3", checked: false)
scroll #
Scroll the page or scroll an element into view. Target by ref, text, or label.
session.scroll(to: "bottom")
session.scroll(direction: "down", amount: 500)
session.scroll(text: "Footer section")
wait #
Wait for a duration, CSS selector, visible text, or a JavaScript expression to become truthy.
session.wait(ms: 2000)
session.wait(selector: ".loaded", timeout: 10000)
session.wait(until: "document.querySelectorAll('.item').length > 5")
upload #
Upload files to a file input. Target by ref, text, or label.
session.upload(["https://example.com/file.pdf"], ref: "file1")
session.upload(["https://example.com/photo.jpg"], label: "Upload photo")
pdf #
Generate a PDF of the page. Supports format, landscape, background, scale, and margin options.
result = session.pdf(format: "A4", landscape: true, scale: 0.8,
margin: { top: "1cm", bottom: "1cm" })
require "base64"
File.write("page.pdf", Base64.decode64(result.media.first.data))
execute_js #
Run JavaScript code in the browser. The result is stored in the extraction under the specified key.
result = session.execute_js("return document.title")
puts result.extraction["js_result"]
# With custom result key and timeout
result = session.execute_js("return document.querySelectorAll('a').length",
result_key: "link_count", timeout: 5000)
Session Management #
List, inspect, and destroy sessions through the client.sessions namespace.
List sessions
result = client.sessions.list(status: "active", limit: 10)
result.sessions.each do |s|
puts "#{s.session_id} — #{s.status} — #{s.started_at}"
end
# Pagination
if result.has_more
next_page = client.sessions.list(after: result.next_cursor)
end
Get session info
info = client.sessions.get("ses_abc123")
puts info.status # "active" | "closed" | "failed"
puts info.expires_at # "2026-03-24T12:00:00Z"
if info.status == "failed"
puts info.error_code, info.error_message
end
Destroy a session
client.sessions.destroy("ses_abc123")
Error Handling #
All errors inherit from Browserbeam::Error. Each error exposes message, code, status_code, context, and request_id.
| Class | HTTP | When |
|---|---|---|
AuthenticationError | 401 | Invalid or missing API key. |
InvalidRequestError | 400 | Malformed request or invalid parameters. |
SessionNotFoundError | 404 | Session does not exist or has expired. |
RateLimitError | 429 | Too many requests. Check retry_after. |
QuotaExceededError | 429 | Credit quota exhausted. Check retry_after. |
EngineUnavailableError | 503 | Service temporarily unavailable. Check retry_after. |
StepExecutionError | varies | A step failed. Check step_index and action. |
begin
session = client.sessions.create(url: "https://example.com")
session.click(ref: "missing")
session.close
rescue Browserbeam::RateLimitError => e
puts "Rate limited. Retry after #{e.retry_after}s"
rescue Browserbeam::SessionNotFoundError => e
puts "Session gone: #{e.message}"
rescue Browserbeam::AuthenticationError => e
puts "Bad API key: #{e.message}"
rescue Browserbeam::Error => e
puts "Error [#{e.code}]: #{e.message}"
puts "Request ID: #{e.request_id}"
end
Full Example #
Extract the top stories from Hacker News, search for a topic, and save a screenshot.
require "browserbeam"
require "base64"
require "json"
client = Browserbeam::Client.new
# Navigate to Hacker News
session = client.sessions.create(url: "https://news.ycombinator.com/")
# Extract top stories from the front page
result = session.extract(
stories: [{ "_parent": ".titleline", "title": "a >> text", "url": "a >> href" }]
)
result.extraction["stories"].first(5).each do |story|
puts "#{story['title']} — #{story['url']}"
end
# Fill the search form and submit
session.fill_form({ "q" => "browser automation" }, submit: true)
# Take a screenshot of search results
shot = session.screenshot(full_page: true)
File.write("hn-search.png", Base64.decode64(shot.media.first.data))
session.close
Source code on GitHub. Found an issue? Open a ticket.