Form Submission
The type, click, select, checkbox, and solve mutations let you fill, submit, and bypass CAPTCHAs in forms. BrowserQL handles element waiting, scrolling, and human-like input timing automatically.
Basic Usage
The example below navigates to a form, fills two fields, selects a dropdown option, solves a CAPTCHA, and then clicks submit. For explicit control over timing, see Waiting for Things.
- BQL Query
- cURL
- Javascript
- Python
- Java
- C#
mutation FormExample {
goto(url: "https://www.browserless.io/practice-form") {
status
}
typeEmail: type(text: "john@email.com", selector: "#Email") {
time
}
typeMessage: type(
selector: "#Message"
text: "Watching BrowserQL do cool things!"
) {
time
}
subject: select(selector: "select#Subject", value: "Support") {
selector
}
solve {
time
solved
}
submitForm: click(selector: "button[type='submit']") {
time
}
}
curl --request POST \
--url 'https://production-sfo.browserless.io/chromium/bql?token=YOUR_API_TOKEN_HERE' \
--header 'Content-Type: application/json' \
--data '{"query":"mutation FormExample {\n goto(url: \"https://www.browserless.io/practice-form\") {\n status\n }\n\n typeEmail: type(text: \"john@email.com\", selector: \"#Email\") {\n time\n }\n\n typeMessage: type(\n selector: \"#Message\"\n text: \"Watching BrowserQL do cool things!\"\n ) {\n time\n }\n\n subject: select(selector: \"select#Subject\", value: \"Support\") {\n selector\n }\n\n solve {\n time\n solved\n }\n}","variables":{},"operationName":"FormExample"}'
const endpoint = "https://production-sfo.browserless.io/chromium/bql";
const token = "YOUR_API_TOKEN_HERE";
const options = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
query: `mutation FormExample {
goto(url: "https://www.browserless.io/practice-form") {
status
}
typeEmail: type(text: "john@email.com", selector: "#Email") {
time
}
typeMessage: type(
selector: "#Message"
text: "Watching BrowserQL do cool things!"
) {
time
}
subject: select(selector: "select#Subject", value: "Support") {
selector
}
solve {
time
solved
}
submitForm: click(selector: "button[type='submit']") {
time
}
}`,
variables: "",
operationName: "FormExample",
})
};
const url = `${endpoint}?token=${token}`;
const response = await fetch(url, options);
const data = await response.json();
console.log(data);
import requests
endpoint = "https://production-sfo.browserless.io/chromium/bql"
query_string = {
"token": "YOUR_API_TOKEN_HERE",
}
headers = {
"Content-Type": "application/json",
}
payload = {
"query": """mutation FormExample {
goto(url: "https://www.browserless.io/practice-form") {
status
}
typeEmail: type(text: "john@email.com", selector: "#Email") {
time
}
typeMessage: type(
selector: "#Message"
text: "Watching BrowserQL do cool things!"
) {
time
}
subject: select(selector: "select#Subject", value: "Support") {
selector
}
solve {
time
solved
}
submitForm: click(selector: "button[type='submit']") {
time
}
}""",
"variables": {},
"operationName": "FormExample",
}
response = requests.post(endpoint, params=query_string, headers=headers, json=payload)
print(response.json())
String url = "https://production-sfo.browserless.io/chromium/bql";
String token = "YOUR_API_TOKEN_HERE";
String endpoint = String.format("%s?token=%s", url, token);
HttpResponse<String> response = Unirest.post(endpoint)
.header("Content-Type", "application/json")
.body("{\"query\":\"mutation FormExample {\\n goto(url: \\\"https://www.browserless.io/practice-form\\\") {\\n status\\n }\\n\\n typeEmail: type(text: \\\"john@email.com\\\", selector: \\\"#Email\\\") {\\n time\\n }\\n\\n typeMessage: type(\\n selector: \\\"#Message\\\"\\n text: \\\"Watching BrowserQL do cool things!\\\"\\n ) {\\n time\\n }\\n\\n subject: select(selector: \\\"select#Subject\\\", value: \\\"Support\\\") {\\n selector\\n }\\n\\n solve {\\n time\\n solved\\n }\\n\\n submitForm: click(selector: \\\"button[type='submit']\\\") {\\n time\\n }\\n}\\",\"variables\":\"\",\"operationName\":\"FormExample\"}")
.asString();
string url = "https://production-sfo.browserless.io/chromium/bql";
string token = "YOUR_API_TOKEN_HERE";
string endpoint = $"{url}?token={token}";
var payload = new
{
query = @"mutation FormExample {
goto(url: ""https://www.browserless.io/practice-form"") {
status
}
typeEmail: type(text: ""john@email.com"", selector: ""#Email"") {
time
}
typeMessage: type(
selector: ""#Message""
text: ""Watching BrowserQL do cool things!""
) {
time
}
subject: select(selector: ""select#Subject"", value: ""Support"") {
selector
}
solve {
time
solved
}
submitForm: click(selector: ""button[type='submit']"") {
time
}
}",
variables = "",
operationName = "FormExample"
};
using (HttpClient httpClient = new HttpClient())
{
var jsonPayload = System.Text.Json.JsonSerializer.Serialize(payload);
var content = new StringContent(jsonPayload, Encoding.UTF8, "application/json");
var response = await httpClient.PostAsync(endpoint, content);
string responseBody = await response.Content.ReadAsStringAsync();
Console.WriteLine(responseBody);
}
Deep Selectors for Iframes and Shadow DOM
Deep selectors target elements inside iframes or shadow DOMs. Add < at the start of any selector to enable deep traversal.
selector: "< [optional URL pattern] css-selector"
<signals a deep selector.- URL pattern (optional) matches the iframe source URL with glob syntax.
- CSS selector identifies the target element inside the matched frame.
- Iframe by URL glob
- Iframe by path
- Any iframe
- Exact iframe URL
Target a button inside any iframe from cnn.com:
selector: "< *cnn.com* button#submit"
Click an anchor inside a Google verification iframe:
selector: "< *google.com/api/verify* a[class*='now']"
Find an element with a price attribute across all iframes and the top-level page:
selector: "< [price]"
Find a span inside an iframe at an exact URL:
selector: "< https://amazon.com/ span.submit.order"
- Parent-child combinators (e.g.,
div > span) are not supported in deep selectors. - Each deep selector must target a single node.
Humanized Typing and Clicking
BrowserQL types with randomized delays between keystrokes to mimic human behavior. Two ways to control this:
Query parameter: Add humanlike=true to the connection URL to enable human-like behavior globally for the session.
Per-mutation delay: Set a delay range on the type mutation to control keystroke timing explicitly.
mutation HumanizedForm {
goto(url: "https://www.browserless.io/practice-form") {
status
}
# Randomize keystroke delay between 80ms and 200ms
typeMessage: type(
selector: "#Message"
text: "Hello, this looks human!"
delay: [80, 200]
) {
time
}
}
For session-level configuration, see Session Settings.
Keyboard Events
BrowserQL does not have a dedicated mutation for sending keyboard events like Enter, Tab, or arrow keys. The recommended approach for form submission is to use click on the submit button (as shown in the examples above).
If you need to simulate keyboard events, you can use the evaluate mutation to dispatch KeyboardEvent objects:
Events created via evaluate have isTrusted: false, and some sites may ignore untrusted events for security reasons.
mutation KeyboardEnter {
goto(url: "https://example.com/form") {
status
}
type(text: "search query", selector: "input[name='q']") {
time
}
pressEnter: evaluate(content: """
const enterEvent = new KeyboardEvent('keydown', {
key: 'Enter',
code: 'Enter',
keyCode: 13,
which: 13,
bubbles: true,
cancelable: true
});
document.activeElement.dispatchEvent(enterEvent);
""") {
value
}
}