You know the drill. You need to scrape some data, automate a workflow, or test that cursed SPA that breaks every other week. You fire up Playwright, spend 2 hours configuring Docker, another hour debugging why your bot got blocked, and then realize you need to handle CAPTCHAs. What if we just... didn't? Steel's Python SDK is browser automation for people who want to ship, not spend their weekend becoming a DevOps expert.

Installation (The Easy Part)

That's it. No Docker, no browser downloads, no "works on my machine" negative energy.

Your First Steel Session

Here's how you create a cloud browser session in just a few lines:

from steel import Steel
import os

# Initialize the client
api_key = os.getenv("STEEL_API_KEY")

client = Steel(steel_api_key=api_key)

try:
    # Create a browser session with anti-bot protection
    session = client.sessions.create()

    print(f"Session ID: {session.id}")
    print(f"Watch live at: {session.session_viewer_url}")
except Exception as e:
    print(f"Error creating session: {e}")

No cap - that's a full browser running in the cloud with anti-bot protection enabled. You can literally watch it live in your browser.

Connect Your Favorite Tools

Steel provides WebSocket URLs that work with browser automation tools. Here's how to get the connection details:

import asyncio
import os
from steel import Steel

async def get_browser_connection():
    api_key = os.getenv("STEEL_API_KEY")
    
    try:
        # Initialize Steel client
        client = Steel(steel_api_key=api_key)
        
        # Create a browser session
        session = client.sessions.create()
        
        print(f"Session ID: {session.id}")
        print(f"WebSocket URL: {session.websocket_url}")
        
        # Note: To connect Puppeteer (Node.js), use:
        print(f"\nFor Puppeteer connection:")
        print(f"browserWSEndpoint: '{session.websocket_url}'")
        
        # Clean up
        client.sessions.release(session.id)
        
    except Exception as e:
        print(f"Error: {e}")

# Run the async function
asyncio.run(get_browser_connection())

Same API you know, but now it's running on infrastructure that doesn't hate you.

Real-World Examples That Actually Work

