Save Logins to Authenticated Profiles
Capture a logged-in browser state once — cookies, localStorage, session tokens — and reuse it across parallel sessions without re-entering credentials.
- A Browserless API token from your account dashboard
Steps
Saving an authenticated profile works in two phases: first you create and save the profile with a live login session, then you reuse that profile in any future request.
- REST API
- Frameworks
Use the REST API to save and reuse authenticated browser profiles.
- cURL
- JavaScript
- Python
- Java
- C#
- Go
- PHP
- Ruby
1. Start a profile session
Create a named profile session. The response includes a WebSocket URL to connect a CDP client for the login flow:
curl -X POST \
"https://production-sfo.browserless.io/profile?token=YOUR_API_TOKEN_HERE" \
-H "Content-Type: application/json" \
-d '{ "name": "my-profile" }'
The response:
{
"id": "<session-id>",
"name": "my-profile",
"connect": "wss://production-sfo.browserless.io/session/connect/<id>?token=...",
"stop": "https://production-sfo.browserless.io/session/<id>?token=..."
}
2. Log in and save
Connect a CDP client (see the Puppeteer tab), complete the login, and call Browserless.saveProfile to persist the session.
3. Reuse the profile
Append ?profile=my-profile to any Browserless endpoint to start pre-authenticated:
curl -X POST \
"https://production-sfo.browserless.io/screenshot?token=YOUR_API_TOKEN_HERE&profile=my-profile" \
-H "Content-Type: application/json" \
-d '{ "url": "https://app.example.com/dashboard" }' \
--output dashboard.png
1. Create a profile session
const TOKEN = 'YOUR_API_TOKEN_HERE';
const ORIGIN = 'https://production-sfo.browserless.io';
const session = await fetch(`${ORIGIN}/profile?token=${TOKEN}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'my-profile' }),
}).then((r) => r.json());
console.log(session.connect);
// Use session.connect with the Frameworks tab to log in and save.
2. Reuse the saved profile
import fs from 'fs';
const response = await fetch(
`${ORIGIN}/screenshot?token=${TOKEN}&profile=my-profile`,
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ url: 'https://app.example.com/dashboard' }),
}
);
const buffer = await response.arrayBuffer();
await fs.promises.writeFile('dashboard.png', Buffer.from(buffer));
console.log('Screenshot saved.');
1. Install dependencies
pip install requests playwright
2. Create a profile session
import requests
TOKEN = 'YOUR_API_TOKEN_HERE'
ORIGIN = 'https://production-sfo.browserless.io'
session = requests.post(
f'{ORIGIN}/profile?token={TOKEN}',
json={'name': 'my-profile'},
).json()
print(session['connect'])
# Use session['connect'] with the Frameworks → Playwright → Python tab to log in and save.
Profile creation requires a live browser session — this tab covers only the HTTP steps. To complete the login and persist the session, switch to the Frameworks → Playwright → Python tab. That example connects to session['connect'] with playwright.chromium.connect_over_cdp, logs in, then calls page.context.new_cdp_session(page) to open a CDP channel and cdp.send('Browserless.saveProfile', {'name': 'my-profile'}) to save it.
3. Reuse the saved profile
import requests
TOKEN = 'YOUR_API_TOKEN_HERE'
ORIGIN = 'https://production-sfo.browserless.io'
response = requests.post(
f'{ORIGIN}/screenshot?token={TOKEN}&profile=my-profile',
json={'url': 'https://app.example.com/dashboard'},
)
with open('dashboard.png', 'wb') as f:
f.write(response.content)
print('Screenshot saved.')
1. Add dependencies
<dependency>
<groupId>com.microsoft.playwright</groupId>
<artifactId>playwright</artifactId>
<version>1.60.0</version>
</dependency>
2. Create a profile session
import java.net.http.*;
import java.net.URI;
import java.nio.file.*;
HttpClient client = HttpClient.newHttpClient();
HttpRequest sessionRequest = HttpRequest.newBuilder()
.uri(URI.create("https://production-sfo.browserless.io/profile?token=YOUR_API_TOKEN_HERE"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString("{\"name\": \"my-profile\"}"))
.build();
HttpResponse<String> sessionResponse = client.send(sessionRequest, HttpResponse.BodyHandlers.ofString());
System.out.println(sessionResponse.body());
// Parse session.connect and use the Playwright Java tab to log in and save.
3. Reuse the saved profile
HttpRequest screenshotRequest = HttpRequest.newBuilder()
.uri(URI.create("https://production-sfo.browserless.io/screenshot?token=YOUR_API_TOKEN_HERE&profile=my-profile"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString("{\"url\": \"https://app.example.com/dashboard\"}"))
.build();
HttpResponse<byte[]> screenshotResponse = client.send(screenshotRequest, HttpResponse.BodyHandlers.ofByteArray());
Files.write(Path.of("dashboard.png"), screenshotResponse.body());
System.out.println("Screenshot saved.");
1. Create a profile session
using System.Net.Http;
using System.Text;
using var client = new HttpClient();
var sessionBody = new StringContent("{\"name\": \"my-profile\"}", Encoding.UTF8, "application/json");
var sessionResponse = await client.PostAsync(
"https://production-sfo.browserless.io/profile?token=YOUR_API_TOKEN_HERE",
sessionBody
);
Console.WriteLine(await sessionResponse.Content.ReadAsStringAsync());
// Parse session.connect and use the Playwright C# tab to log in and save.
2. Reuse the saved profile
var screenshotBody = new StringContent("{\"url\": \"https://app.example.com/dashboard\"}", Encoding.UTF8, "application/json");
var screenshotResponse = await client.PostAsync(
"https://production-sfo.browserless.io/screenshot?token=YOUR_API_TOKEN_HERE&profile=my-profile",
screenshotBody
);
var bytes = await screenshotResponse.Content.ReadAsByteArrayAsync();
await File.WriteAllBytesAsync("dashboard.png", bytes);
Console.WriteLine("Screenshot saved.");
1. Create a profile session
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
)
func main() {
client := &http.Client{}
// Create profile session.
sessionBody := bytes.NewBufferString(`{"name": "my-profile"}`)
sessionReq, _ := http.NewRequest("POST",
"https://production-sfo.browserless.io/profile?token=YOUR_API_TOKEN_HERE",
sessionBody,
)
sessionReq.Header.Set("Content-Type", "application/json")
sessionResp, _ := client.Do(sessionReq)
defer sessionResp.Body.Close()
var session map[string]string
json.NewDecoder(sessionResp.Body).Decode(&session)
fmt.Println(session["connect"])
// Use session["connect"] with the Frameworks tab to log in and save.
2. Reuse the saved profile
// Reuse the saved profile.
screenshotBody := bytes.NewBufferString(`{"url": "https://app.example.com/dashboard"}`)
screenshotReq, _ := http.NewRequest("POST",
"https://production-sfo.browserless.io/screenshot?token=YOUR_API_TOKEN_HERE&profile=my-profile",
screenshotBody,
)
screenshotReq.Header.Set("Content-Type", "application/json")
screenshotResp, _ := client.Do(screenshotReq)
defer screenshotResp.Body.Close()
data, _ := io.ReadAll(screenshotResp.Body)
os.WriteFile("dashboard.png", data, 0644)
fmt.Println("Screenshot saved.")
}
1. Create a profile session
<?php
$ch = curl_init('https://production-sfo.browserless.io/profile?token=YOUR_API_TOKEN_HERE');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => '{"name": "my-profile"}',
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
CURLOPT_RETURNTRANSFER => true,
]);
$session = json_decode(curl_exec($ch), true);
curl_close($ch);
// Use $session['connect'] with the Frameworks tab to log in and save.
2. Reuse the saved profile
$ch2 = curl_init('https://production-sfo.browserless.io/screenshot?token=YOUR_API_TOKEN_HERE&profile=my-profile');
curl_setopt_array($ch2, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => '{"url": "https://app.example.com/dashboard"}',
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
CURLOPT_RETURNTRANSFER => true,
]);
file_put_contents('dashboard.png', curl_exec($ch2));
curl_close($ch2);
echo "Screenshot saved.\n";
1. Create a profile session
require 'net/http'
require 'json'
require 'uri'
# Create profile session.
uri = URI('https://production-sfo.browserless.io/profile?token=YOUR_API_TOKEN_HERE')
response = Net::HTTP.post(uri, '{"name": "my-profile"}', 'Content-Type' => 'application/json')
session = JSON.parse(response.body)
puts session['connect']
# Use session['connect'] with the Frameworks tab to log in and save.
2. Reuse the saved profile
# Reuse the saved profile.
uri2 = URI('https://production-sfo.browserless.io/screenshot?token=YOUR_API_TOKEN_HERE&profile=my-profile')
response2 = Net::HTTP.post(uri2, '{"url": "https://app.example.com/dashboard"}', 'Content-Type' => 'application/json')
File.binwrite('dashboard.png', response2.body)
puts 'Screenshot saved.'
Use a browser connection to log in, save a profile via CDP, and reconnect with the saved profile.
- Puppeteer
- Playwright
1. Install dependencies
npm install puppeteer-core
2. Log in and save the profile
import puppeteer from 'puppeteer-core';
const TOKEN = 'YOUR_API_TOKEN_HERE';
const ORIGIN = 'https://production-sfo.browserless.io';
const session = await fetch(`${ORIGIN}/profile?token=${TOKEN}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'my-profile' }),
}).then((r) => r.json());
const browser = await puppeteer.connect({ browserWSEndpoint: session.connect });
try {
const page = await browser.newPage();
await page.goto('https://app.example.com/login');
await page.type('#email', 'user@example.com');
await page.type('#password', process.env.PASSWORD);
await page.click("button[type='submit']");
await page.waitForNavigation();
const cdp = await page.createCDPSession();
const result = await cdp.send('Browserless.saveProfile', { name: 'my-profile' });
console.log(result);
// { ok: true, profileId: '<id>', name: 'my-profile', cookieCount: 12, originCount: 1 }.
} finally {
// Always close to release the session even on error.
await browser.close();
}
3. Reuse the saved profile in parallel sessions
import puppeteer from 'puppeteer-core';
const WS = 'wss://production-sfo.browserless.io?token=YOUR_API_TOKEN_HERE&profile=my-profile';
// Each connect() call is a separate browser process — profiles let them all start authenticated.
const results = await Promise.all(
Array.from({ length: 5 }, async (_, i) => {
const browser = await puppeteer.connect({ browserWSEndpoint: WS });
try {
const page = await browser.newPage();
await page.goto(`https://app.example.com/item/${i}`);
return await page.title();
} finally {
// Always close to release the session even on error.
await browser.close();
}
})
);
console.log(results);
- JavaScript
- Python
- Java
- C#
1. Install dependencies
npm install playwright-core
2. Log in and save the profile
import { chromium } from 'playwright-core';
const TOKEN = 'YOUR_API_TOKEN_HERE';
const ORIGIN = 'https://production-sfo.browserless.io';
const session = await fetch(`${ORIGIN}/profile?token=${TOKEN}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'my-profile' }),
}).then((r) => r.json());
const browser = await chromium.connectOverCDP(session.connect);
try {
const context = browser.contexts()[0];
const page = await context.newPage();
await page.goto('https://app.example.com/login');
await page.fill('#email', 'user@example.com');
await page.fill('#password', process.env.PASSWORD);
await page.click("button[type='submit']");
await page.waitForURL('**/dashboard');
const cdpSession = await context.newCDPSession(page);
const result = await cdpSession.send('Browserless.saveProfile', { name: 'my-profile' });
console.log(result);
// { ok: true, profileId: '<id>', name: 'my-profile', cookieCount: 12, originCount: 1 }.
} finally {
// Always close to release the session even on error.
await browser.close();
}
3. Reuse the saved profile
import { chromium } from 'playwright-core';
const browser = await chromium.connect(
`wss://production-sfo.browserless.io/chromium/playwright?token=YOUR_API_TOKEN_HERE&profile=my-profile`
);
try {
const context = browser.contexts()[0];
const page = await context.newPage();
await page.goto('https://app.example.com/dashboard'); // already logged in.
console.log('Title:', await page.title());
} finally {
// Always close to release the session even on error.
await browser.close();
}
1. Install dependencies
pip install requests playwright
2. Log in and save the profile
import requests
from playwright.sync_api import sync_playwright
TOKEN = 'YOUR_API_TOKEN_HERE'
ORIGIN = 'https://production-sfo.browserless.io'
session = requests.post(
f'{ORIGIN}/profile?token={TOKEN}',
json={'name': 'my-profile'},
).json()
with sync_playwright() as playwright:
# connect_over_cdp gives access to CDP sessions needed for Browserless.saveProfile.
browser = playwright.chromium.connect_over_cdp(session['connect'])
try:
context = browser.contexts[0]
page = context.new_page()
page.goto('https://app.example.com/login')
page.fill('#email', 'user@example.com')
page.fill('#password', 'YOUR_PASSWORD')
page.click("button[type='submit']")
page.wait_for_url('**/dashboard')
cdp = page.context.new_cdp_session(page)
result = cdp.send('Browserless.saveProfile', {'name': 'my-profile'})
print(result)
# {'ok': True, 'profileId': '<id>', 'name': 'my-profile', 'cookieCount': 12, 'originCount': 1}
finally:
# Always close to release the session even on error.
browser.close()
3. Reuse the saved profile
WS_REUSE = (
'wss://production-sfo.browserless.io/chromium/playwright'
'?token=YOUR_API_TOKEN_HERE&profile=my-profile'
)
with sync_playwright() as playwright:
browser = playwright.chromium.connect_over_cdp(WS_REUSE)
try:
context = browser.contexts[0]
page = context.pages[0]
page.goto('https://app.example.com/dashboard') # already logged in.
print('Title:', page.title())
finally:
# Always close to release the session even on error.
browser.close()
1. Add dependencies
<dependency>
<groupId>com.microsoft.playwright</groupId>
<artifactId>playwright</artifactId>
<version>1.60.0</version>
</dependency>
<!-- Add Gson or Jackson for JSON parsing -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.10.1</version>
</dependency>
2. Log in and save the profile
import com.microsoft.playwright.*;
import com.google.gson.*;
import java.net.http.*;
import java.net.URI;
public class SaveProfile {
public static void main(String[] args) throws Exception {
String token = "YOUR_API_TOKEN_HERE";
String origin = "https://production-sfo.browserless.io";
HttpClient http = HttpClient.newHttpClient();
HttpRequest req = HttpRequest.newBuilder()
.uri(URI.create(origin + "/profile?token=" + token))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString("{\"name\": \"my-profile\"}"))
.build();
String body = http.send(req, HttpResponse.BodyHandlers.ofString()).body();
String wsEndpoint = JsonParser.parseString(body)
.getAsJsonObject().get("connect").getAsString();
try (Playwright playwright = Playwright.create()) {
Browser browser = playwright.chromium().connectOverCDP(wsEndpoint);
try {
BrowserContext context = browser.contexts().get(0);
Page page = context.newPage();
page.navigate("https://app.example.com/login");
page.fill("#email", "user@example.com");
page.fill("#password", System.getenv("PASSWORD"));
page.click("button[type='submit']");
page.waitForURL("**/dashboard");
// connectOverCDP gives access to CDP sessions needed for Browserless.saveProfile.
CDPSession cdp = page.context().newCDPSession(page);
JsonElement result = cdp.send("Browserless.saveProfile", Map.of("name", "my-profile"));
System.out.println(result);
// {"ok":true,"profileId":"<id>","name":"my-profile","cookieCount":12,"originCount":1}
} finally {
// Always close to release the session even on error.
browser.close();
}
}
}
}
3. Reuse the saved profile
String wsReuse = "wss://production-sfo.browserless.io?token=YOUR_API_TOKEN_HERE&profile=my-profile";
try (Playwright playwright = Playwright.create()) {
Browser browser = playwright.chromium().connectOverCDP(wsReuse);
try {
BrowserContext context = browser.contexts().get(0);
Page page = context.pages().get(0);
page.navigate("https://app.example.com/dashboard"); // already logged in.
System.out.println("Title: " + page.title());
} finally {
// Always close to release the session even on error.
browser.close();
}
}
1. Add dependencies
dotnet add package Microsoft.Playwright
2. Log in and save the profile
using Microsoft.Playwright;
using System.Net.Http;
using System.Text;
using System.Text.Json;
const string Token = "YOUR_API_TOKEN_HERE";
const string Origin = "https://production-sfo.browserless.io";
using var http = new HttpClient();
var sessionBody = new StringContent("{\"name\": \"my-profile\"}", Encoding.UTF8, "application/json");
var sessionResp = await http.PostAsync($"{Origin}/profile?token={Token}", sessionBody);
var sessionJson = JsonDocument.Parse(await sessionResp.Content.ReadAsStringAsync());
var wsEndpoint = sessionJson.RootElement.GetProperty("connect").GetString();
using var playwright = await Playwright.CreateAsync();
var browser = await playwright.Chromium.ConnectOverCDPAsync(wsEndpoint);
try
{
var context = browser.Contexts[0];
var page = await context.NewPageAsync();
await page.GotoAsync("https://app.example.com/login");
await page.FillAsync("#email", "user@example.com");
await page.FillAsync("#password", Environment.GetEnvironmentVariable("PASSWORD"));
await page.ClickAsync("button[type='submit']");
await page.WaitForURLAsync("**/dashboard");
// ConnectOverCDP gives access to CDP sessions needed for Browserless.saveProfile.
var cdp = await page.Context.NewCDPSessionAsync(page);
var result = await cdp.SendAsync(
"Browserless.saveProfile",
new Dictionary<string, object> { ["name"] = "my-profile" }
);
Console.WriteLine(result);
// { ok: true, profileId: '<id>', name: 'my-profile', cookieCount: 12, originCount: 1 }
}
finally
{
// Always close to release the session even on error.
await browser.CloseAsync();
}
3. Reuse the saved profile
using var playwright = await Playwright.CreateAsync();
var browser = await playwright.Chromium.ConnectOverCDPAsync(
"wss://production-sfo.browserless.io?token=YOUR_API_TOKEN_HERE&profile=my-profile"
);
try
{
var context = browser.Contexts[0];
var page = context.Pages[0];
await page.GotoAsync("https://app.example.com/dashboard"); // already logged in.
Console.WriteLine($"Title: {await page.TitleAsync()}");
}
finally
{
// Always close to release the session even on error.
await browser.CloseAsync();
}
Next steps
- Log In and Reuse Sessions — full walkthrough of the session reuse pattern
- Run Concurrent Browser Sessions — launch many sessions in parallel using the saved profile
- Scrape Structured Data — extract data from authenticated pages