Profiles let you save and reuse browser state, which includes cookies, local storage, session storage, and cache, across multiple sessions. Perfect for maintaining login states, preserving user preferences, or building realistic multi session workflows.
How Profiles Work
A profile is essentially a saved snapshot of a browser’s user data directory. By default, each Hyperbrowser session uses a fresh user data directory to ensure isolation.
When you create a profile and then use and persist it in a new session, Hyperbrowser saves that session’s user data directory. You can then attach the profile to future sessions.
Create a Profile
Create a profile to store browser state. Optionally, give it a name to keep track of different profiles:
import { Hyperbrowser } from "@hyperbrowser/sdk";
import { config } from "dotenv";
config();
const client = new Hyperbrowser({
  apiKey: process.env.HYPERBROWSER_API_KEY,
});
// Create a profile with optional name
const profile = await client.profiles.create({
  name: "my-profile",
});
console.log("Profile created:", profile.id);
console.log("Profile name:", profile.name);
Use a Profile in a Session
Attach a profile to a session to load and save browser state:
const session = await client.sessions.create({
  profile: {
    id: profile.id,
    // Optional: Persist changes made to the profile during the session
    // Set to true for the first session of a new profile
    // persistChanges: true,
  },
});
console.log("Session created with profile:", session.id);
Persist Changes
The persistChanges parameter controls whether session changes are saved. You will need to do this for the first time you use a new profile in a session so it can be used in subsequent sessions.
- true- Persist changes made to the profile during the session
- false(default) - Use the profile as read-only; don’t persist changes made to the profile during the session
Once whatever changes to the  profile have been made, the session should be safely closed to ensure that the profile is saved. After that unless the “persistChanges”: true option is passed to the session again, the profile will be immutable.
When using browser automation libraries like Playwright or Puppeteer, it is important to always use the default context in order for profiles to work properly.
Profile Login Example
Step 1: Login and Save to Profile
First, create a profile and perform a login. When the session ends, all cookies and browser state are automatically saved to the profile.
import { Hyperbrowser } from "@hyperbrowser/sdk";
import { chromium } from "playwright-core";
import { config } from "dotenv";
config();
const client = new Hyperbrowser({
  apiKey: process.env.HYPERBROWSER_API_KEY,
});
async function loginAndSaveProfile() {
  // 1. Create a profile
  const profile = await client.profiles.create({
    name: "hackernews-profile",
  });
  console.log("Profile created:", profile.id);
  // 2. Create session with profile
  const session = await client.sessions.create({
    profile: {
      id: profile.id,
      persistChanges: true, // Save browser state to profile
    },
  });
  const HACKER_NEWS_USERNAME = "your_username";
  const HACKER_NEWS_PASSWORD = "your_password";
  try {
    const browser = await chromium.connectOverCDP(session.wsEndpoint);
    const defaultContext = browser.contexts()[0];
    const page = defaultContext.pages()[0];
    // 3. Log in to Hacker News
    await page.goto("https://news.ycombinator.com/login");
    await page.fill('input[name="acct"]', HACKER_NEWS_USERNAME);
    await page.fill('input[name="pw"]', HACKER_NEWS_PASSWORD);
    await page.click('input[type="submit"]');
    await page.waitForLoadState("networkidle");
    const pageUrl = page.url();
    const isLoggedIn = pageUrl === "https://news.ycombinator.com/";
    if (isLoggedIn) {
      console.log("Logged in successfully to Hacker News");
    } else {
      console.error("Failed to log in to Hacker News");
    }
  } catch (err) {
    console.error("Error logging in to Hacker News:", err);
  } finally {
    // 4. Stop session - profile now contains login cookies
    await client.sessions.stop(session.id);
  }
  return profile.id;
}
// Save the profile ID for later use
loginAndSaveProfile().then((profileId) => {
  console.log("Saved profile ID:", profileId);
});
Profiles can take a few seconds after the browser session is closed to completely save.
Step 2: Reuse the Saved Profile
After waiting a couple seconds, now use the saved profile in a new session. The browser will automatically load the saved cookies, so you’re already logged in without needing to authenticate again.
import { Hyperbrowser } from "@hyperbrowser/sdk";
import { chromium } from "playwright-core";
import { config } from "dotenv";
config();
const client = new Hyperbrowser({
  apiKey: process.env.HYPERBROWSER_API_KEY,
});
async function useExistingProfile(profileId) {
  // Use the same profile in a new session
  const session = await client.sessions.create({
    profile: {
      id: profileId,
    },
  });
  try {
    const browser = await chromium.connectOverCDP(session.wsEndpoint);
    const defaultContext = browser.contexts()[0];
    const page = defaultContext.pages()[0];
    // Navigate directly to Hacker News - already logged in!
    await page.goto("https://news.ycombinator.com/", {
      waitUntil: "networkidle",
    });
    try {
      await page.waitForSelector("#me", { timeout: 7_000 });
    } catch {
      console.error("Timed out waiting for selector #me");
    }
    const username = await page.$eval("#me", (el) =>
      el ? el.textContent : null
    );
    if (username) {
      console.log("Still logged in as:", username);
    } else {
      console.error("Not logged in to Hacker News");
    }
  } catch (err) {
    console.error("Error using existing profile:", err);
  } finally {
    await client.sessions.stop(session.id);
  }
}
useExistingProfile("your-profile-id").then(() => {
  console.log("Finished reusing profile!");
});
- A new session is created using the same profile ID
- The browser automatically loads the saved cookies and state from the profile
- You can navigate directly to Hacker News and you’re already authenticated
- No login required - the session picks up right where you left off
This pattern is useful for scenarios where you need to:
- Avoid repeated logins across multiple automation runs
- Test authenticated user flows
- Manage multiple accounts with separate profiles
- Maintain session state over time
 Managing Profiles
List Profiles
Retrieve all your profiles with optional pagination and filtering:
const result = await client.profiles.list({
  page: 1,
  name: "hackernews-profile",
});
console.log(`Found ${result.totalCount} profiles`);
result.profiles.forEach((profile) => {
  console.log(`- ${profile.name}: ${profile.id}`);
});
Get Profile Details
Retrieve information about a specific profile:
const profile = await client.profiles.get("your-profile-id");
console.log("Profile ID:", profile.id);
console.log("Profile Name:", profile.name);
console.log("Created At:", profile.createdAt);
Delete a Profile
Delete a profile when you no longer need it:
await client.profiles.delete("profile-id");
console.log("Profile deleted");
Read-Only Profile Usage
Load a previously persisted profile without saving any changes made during this new session:
const session = await client.sessions.create({
  profile: {
    id: "your-profile-id",
    persistChanges: false,  // Don't save changes
  },
});
// Use the session - changes will not be persisted
- Test workflows without affecting the saved state
- Run parallel sessions with the same profile safely
- Maintain a clean baseline profile for repeated use
By default, persistChanges is set to false so you don’t have to explicitly specify it.
Next Steps