Price Monitoring (Because Everything's Expensive)

import asyncio
import os
from steel import Steel

async def check_price(product_url):
    api_key = os.getenv("STEEL_API_KEY")
    
    try:
        # Initialize Steel client
        client = Steel(steel_api_key=api_key)
        
        # Create session with all the good stuff enabled
        # Note: use_proxy and solve_captcha require paid plan
        session = client.sessions.create(
            # use_proxy=True,      # Requires paid plan
            # solve_captcha=True   # Requires paid plan
        )
        
        print(f"Session created: {session.id}")
        print(f"WebSocket URL for browser connection: {session.websocket_url}")
        
        # Note: To implement full price monitoring, you would:
        # 1. Connect your browser automation tool to session.websocket_url
        # 2. Navigate to product_url
        # 3. Wait for price element
        # 4. Extract price data
        
        price_data = {
            "session_id": session.id,
            "product_url": product_url,
            "websocket_url": session.websocket_url,
            "status": "session_ready_for_browser_connection"
        }
        
        # Clean up
        client.sessions.release(session.id)
        
        return price_data
        
    except Exception as e:
        print(f"Error in price monitoring: {e}")
        return None

# Example usage
async def main():
    result = await check_price("https://example.com/product")
    print("Price monitoring result:", result)

asyncio.run(main())

Form Automation (For When You're Too Lazy to Click)

import asyncio
import os
from steel import Steel

async def fill_application_form(form_url, user_data):
    api_key = os.getenv("STEEL_API_KEY")
    
    try:
        # Initialize Steel client
        client = Steel(steel_api_key=api_key)
        
        # Extended timeout because forms are complicated
        # Note: use_proxy and solve_captcha require paid plan
        session = client.sessions.create(
            # use_proxy=True,      # Requires paid plan
            # solve_captcha=True,  # Requires paid plan
            timeout=3600000  # 1 hour, because government websites
        )
        
        print(f"Session created: {session.id}")
        print(f"WebSocket URL: {session.websocket_url}")
        print(f"Form URL to automate: {form_url}")
        print(f"User data: {user_data}")
        
        # Note: With a browser automation library connected, you would:
        # 1. Connect to session.websocket_url
        # 2. Navigate to form_url
        # 3. Fill form fields with user_data
        # 4. Submit form and handle navigation
        
        form_automation_plan = {
            "session_id": session.id,
            "websocket_url": session.websocket_url,
            "form_url": form_url,
            "automation_steps": [
                f"Fill firstName with: {user_data.get('first_name', 'N/A')}",
                f"Fill lastName with: {user_data.get('last_name', 'N/A')}",
                f"Fill email with: {user_data.get('email', 'N/A')}",
                f"Select country: {user_data.get('country', 'N/A')}",
                "Submit form and wait for step-2"
            ]
        }
        
        # Clean up
        client.sessions.release(session.id)
        
        return form_automation_plan
        
    except Exception as e:
        print(f"Error in form automation: {e}")
        return None

# Example usage
async def main():
    sample_user_data = {
        'first_name': 'John',
        'last_name': 'Doe',
        'email': 'john.doe@example.com',
        'country': 'US'
    }
    
    result = await fill_application_form("https://example.com/application-form", sample_user_data)
    print("Form automation result:", result)

asyncio.run(main())

The Secret Sauce: Built-in Anti-Bot Protection

Here's what you get with paid plans:

  • Residential proxy rotation - No more IP bans

  • Automatic CAPTCHA solving - For common types (sorry, no magic)

  • Browser fingerprint randomization - Look like a different human every time

  • Human-like behavior simulation - Mouse movements that don't scream "I'M A BOT"

All enabled with one flag: use_proxy=True, solve_captcha=True

Note: These features require upgrading from the hobby (free) plan. The hobby plan still gives you cloud browsers with basic anti-detection.

Session Management That Makes Sense

Sessions stick around for up to 24 hours and maintain state across requests:

try:
    # Create a session
    session = client.sessions.create(
        timeout=86400000  # 24 hours in milliseconds
    )

    print(f"Session created: {session.id}")
    print(f"Session URL: {session.session_viewer_url}")

    # Do some work
    # ... browser automation happens here ...
    print("Browser automation would happen here...")

    # Come back later and the session is still there
    session_details = client.sessions.retrieve(session.id)
    print(f"Status: {session_details.status}")

    # Clean up when done
    client.sessions.release(session.id)
    print("Session released successfully")

except Exception as e:
    print(f"Error in session management: {e}")

Pro tip: Use the session viewer URL to watch your automation live. It's surprisingly satisfying.

Error Handling (Because Things Break)

import asyncio
import time
import random
import os
from steel import Steel

async def scrape_the_thing(session, url):
    """Placeholder function for actual scraping logic."""
    print(f"Scraping {url} using session {session.id}")
    
    # Simulate some work and potential failure
    if random.random() < 0.3:  # 30% chance of failure for testing
        raise Exception("Simulated scraping failure")
    
    return {"url": url, "data": "scraped_data", "session_id": session.id}

async def robust_automation(url, max_retries=3):
    api_key = os.getenv("STEEL_API_KEY")
    
    if not api_key:
        raise Exception("STEEL_API_KEY environment variable is not set")
    
    client = Steel(steel_api_key=api_key)
    
    for attempt in range(max_retries):
        session = None
        try:
            # Create session
            session = client.sessions.create()
            
            print(f"Attempt {attempt + 1}: Created session {session.id}")
            
            # Your automation logic here
            result = await scrape_the_thing(session, url)
            
            # Clean up
            client.sessions.release(session.id)
            print(f"Success on attempt {attempt + 1}")
            return result
            
        except Exception as error:
            print(f"Attempt {attempt + 1} failed: {error}")
            
            # Clean up session if it was created
            if session:
                try:
                    client.sessions.release(session.id)
                except:
                    pass  # Ignore cleanup errors
            
            if attempt < max_retries - 1:
                # Exponential backoff with jitter (because we're not animals)
                delay = (2 ** attempt) + random.uniform(0, 1)
                print(f"Waiting {delay:.2f} seconds before retry...")
                await asyncio.sleep(delay)  # Use asyncio.sleep in async functions
    
    raise Exception(f"Failed after {max_retries} attempts")

# Example usage
async def main():
    try:
        result = await robust_automation("https://example.com", max_retries=3)
        print("Final result:", result)
    except Exception as e:
        print(f"Robust automation failed: {e}")

asyncio.run(main())

File Management (Upload/Download Vibes)

Need to handle files? We got you:

# Upload a file to your session
file_upload = client.sessions.files.upload(
    session_id=session.id,
    file=open('document.pdf', 'rb'),
    path='uploads/document.pdf'
)

# Download files from your session
file_data = client.sessions.files.download(
    session_id=session.id,
    path='downloads/result.pdf'
)

# Download everything as a zip
archive = client.sessions.files.download_archive(session_id=session.id)

Quick Start for the Impatient

from steel import Steel
import os

# Set your API key (get one at steel.dev)
client = Steel(steel_api_key=os.getenv("STEEL_API_KEY"))

# Create session with the works
session = client.sessions.create(
    use_proxy=True,
    solve_captcha=True,
    block_ads=True  # Because ads are annoying
)

print(f"✨ Session created: {session.id}")
print(f"🔗 Watch live: {session.session_viewer_url}")
print(f"🌐 WebSocket: {session.websocket_url}")

# Connect your favorite automation tool and go wild

Next Steps (Ship Something Cool)

Ready to build? Check out:

Free tier gives you 100 browser hours per month - perfect for testing and small projects.

The Bottom Line

Steel's Python SDK removes the infrastructure headaches so you can focus on building cool stuff. No Docker containers to manage, no proxy services to configure, no CAPTCHAs to solve manually.

Just pip install steel-sdk and start automating.

Questions? Hit us up in Discord or check the docs. We're here to help you ship faster.

Ready to try it? Get your free API key and let's see what you build. 🚀

Jul 20, 2025

Jul 20, 2025

Jul 20, 2025

Steel Python SDK: Browser Automation That Actually Works

Steel Python SDK: Browser Automation That Actually Works

Steel Python SDK: Browser Automation That Actually Works

Tired of wrestling with Selenium and Playwright infrastructure? Steel's Python SDK gives you cloud browsers in 3 lines of code. No Docker, no proxies, no pain.

Ready to

Build with Steel?

Ready to

Build with Steel?

Ready to

Build with Steel?

Ready to Build with Steel?

A better way to take your LLMs online.

© Steel · Inc. 2024.

All Systems Operational

Platform

Join the community