For AI agents: a documentation index is available at /llms.txt
Skip to main content

BQL Best Practices

We recommend you use the following best practices to make the most out of BQL. This guide covers essential patterns that will make your browser automation more reliable and maintainable. If you're new to BrowserQL, start with the Writing BrowserQL guide first.

Prefer Variables Over Hardcoded Values

Use GraphQL variables to keep your BQL operations reusable and easy to maintain. Variables let you parameterize values like URLs, selectors, and input data, so you can avoid hardcoding and repeating them across queries.

# Avoiding concatenating strings by using GraphQL variables
mutation LoginUser($email: String!, $password: String!, $loginUrl: String!) {
goto(url: $loginUrl, waitUntil: firstContentfulPaint) {
status
}

typeEmail: type(
selector: "input[type='email']"
text: $email
) {
time
}

typePassword: type(
selector: "input[type='password']"
text: $password
) {
time
}

submitLogin: click(selector: "button[type='submit']") {
x
y
}
}

Then, send a variables object in your JSON payload read more about them here

{
"email": "user@example.com",
"password": "securepassword123",
"loginUrl": "https://example.com/login"
}

Prefer Semantic Selectors Over Flaky CSS Classes

Prefer semantic selectors for more reliable automations. Use stable HTML attributes, roles, or other meaningful selectors that are less likely to change, and avoid relying on CSS classes that can break with UI updates.

mutation AddProductToCart($url: String!) {
goto(url: $url, waitUntil: networkIdle) {
status
}
# Use HTML attributes for more reliable scraping
click(selector: "[data-testid='add-to-cart-button']") {
selector
}
# Use HTML roles instead of classes for waiting for a particular selector
waitForSelector(
selector: "[role='status'][aria-label='cart-item-count']"
visible: true
) {
selector
}
html(visible: true) {
html
}
}

Reject Unnecessary Resources

Block unnecessary resources to improve performance. Use the reject mutation to prevent loading ads, images, videos, and other non-essential requests, reducing bandwidth usage and speeding up execution. It's important to keep in mind that reducing bandwidth usage may reduce your proxy cost if they are enabled, however they could also trigger bot detection on some sites.

# Make sure to enable the Adblock parameter in the editor!
mutation Search($url: String!, $query: String!) {
# Reject images, styles and media right away
reject(
type: [image, stylesheet, media]
) {
time
}
goto(url: $url, waitUntil: networkIdle) {
status
}
type(text: $query, selector: "input[name='q']") {
selector
}
click(selector: "button[type='submit']") {
selector
}
waitForNavigation {
status
}
html(visible: true) {
html
}
}

Summary

The tl; dr is:

  • Use variables instead of hardcoded values
  • Use semantic selectors instead of CSS classes
  • Block unnecessary resources to improve performance

Frequently Asked Questions

What are the most important BQL best practices?

Use specific selectors, add appropriate wait conditions, handle dynamic content with waitForSelector or waitForTimeout, and use the stealth route for protected sites. Always test queries in the IDE before deploying.

How do I handle pages that load content dynamically?

Use wait mutations like waitForSelector, waitForTimeout, or waitForNetwork to pause execution until the content you need is present. BQL also supports response interception to capture API data directly.

Should I use the stealth route for all queries?

The stealth route adds overhead for fingerprint randomization and entropy injection. Use the standard /bql route for sites without bot detection, and switch to /stealth/bql only when you encounter blocks or CAPTCHAs. See the BQL API reference for the full list of available operations.