Browserbase is designed to be easy for developers to use. That said, we recognize that it may not always be obvious to those unfamiliar with the headless browser space how to ensure your app uses our platform most efficiently. We’ve assembled these recommendations for optimizing your Browserbase app for performance:

  • Avoid using multi tab browser sessions. We support browser sessions with multiple tabs, but depending on the workload, you will likely begin to notice worse performance with more tabs. We don’t recommend this approach. You’ll get better performance by using multiple single tab browser sessions instead.

  • Reuse your sessions. When connecting to the WebSocket endpoint wss://connect.browserbase.com using the Connect API, the default behavior is to create a new browser session implicitly before connecting to it. Creating it naturally has a performance cost.

    Instead, if your use case allows for it, you can reuse an existing session. When connecting, use the sessionId query parameter to pass in the session ID for the existing session. This eliminates the session create cost and improves performance.

  • Disable proxies for your session. Using proxies unavoidably adds to request latency. Don’t use them if you don’t need to. You can learn more about using proxies here.

You may be able to improve proxy latency by using contexts to share browser cache. Note that this will also copy cookies across browser sessions.

  • Disable CSS, images, JavaScript, and other unwanted assets. If you don’t need it, don’t request it. Optimizing this should speed up your page downloads.

    Here’s a Playwright example in JavaScript showing how to intercept the request to block image, CSS, and JavaScript requests:

Node.js
  // Enable request interception
  await page.route('**/*', route => {
    if (
      route.request().resourceType() === 'image' ||
      route.request().resourceType() === 'stylesheet' ||
      route.request().resourceType() === 'script'
    ) {
      route.abort();
    } else {
      route.continue();
    }
  });
  • Optimize your login flows. You can use your web automation framework to save cookies to a JSON file. Then, on your next run, you can load the cookies into the browser and force a “logged in” session state, skipping the authentication process. To learn more, check out the “Speed up your automation by reusing cookies” section in our authentication guide.

  • Consider round trip time (RTT). It’s common for Browserbase customers to prototype their app initially using a headless browser locally. In this case, the round trip time—the round trip latency of the request minus execution time—is essentially instantaneous, producing a high perceived speed for your app that may not reflect real-world conditions. (This is especially true when factoring in nonlocal capabilities like captcha solving or proxies.)

    When using Browserbase to make a request, the request has to go out over the network, get processed, and return. Under normal circumstances that will have far higher RTT than localhost.

    Also, keep in mind that each interaction with the page—even ones that seem atomic—can result in several underlying CDP commands. The RTT can stack up. And if your app implements requests serially, the total RTT for all requests increases monotonically. The performance consequences of this approach are probably imperceptible locally, but in real world conditions with substantial RTT, you may see a severe performance hit.

    Which leads to the next point:

  • Implement parallel processing. For that reason, when using Browserbase to run jobs, we strongly recommend parallelizing your job processing. If you process the work concurrently, tasks can run in parallel, and depending on your use case, it’s possible to realize order of magnitude gains in perceived app performance.

    Both Node.js and Python have good support for parallel processing. Here’s a Python example to illustrate:

Python
    # BAD WAY -- processes elements sequentially
    # for an_html_element in html_elements:
    #     await get_element_text(an_html_element)

    # GOOD WAY -- processes elements in parallel
    await asyncio.gather(
        *(get_element_text(an_html_element) for an_html_element
        in html_elements)
    )
  • Parallelize session creation with app initialization. Similarly, if your app has setup steps to do prior to processing work, you can parallelize session creation with that work. During initialization, use the Sessions API. This effectively eliminates some or all of the perceived clock time for creating the new session.

  • Local regional deployment. Locate your app near us geographically. With RTT-sensitive apps such as remote browser automation, the physical distance between systems becomes a major performance factor. Our testing with common use cases shows that region localizing may produce an 8-9x gain in performance without any code changes.

    Our productions systems are currently located in Oregon (us-west-2). In order to minimize RTT, we advise you to run your code as physically close to our browser infrastructure as possible.

  • Choose the right runtime environment. If performance is a primary factor for your app, try comparison performance benchmarking across implementation languages and runtimes. For example, you may discover through testing your app that Node.js outperforms Python in raw speed for I/O bound requests, whereas Python may be a better choice for CPU-bound tasks.

  • Choose the right web automation framework. When making your choice, consider evaluating its performance for your app. Some commonly used frameworks were originally designed for low-throughput use cases like automated webapp testing and may not be able to scale effectively and provide the performance your app needs.

    • Selenium uses a series of HTTP calls to connect to Browserbase (instead of a WebSocket) which impacts performance. Playwright and Puppeteer both use WebSockets, so switching to another framework may be a better option for you.

    • There are many opportunities for optimizing your web automation code. For example, in Playwright, the Keyboard.type method simulates physical keyboard input, typing characters one by one. Thus it sends multiple events over the wire which can slow some operations down, such as entering text into input fields. (This comes back to considering RTT.) So where possible, we recommend using locator.fill() instead, which sets the value of an input field directly without simulating keystrokes. There are analogous optimization strategies for each framework.

    • If performance is of critical concern, you may want to consider alternative frameworks such as chromedp. This Go framework is lower level than other frameworks and has a steeper learning curve, but the performance gains may be worth it.

  • Follow your framework’s best practices. Check out these guides for Playwright, Puppeteer, and Selenium. Each guide includes tips on performance optimization.

Still having performance issues? We may be able to help! Contact us at [email protected].