> ## Documentation Index
> Fetch the complete documentation index at: https://docs.browserbase.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Automating form submissions

> Automate form submissions at scale with Browserbase. Handle logins, registrations, data entry, and checkouts using Stagehand or Playwright in reliable cloud browsers.

Automate form submissions using cloud browsers that handle dynamic content, authentication, and bot protection. Browserbase gives you reliable infrastructure for form filling workflows, whether you're using Stagehand or Playwright.

* Scale form automation across concurrent sessions without managing infrastructure
* Persist login state across form workflows with [browser contexts](/platform/browser/core-features/contexts)
* Browse protected sites with [Verified](/platform/identity/overview)
* Debug failed submissions with [session recordings](/platform/browser/observability/session-recording) and [live views](/platform/browser/observability/session-live-view)

<Info>
  **Looking to deploy form automation as an API?** [Functions](/platform/runtime/overview) let you package form automation workflows as cloud functions that can be invoked via webhook or API call—perfect for integrating form submissions into your applications.
</Info>

## Implementation

<Steps>
  <Step title="Create a session">
    Create a Browserbase session and [authenticate](/platform/identity/authentication) if needed. Use [browser contexts](/platform/browser/core-features/contexts) to persist authentication across pages.
  </Step>

  <Step title="Navigate to the form">
    Go to the target page and wait for form elements to fully load before interacting with them.
  </Step>

  <Step title="Fill form fields">
    Identify and populate form elements (text inputs, dropdowns, radio buttons, and checkboxes) with your data.
  </Step>

  <Step title="Submit and verify">
    Trigger the submit button and check for success messages or validation errors.
  </Step>
</Steps>

## Template

Get started quickly with a ready-to-use form filling template.

<Card title="Form Filling" icon="rocket" href="https://www.browserbase.com/templates/form-filling">
  Clone, configure, and run in minutes
</Card>

## Example: Automating a Google form

