Browsers
@solarisdk/browser: classes Solari and BrowserSession. See the TypeScript SDK hub for install and configuration.
npm install @solarisdk/browserContents
Solari: new Solari(), launch(), close(), request(), sessions.create(), sessions.release(), sessions.releaseAndWait(), sessions.getReplayUrl(), sessions.downloadReplay(), profiles.create(), profiles.list(), profiles.save(), profiles.delete()BrowserSession: isConnected(), version(), contexts(), newContext(), newPage(), close(), [Symbol.asyncDispose]()- Types:
SolariOptions,LaunchOptions,CreateSessionOptions,Session,StorageState,ProxyRequest,ResolvedProxyConfig,Profile,ReplayUrl,SolariError
Solari
The browser client. Creates sessions and manages profiles.
Properties
sessionsSessionsResource: session create/release/replay calls.profilesProfilesResource: stored browser profile CRUD.
Constructors
new Solari()
new Solari(opts: SolariOptions): SolariCreates a client. Does not open any connection.
Parameters:
optsSolariOptions: see SolariOptions.
Returns: Solari
Throws: SolariError if apiKey is empty or region is unknown.
Example:
import { Solari } from "@solarisdk/browser";
const solari = new Solari({ apiKey: process.env.SOLARI_API_KEY! });Methods
launch()
launch(options?: LaunchOptions): Promise<BrowserSession>Creates a session and connects to it. Returns a BrowserSession wrapping a real Playwright Browser.
Parameters:
options?LaunchOptions: CreateSessionOptions plusretries,probe,probeTimeoutMs.
Returns: Promise<BrowserSession>
Throws: the last connect error once attempts are spent; SolariError with code BrowserUnhealthy if the probe fails.
Example:
const browser = await solari.launch({ stealth: true, retries: 2 });
const page = await browser.newPage();
await page.goto("https://example.com");
await browser.close();sessions.create() again and sleeps 100ms × attempt, and only for a BrowserUnhealthy probe failure or a transient connect error. Anything else throws on the first attempt. A failure from sessions.create() itself is not retried here; it propagates immediately under its own HTTP retry policy. probe defaults to true when retries > 0.close()
close(): Promise<void>Stops the client’s local proxy. Call once you are done with the client. It does not release live sessions.
Returns: Promise<void>
Example:
await browser.close(); // release the session first
await solari.close(); // then shut the client downrequest()
request(method: string, path: string, body?: unknown): Promise<Response>Low-level authenticated call against the API, with the client’s retry and timeout policy applied. An escape hatch for endpoints the SDK does not wrap.
Parameters:
methodstring: HTTP verb.pathstring: path appended tobaseUrl, e.g./sessions.body?unknown: JSON-serialized when present.
Returns: Promise<Response>: the raw fetch response. Non-2xx is returned, not thrown.
Throws: SolariError only when all attempts are exhausted.
Example:
const res = await solari.request("GET", "/profiles");
console.log(res.status, await res.json());backoffMs sleep, not exponential. maxAttempts: 2 means one retry. timeoutMs is per attempt. 429 is not retried.sessions.create()
sessions.create(options?: CreateSessionOptions): Promise<Session>Acquires a browser session without connecting. Use when you want to drive the endpoints yourself; otherwise use launch().
Parameters:
options?CreateSessionOptions:profileId,recording,stealth,captcha,webBotAuth,proxy.
Returns: Promise<Session>
Throws: SolariError carrying status and the server code (e.g. FeatureRequiresPlan, ConcurrencyLimitExceeded).
Example:
const session = await solari.sessions.create({ stealth: true, proxy: "us" });
const browser = await chromium.connect(session.wsEndpoint);captcha: true and any proxy request are rejected unless stealth: true is also set.sessions.release()
sessions.release(id: string): voidFire-and-forget release. Returns immediately; failures are logged, not thrown. Use releaseAndWait() when you need confirmation.
Parameters:
idstring: session id.
Returns: void
Example:
solari.sessions.release(session.id);sessions.releaseAndWait()
sessions.releaseAndWait(id: string): Promise<void>Releases a session and waits for the server to confirm.
Parameters:
idstring: session id.
Returns: Promise<void>
Throws: SolariError on any error status except a bare 404, including a 404 carrying code: "InvalidSessionId".
Example:
await solari.sessions.releaseAndWait(session.id);
// the replay is available ~1-3s later204 for any authentic session id, even one already gone, so a 404 does not mean “already released”. A 404 tagged InvalidSessionId means the id was refused (malformed, forged, or another org’s) and nothing was released; that throws. A bare 404 with no code is tolerated for older gateways.sessions.getReplayUrl()
sessions.getReplayUrl(id: string): Promise<ReplayUrl>Returns a presigned URL for a recorded session’s replay. Requires recording: true at create time.
Parameters:
idstring: session id.
Returns: Promise<ReplayUrl>: url, expiresInSeconds, contentEncoding.
Throws: SolariError if the replay is not ready or does not exist.
Example:
const { url, expiresInSeconds } = await solari.sessions.getReplayUrl(id);sessions.downloadReplay()
sessions.downloadReplay(id: string): Promise<Uint8Array>Resolves the replay URL and downloads the NDJSON bytes in one call.
Parameters:
idstring: session id.
Returns: Promise<Uint8Array>: gzipped NDJSON.
Throws: SolariError if the download fails.
Example:
import { writeFile } from "node:fs/promises";
const bytes = await solari.sessions.downloadReplay(id);
await writeFile("replay.ndjson.gz", bytes);profiles.create()
profiles.create(opts: { name: string }): Promise<Profile>Creates an empty profile. Attach it to a session with profileId to persist cookies and localStorage.
Parameters:
opts.namestring: human-facing name.
Returns: Promise<Profile>
Throws: SolariError with the server code on failure.
Example:
const profile = await solari.profiles.create({ name: "logged-in" });
const browser = await solari.launch({ profileId: profile.id });profiles.list()
profiles.list(): Promise<Profile[]>Lists every profile on the account.
Returns: Promise<Profile[]>
Example:
for (const p of await solari.profiles.list()) console.log(p.id, p.name);profiles.save()
profiles.save(id: string, storageState: StorageState): Promise<{ version: number; sizeBytes: number }>Overwrites a profile with the given storage state. Pair it with Playwright’s context.storageState() to snapshot a logged-in session.
Parameters:
idstring: profile id.storageStateStorageState: cookies + origins.
Returns: Promise<{ version: number; sizeBytes: number }>
Throws: SolariError on a non-2xx response.
Example:
const state = await browser.contexts()[0].storageState();
await solari.profiles.save(profile.id, state);profiles.delete()
profiles.delete(id: string): Promise<void>Deletes a profile. Idempotent, so a 404 resolves successfully.
Parameters:
idstring: profile id.
Returns: Promise<void>
Example:
await solari.profiles.delete(profile.id);BrowserSession
A live browser and its session. Returned by launch(); not constructed directly.
Properties
sessionSession: the raw session record.idstring: session id.expiresAtstring: ISO 8601 UTC deadline; the session auto-releases then.proxyResolvedProxyConfig | undefined: resolved proxy confirmation (country, tier, timezone — no credentials), when one was requested.wsEndpointstring: Playwright wire-protocol endpoint (loopback-wrapped).cdpEndpointstring: raw CDP endpoint (loopback-wrapped).rawBrowser: the underlying PlaywrightBrowser, for APIs this wrapper does not surface.
wsEndpoint and cdpEndpoint point at a LocalProxy on 127.0.0.1 that the TypeScript SDK runs in-process. They are only reachable from this process and only while the client is alive. Do not persist them or hand them to another machine. Other language SDKs return the upstream URL directly.Methods
isConnected()
isConnected(): booleantrue while the browser connection is alive and close() has not been called.
Returns: boolean
Example:
if (!browser.isConnected()) throw new Error("session dropped");version()
version(): stringThe browser version string.
Returns: string, e.g. "Chromium/130.0.6723.31".
Example:
console.log(browser.version());contexts()
contexts(): BrowserContext[]All open contexts. Sessions ship with a default context at contexts()[0]. Use it rather than opening a new one.
Returns: BrowserContext[]
Example:
const ctx = browser.contexts()[0];
const page = await ctx.newPage();newContext()
newContext(options?: BrowserContextOptions): Promise<BrowserContext>Opens a fresh context. Most callers want contexts()[0] instead. A new context does not inherit the session’s profile or stealth setup.
Parameters:
options?BrowserContextOptions: Playwright context options.
Returns: Promise<BrowserContext>
Example:
const ctx = await browser.newContext({ locale: "en-GB" });newPage()
newPage(): Promise<Page>Opens a fresh page in a new context.
Returns: Promise<Page>
Example:
const page = await browser.newPage();
await page.goto("https://example.com");close()
close(): Promise<void>Closes the browser and releases the session, waiting for confirmation. Idempotent.
Returns: Promise<void>
Throws: the browser-close error if one occurred, otherwise the release error.
Example:
try {
const page = await browser.newPage();
await page.goto("https://example.com");
} finally {
await browser.close();
}[Symbol.asyncDispose]()
[Symbol.asyncDispose](): Promise<void>Calls close(). Enables await using on Node 22+.
Returns: Promise<void>
Example:
await using browser = await solari.launch({ stealth: true });
const page = await browser.newPage();
await page.goto("https://example.com");
// released automatically at scope exitTypes
SolariOptions
apiKeystring: required.region?SolariRegion:"us-west"(default). Ignored whenbaseUrlis set.baseUrl?string: override the resolved region URL.maxAttempts?number: default2(one retry).backoffMs?number: default500, fixed.timeoutMs?number: default90_000, per attempt.
CreateSessionOptions
profileId?string: attach a stored profile.recording?boolean: record the session. Off by default.stealth?boolean: enable the runtime stealth shim. Off by default.captcha?boolean: managed captcha solving. Requiresstealth.webBotAuth?boolean: sign outbound requests for Cloudflare Web Bot Auth. Independent ofstealth; inert unless Web Bot Auth is enabled for your account.proxy?string | ProxyRequest | "off" | "smart": managed egress. Requiresstealth.
LaunchOptions
Extends CreateSessionOptions.
retries?number: extra re-launch attempts. Default0.probe?boolean: probe the browser before returning. Defaults totruewhenretries > 0.probeTimeoutMs?number: probe cap. Default2000.
Session
idstring: session id.wsEndpointstring: loopback Playwright endpoint.cdpEndpointstring: loopback CDP endpoint.expiresAtstring: ISO 8601 UTC deadline.storageState?StorageState | null: tri-state. Absent = no profile attached;null= profile exists but is empty; an object = the profile’s state.proxy?ResolvedProxyConfig: present only when a managed proxy was requested.
StorageState
cookies?Array: each withname,value, and optionaldomain,path,expires,httpOnly,secure,sameSite.origins?Array: each withoriginand optionallocalStorageentries.
ProxyRequest
country?string: ISO-3166-1 alpha-2, lowercase. Default"us".tier?"residential" | "static" | "mobile": default"residential"(rotating).asn?string: pin egress to an ASN.session?string: sticky-session id (alnum + dash, ≤32 chars).sessionDuration?number: sticky lifetime in minutes (1 to 30, default 10). Only withsession.state?string: US-only geo narrowing.city?string: US-only city pin.static?boolean: deprecated; usetier: "static".
ResolvedProxyConfig
Confirmation only. It carries no credentials by design — egress is applied server-side, so you never dial the proxy yourself and never receive its address or account.
timezoneIdstring: timezone matching the egress country. Pass it to Playwright’snewContextastimezoneIdif you build your own context and wantIntl/Dateto line up.countrystring: egress country actually resolved (lowercase ISO code).tier?"residential" | "static" | "mobile": the tier that actually served the session. Amobilerequest can degrade to residential, so compare against what you asked for.
Profile
idstring: profile id.namestring: human-facing name.
ReplayUrl
urlstring: presigned download URL.expiresInSecondsnumber: URL lifetime.contentEncodingstring: e.g."gzip".
SolariError
Extends Error. Every failure the browser SDK throws.
status?number: HTTP status, when the error came from a response.cause?unknown: the underlying error.code?SolariErrorCode | string:FeatureRequiresPlan,ConcurrencyLimitExceeded,PlanLimitExceeded,BrowserUnhealthy, orInvalidSessionId. Unknown codes pass through as plain strings.
import { SolariError } from "@solarisdk/browser";
try {
await solari.launch({ stealth: true, captcha: true });
} catch (e) {
if (e instanceof SolariError && e.code === "FeatureRequiresPlan") {
console.error("upgrade required:", e.status);
}
}