Session Replay with BQL
Session Replay in BrowserQL captures your browser sessions as interactive replays viewable in your Browserless dashboard. Using RRWeb technology, it records DOM mutations, mouse movements, clicks, scrolling, keyboard input, console logs, and network requests. This creates lightweight, high-fidelity recordings that can be played back at any speed. Not to confuse this feature with Screen Recording (record=true), which captures WebM video files, as it's only available for BaaS connections and not BQL.
This feature is particularly useful for debugging complex BQL automation workflows, creating visual documentation of GraphQL-based browser automation, and troubleshooting BQL mutations with recorded evidence.

Prerequisites
Before using Session Replay with BQL, make sure you have a Browserless account with Session Replay access (available on paid plans), a valid API key, and access to the BQL endpoint.
What Gets Recorded
Session Replay captures DOM events (all visual changes to the page), user interactions (mouse movements, clicks, scrolling, keyboard input), console logs (all console output with timestamps), and network requests (HTTP requests and responses).
Recording a Session with BQL
Connection String Configuration
To enable session recording with BQL, add the replay=true parameter to your connection URL. Recording starts automatically when you connect.
https://production-sfo.browserless.io/chromium/bql?token=YOUR_API_TOKEN_HERE&replay=true
When you connect with replay=true, recording begins immediately. There is no need to call a start mutation. The recording continues until the BQL query completes or you explicitly stop it.
Basic Recording Example
- CURL
- JavaScript
- Python
curl --request POST \
--url 'https://production-sfo.browserless.io/chrome/bql?token=YOUR_API_TOKEN_HERE&replay=true' \
--header 'Content-Type: application/json' \
--data '{
"query": "mutation replay {\n goto(url: \"https://www.browserless.io\") {\n time\n }\n click(selector: \"a\") {\n time\n }\n waitForTimeout(time: 2000) {\n time\n }\n}",
"variables": {},
"operationName": "replay"
}'
import fetch from 'node-fetch';
const API_KEY = "YOUR_API_TOKEN_HERE";
const BQL_ENDPOINT = `https://production-sfo.browserless.io/chromium/bql?replay=true&token=${API_KEY}`;
const recordingQuery = `
mutation replay {
goto(url: "https://www.browserless.io") {
time
}
click(selector:"a"){
time
}
waitForTimeout(time:2000){
time
}
}`;
const recordSession = async () => {
const response = await fetch(BQL_ENDPOINT, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ query: recordingQuery,
"operationName":"replay" })
});
const result = await response.json();
console.log('Recording Result:', JSON.stringify(result, null, 2));
// Session recording automatically stops when the BQL query completes
console.log('Session recorded successfully');
};
recordSession().catch(console.error);
import requests
import json
API_KEY = "YOUR_API_TOKEN_HERE"
BQL_ENDPOINT = f"https://production-sfo.browserless.io/chromium/bql?replay=true&token={API_KEY}"
recording_query = """
mutation RecordSession {
goto(url: "https://www.example.com") {
status
time
}
click(selector: "button.example") {
time
x
y
}
type(selector: "input[name='search']", text: "browserless") {
time
}
waitForSelector(selector: ".search-results") {
time
}
}
"""
def record_session():
response = requests.post(
BQL_ENDPOINT,
headers={
'Content-Type': 'application/json'
},
json={'query': recording_query}
)
result = response.json()
print('Recording Result:', json.dumps(result, indent=2))
# Session recording automatically stops when the BQL query completes
print('Session recorded successfully')
if __name__ == "__main__":
record_session()
Stopping Recording Manually
For long-running sessions or when you need precise control over when recording stops, use the stopSessionRecording mutation. This mutation stops the recording and uploads the captured data to your dashboard.
- CURL
- JavaScript
curl --request POST \
--url 'https://production-sfo.browserless.io/chrome/bql?token=YOUR_API_TOKEN_HERE&replay=true' \
--header 'Content-Type: application/json' \
--data '{
"query": "mutation StopRecording {\n goto(url: \"https://www.browserless.io\") {\n status\n time\n }\n waitForTimeout(time: 5000) {\n time\n }\n stopSessionRecording {\n success\n error\n }\n}",
"variables": {},
"operationName": "StopRecording"
}'
import fetch from 'node-fetch';
const API_KEY = "YOUR_API_TOKEN_HERE";
const BQL_ENDPOINT = `https://production-sfo.browserless.io/chromium/bql?replay=true&token=${API_KEY}`;
const recordingQuery = `
mutation StopRecording {
goto(url: "https://www.browserless.io") {
status
time
}
waitForTimeout(time: 5000) {
time
}
stopSessionRecording {
success
error
}
}`;
const recordSession = async () => {
const response = await fetch(BQL_ENDPOINT, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
query: recordingQuery,
operationName: "StopRecording"
})
});
const result = await response.json();
console.log('Recording Result:', JSON.stringify(result, null, 2));
if (result.data?.stopSessionRecording?.success) {
console.log('Session recording stopped and uploaded successfully');
}
};
recordSession().catch(console.error);
The stopSessionRecording mutation returns a response with success (boolean indicating if the recording was stopped successfully) and error (error message if the operation failed, null otherwise).
Viewing Replays
After your BQL query completes (or after calling stopSessionRecording), the replay is automatically uploaded and available in your Browserless dashboard. Navigate to the Session Replay section to find your recorded sessions. Each replay includes a timeline showing all captured events, console logs, and network activity.
Navigation Handling
Session Replay automatically handles page navigations within your BQL session. When mutations like goto navigate to a new page, the recording system reinjects the RRWeb recorder and continues capturing events. All data from multiple page navigations is aggregated into a single replay.
Limitations
Session Replay has some limitations to be aware of. Cross-origin iframes may not be fully captured due to browser security restrictions. Very long sessions with high DOM activity may result in large replay files. Some dynamic content loaded via WebGL or canvas may not be perfectly reproduced in playback.