To demonstrate how to automate form submissions using Browserbase, you can use a sample Google Form designed specifically for this tutorial: [Google Form](https://forms.gle/f4yNQqZKBFCbCr6j7)

This form collects responses in various formats:

* Text input
* Radio button
* Checkboxes

### Code example

<Tabs>
  <Tab title="Node.js">
    <CodeGroup>
      ```typescript Stagehand theme={null}
      import { Stagehand } from "@browserbasehq/stagehand";
      import { z } from "zod";
      import dotenv from "dotenv";

      dotenv.config();

      async function main() {
      	const stagehand = new Stagehand({
      		env: "BROWSERBASE",
              verbose: 0,
      	});

      	await stagehand.init();
      	const page = stagehand.context.pages()[0];

      	async function fillForm(inputs: any) {
      		// Navigate to the form
      		await page.goto("https://forms.gle/f4yNQqZKBFCbCr6j7");

      		// Select the superpower radio button
      		await stagehand.act(`Select the superpower: ${inputs.superpower}`);

      		// Select the features used checkboxes
      		await stagehand.act("Select the features used: " + inputs.features_used.join(", "));

      		// Fill in the coolest build text field
      		await stagehand.act("Fill in the coolest_build field with the following value: " + inputs.coolest_build);

      		// Submit the form
      		await stagehand.act("Click the submit button");
      		await page.waitForTimeout(5000);

      		// Extract to log the status of the form
      		const status = await stagehand.extract({instruction: "Extract the status of the form", schema: z.object({status: z.string()})});
      		console.log(status);

      		await stagehand.close();
      	}

      	const inputs = {
      		"superpower": "Invisibility",
      		"features_used": [
      			"Verified",
      			"Proxies",
      			"Session Replay"
      		],
      		"coolest_build": "A bot that automates form submissions across multiple sites.",
      	}
      	
      	await fillForm(inputs);
      }

      main().catch(console.error);
      ```

      ```typescript Playwright theme={null}
      import { chromium } from "playwright-core";
      import Browserbase from "@browserbasehq/sdk";
      import { config } from "dotenv";
      config();

      async function createSession() {
          const bb = new Browserbase({ apiKey: process.env.BROWSERBASE_API_KEY! });
          const session = await bb.sessions.create({
              // Add configuration options here
            });
          return session;
      }

      async function fillForm(inputs: any) {
          const session = await createSession()
          const browser = await chromium.connectOverCDP(session.connectUrl);

          // Getting the default context to ensure the sessions are recorded.
          const defaultContext = browser.contexts()[0];
          const page = defaultContext?.pages()[0];

          console.log(`View session recording at https://browserbase.com/sessions/${session.id}`,);
          // Navigate to page
          await page.goto("https://forms.gle/f4yNQqZKBFCbCr6j7");

          // fill superpower
          await page.locator(`[role="radio"][data-value="${inputs.superpower}"]`).click();
          await page.waitForTimeout(1000);

          // fill features_used
          for (const feature of inputs.features_used) {
              await page.locator(`[role="checkbox"][aria-label="${feature}"]`).click();
          }
          await page.waitForTimeout(1000);

          // fill coolest_build
          await page.locator('input[jsname="YPqjbf"]').fill(inputs.coolest_build);
          await page.waitForTimeout(1000);

          // click submit button
          await page.locator('div[role="button"]:has-text("Submit")').click();

          // wait 10 seconds
          await page.waitForTimeout(10000);

          console.log("Shutting down...");
          await page.close();
          await browser.close();
      }

      const inputs = {
          "superpower": "Invisibility",
          "features_used": [
              "Verified",
              "Proxies",
              "Session Replay"
          ],
          "coolest_build": "A bot that automates form submissions across multiple sites.",
      }
      fillForm(inputs);
      ```
    </CodeGroup>
  </Tab>

  <Tab title="Python">
    <CodeGroup>
      ```python Stagehand theme={null}
      import os
      import asyncio
      from stagehand import AsyncStagehand
      from dotenv import load_dotenv

      load_dotenv()

      async def main():
          client = AsyncStagehand(
              browserbase_api_key=os.environ["BROWSERBASE_API_KEY"],
              model_api_key=os.environ["MODEL_API_KEY"],
          )
          session = await client.sessions.create(model_name="google/gemini-2.5-flash")

          async def fill_form(inputs):
              # Navigate to the form
              await session.navigate(url="https://forms.gle/f4yNQqZKBFCbCr6j7")

              # Select the superpower radio button
              await session.act(input=f"Select the superpower: {inputs['superpower']}")

              # Select the features used checkboxes
              await session.act(input="Select the features used: " + ", ".join(inputs["features_used"]))

              # Fill in the coolest build text field
              await session.act(input="Fill in the coolest_build field with the following value: " + inputs["coolest_build"])

              # Submit the form
              await session.act(input="Click the submit button")

          await fill_form({
              "superpower": "Invisibility",
              "features_used": [
                  "Verified",
                  "Proxies",
                  "Session Replay"
              ],
              "coolest_build": "A bot that automates form submissions across multiple sites.",
          })

          await session.end()

      if __name__ == "__main__":
          asyncio.run(main())
      ```

      ```python Playwright theme={null}
      import os
      from playwright.sync_api import sync_playwright
      from browserbase import Browserbase
      from dotenv import load_dotenv

      load_dotenv()

      def create_session():
          """Creates a Browserbase session."""
          bb = Browserbase(api_key=os.environ["BROWSERBASE_API_KEY"])
          session = bb.sessions.create(
              # Add configuration options here if needed
          )
          return session

      def fill_form(inputs):
          """Automates form filling using Playwright with Browserbase."""
          session = create_session()
          print(f"View session recording at https://browserbase.com/sessions/{session.id}")

          with sync_playwright() as p:
              browser = p.chromium.connect_over_cdp(session.connect_url)

              # Get the default browser context and page
              context = browser.contexts[0]
              page = context.pages[0]

              # Navigate to the form page
              page.goto("https://forms.gle/f4yNQqZKBFCbCr6j7")

              # Select superpower
              page.locator(f'[role="radio"][data-value="{inputs["superpower"]}"]').click()
              page.wait_for_timeout(1000)

              # Select features used
              for feature in inputs["features_used"]:
                  page.locator(f'[role="checkbox"][aria-label="{feature}"]').click()
              page.wait_for_timeout(1000)

              # Fill in coolest build
              page.locator('input[jsname="YPqjbf"]').fill(inputs["coolest_build"])
              page.wait_for_timeout(1000)

              # Click submit button
              page.locator('div[role="button"]:has-text("Submit")').click()

              # Wait 10 seconds
              page.wait_for_timeout(10000)

              print("Shutting down...")
              page.close()
              browser.close()

      if __name__ == "__main__":
          inputs = {
              "superpower": "Invisibility",
              "features_used": [
                  "Verified",
                  "Proxies",
                  "Session Replay"
              ],
              "coolest_build": "A bot that automates form submissions across multiple sites.",
          }
          fill_form(inputs)
      ```
    </CodeGroup>
  </Tab>
</Tabs>

<Note>
  This example form is for testing purposes - feel free to submit responses multiple times while experimenting.
</Note>

## Best practices

### Wait for form elements to load

Target specific form elements with `waitForSelector` rather than using arbitrary timeouts. This ensures your automation proceeds only when the form is ready.

<Tabs>
  <Tab title="Node.js">
    ```typescript theme={null}
    // Wait for a specific input field to appear
    await page.waitForSelector('input[name="email"]', { timeout: 10000 });
    await page.fill('input[name="email"]', "user@example.com");
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    # Wait for a specific input field to appear
    page.wait_for_selector('input[name="email"]', timeout=10000)
    page.fill('input[name="email"]', "user@example.com")
    ```
  </Tab>
</Tabs>

### Handle multi-step and dynamic forms

Forms that reveal fields based on previous selections need sequential interaction. Wait for each new section to render before continuing.

<Tabs>
  <Tab title="Node.js">
    ```typescript theme={null}
    // Select an option that reveals additional fields
    await page.selectOption('select[name="account_type"]', "business");

    // Wait for the dynamically revealed fields
    await page.waitForSelector('input[name="company_name"]', { state: "visible" });
    await page.fill('input[name="company_name"]', "Acme Corp");
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    # Select an option that reveals additional fields
    page.select_option('select[name="account_type"]', "business")

    # Wait for the dynamically revealed fields
    page.wait_for_selector('input[name="company_name"]', state="visible")
    page.fill('input[name="company_name"]', "Acme Corp")
    ```
  </Tab>
</Tabs>

### Persist authentication with contexts

Use [browser contexts](/platform/browser/core-features/contexts) to save login state across form automation runs, so you don't need to re-authenticate each time.

<Tabs>
  <Tab title="Node.js">
    ```typescript theme={null}
    const bb = new Browserbase({ apiKey: process.env.BROWSERBASE_API_KEY });
    const session = await bb.sessions.create({
      browserSettings: {
        context: { id: "my-form-context", persist: true },
      },
    });
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    bb = Browserbase(api_key=os.environ["BROWSERBASE_API_KEY"])
    session = bb.sessions.create(
        browser_settings={
            "context": {"id": "my-form-context", "persist": True},
        },
    )
    ```
  </Tab>
</Tabs>

### Verify submission success

Check for confirmation messages, URL changes, or response status after submitting a form.

<Tabs>
  <Tab title="Node.js">
    ```typescript theme={null}
    await page.click('button[type="submit"]');

    // Check for a success message or URL change
    await page.waitForSelector("text=Your response has been recorded.");
    const currentUrl = page.url();
    console.log(`Submission complete. Redirected to: ${currentUrl}`);
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    page.click('button[type="submit"]')

    # Check for a success message or URL change
    page.wait_for_selector("text=Your response has been recorded.")
    current_url = page.url
    print(f"Submission complete. Redirected to: {current_url}")
    ```
  </Tab>
</Tabs>

## Next steps

<CardGroup cols={3}>
  <Card title="Session live view" icon="magnifying-glass" href="/platform/browser/observability/session-live-view">
    Learn how to watch test sessions in real time
  </Card>

  <Card title="Uploads" icon="upload" href="/platform/browser/files/uploads">
    Learn how to upload files with Browserbase
  </Card>

  <Card title="Browserbase Functions" icon="bolt" href="/platform/runtime/overview">
    Deploy form automation as serverless functions
  </Card>
</CardGroup>
