String API
Browser (CDP)

Browser contexts

Save logins, cookies and site storage between browser sessions.

A browser context is a saved browser profile. Sign in once, and every later /wss session that uses the same contextId starts with that login, its cookies, and its site storage in place.

Quick start

  1. Create a profile with POST /contexts and keep the returned id.
  2. Connect to /wss?contextId=…, sign in using the browser's default context, and close the browser. Closing is what saves the profile.
  3. Reconnect with the same contextId. Add persist=false to open as many read-only sessions as you need at once.
import { chromium } from "playwright-core";

const API = "https://request.usestring.ai/v1";
const auth = { Authorization: `Bearer ${process.env.STRING_API_KEY}` };
const connect = (contextId, persist = true) =>
  chromium.connectOverCDP(`${API.replace("https", "wss")}/wss?contextId=${contextId}&persist=${persist}`, {
    headers: auth,
    timeout: 90_000
  });

// 1. Create a profile.
const created = await fetch(`${API}/contexts`, {
  method: "POST",
  headers: { ...auth, "Content-Type": "application/json" },
  body: JSON.stringify({ name: "checkout-login" })
});
const { id: contextId } = await created.json();

// 2. Sign in once. Closing the browser saves the profile.
const browser = await connect(contextId);
const [context] = browser.contexts();
const page = context.pages()[0] ?? (await context.newPage());
await page.goto("https://example.com/login");
// ...fill in the login form and submit...
await browser.close();

// 3. Reconnect. The saved login is restored; read-only sessions (persist=false) can run in parallel.
const restored = await connect(contextId);
const [restoredContext] = restored.contexts();
await (restoredContext.pages()[0] ?? (await restoredContext.newPage())).goto("https://example.com/account");
await restored.close();

What to know

  • One saving session at a time. A second saving connection to the same profile is refused while the first is running. Read-only sessions (persist=false) are unlimited and run alongside it, but a profile that has never been saved cannot be opened read-only.
  • Only the default browser context is saved. browser.newContext() creates an incognito context whose data is discarded. Use browser.contexts()[0].
  • Send the same countryCode and proxy every time. Profiles do not store them, and a login restored from a different country may be challenged or signed out by the website.
  • Allow 90 seconds to connect. Set your client's connection timeout to at least that.
  • Profiles are shared across your organization and expire after 30 days without use.

Create a profile

POST https://request.usestring.ai/v1/contexts
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
Idempotency-Key: checkout-login-2026-09-07

{ "name": "checkout-login" }

name is optional: 1–200 characters after trimming, unique among your live profiles. Unknown fields return 400; a duplicate name returns 409. Success returns 201 with the profile object:

{
  "id": "d7a89cd7-c9f7-47fd-84df-77320f254cf8",
  "name": "checkout-login",
  "status": "EMPTY",
  "sizeBytes": null,
  "failureReason": null,
  "latestSaveOperation": null,
  "lastSavedAt": null,
  "lastUsedAt": null,
  "createdAt": "2026-09-07T09:14:22.031Z",
  "updatedAt": "2026-09-07T09:14:22.031Z"
}

Send an Idempotency-Key to retry a create without making a second profile. Keys are scoped to your organization for 24 hours and accept 1–255 characters from A-Z a-z 0-9 . _ : -.

SituationResponse
First use201 with the new profile.
Retry after completion201 with the same profile.
Retry while creation is in progress409; retry with backoff.
Retry after that profile was deletedA new profile is created.
Malformed or repeated header400.

Connect

Connect to /wss?contextId=… with your API key; persist defaults to true. Parameters are listed under GET /wss.

Saving and read-only sessions

A session with contextId saves its changes when it closes. Only one saving session can use a profile at a time; a second saving connection is refused while the first is still running. Any connection that arrives while the previous session's save is still finishing waits for it, at most 45 seconds, and then starts with the result.

Add persist=false to restore the saved profile without saving anything back. Read-only sessions are not the profile's saving session: run as many as you need at once, alongside a saving session. A profile that has never been saved cannot be used with persist=false. Read-only sessions count as use for retention.

Country and proxy settings

countryCode and proxy are session parameters, documented under GET /wss. Profiles do not remember them, so send the same values on every connection. A login restored from a different country may be challenged or signed out, and the same settings do not guarantee the same exit IP.

Refused connections

A save that outlives the wait described above answers Context is still being saved; a profile that another saving session is using answers Context is attached to a running session. Both are a WebSocket close with code 1008 and that reason. Playwright and Puppeteer report the close only as a failed connection, so read the profile's status to tell a busy refusal (IN_USE or UPLOADING) from any other error, then retry with bounded backoff:

for (let attempt = 0; attempt < 10; attempt++) {
  try {
    return await connect(contextId);
  } catch (error) {
    const { status } = await (await fetch(`${API}/contexts/${contextId}`, { headers: auth })).json();
    if (attempt === 9 || (status !== "IN_USE" && status !== "UPLOADING")) throw error;
    await new Promise((resolve) => setTimeout(resolve, 5_000));
  }
}

Read-only sessions are never refused for either reason.

Confirm the save

Optional. Use it when you need to know that a session's changes were saved, for example before telling a user that their login is stored.

Read GET /contexts/{contextId} before connecting and keep its lastSavedAt (null for a profile that has never been saved). After closing the browser, poll the same endpoint every few seconds until status is no longer IN_USE or UPLOADING:

ResultMeaning
READY and lastSavedAt is later than the value you keptThis session's changes were saved.
FAILEDThe save failed. Read failureReason. Any previously saved profile is retained.
READY or EMPTY and lastSavedAt is unchangedNothing was saved, for example because the connection was refused or closed before the session started. Check latestSaveOperation.
Still IN_USE or UPLOADING after your timeoutThe outcome is unknown, not failed. Inspect the profile before retrying.

If more than one client may save to the same profile, lastSavedAt cannot tell you whose save finished; track the specific save instead.

Track a specific save

latestSaveOperation describes the most recent save. While your session is connected, read GET /contexts/{contextId} and record latestSaveOperation.id. After closing the browser, poll and check that the id still matches before reading its status; if another session has saved since, the status endpoint cannot confirm your earlier save. With persist=false, no save is started.

Save statusMeaning
PENDINGThe save has not finished. Keep polling within your timeout.
COMMITTEDThe save succeeded.
FAILEDThe save failed. Read its failureReason. Any previously saved profile is retained.
EXPIREDThe session ended before anything could be saved. Retry.
SUPERSEDEDAnother session saved before this one finished. Do not treat it as success.

When a save fails

latestSaveOperation.failureReason describes the save you are tracking; the profile's top-level failureReason keeps the most recent failure until a save succeeds. Reconnecting restores the previous successful save, and the failed session's changes are lost. If the profile's only save failed, delete it and create another.

Status

Profile statusMeaning
EMPTYCreated with no saved data. Connect with persistence enabled to populate it.
IN_USEA saving session is using the profile. Another saving session or a deletion is refused.
UPLOADINGA save is finishing.
READYSaved data is available.
FAILEDA save failed. Previously saved data, if any, remains available.
DELETINGDeletion was accepted; stored data is still being removed. Returned only by DELETE.

Read-only sessions do not change the status, and READY does not reserve the profile. Contact us if a profile stays busy long after its session ended.

The profile object

Create, inspect, and list return:

FieldTypeMeaning
idstringOpaque UUID used to reconnect, inspect, or delete.
namestring or nullOptional label.
statusstringCurrent profile status.
sizeBytesinteger or nullCompressed size of the saved profile. Null before the first successful save.
failureReasonstring or nullMost recent save failure, cleared by a successful save.
latestSaveOperationobject or nullLatest save: { id, status, failureReason, completedAt }.
lastSavedAttimestamp or nullTime of the last successful save.
lastUsedAttimestamp or nullLast connection, saving or read-only. Reading status does not update it.
createdAttimestampCreation time.
updatedAttimestampLast profile update.

List profiles

GET https://request.usestring.ai/v1/contexts?limit=50&offset=0 returns { "contexts": [...] }, newest first, without deleted profiles.

limit defaults to 50 and is capped at 200; invalid values use the default. offset defaults to 0. Use a page size from 1–200 and stop when a page comes back short.

Inspect a profile

GET https://request.usestring.ai/v1/contexts/{contextId} returns the profile object. A malformed ID returns 400; an unknown, foreign-owned, or deleted profile returns 404 { "error": "Context not found" }.

Delete a profile

DELETE https://request.usestring.ai/v1/contexts/{contextId} returns 202:

{ "id": "d7a89cd7-c9f7-47fd-84df-77320f254cf8", "status": "DELETING", "deleted": false }

The profile is gone at once: it cannot be used, inspect returns 404, list omits it, and its name and its slot in the profile limit are free to reuse. Stored data is removed in the background. A profile with a saving session open returns 409; read-only sessions do not block deletion. Repeating the request answers 202 until the stored data has been removed, then 404.

Limits and retention

LimitDefault value
Profiles per organization25. A deleted profile frees its slot immediately.
Concurrent saving sessions10 per organization, including saves still in progress. Read-only sessions are not counted.
Storage per organization1 GiB across all profiles, including saves in progress.
Profile size, compressed50 MiB per save.
Profile size, uncompressed256 MiB.
Idle expiry30 days after last use, saving or read-only. Status polling does not extend it.
Never-used empty profile expiry7 days after creation.

Read-only sessions are still subject to the normal browser session limits. At a limit, creation returns 429 and connecting closes with 1008. A save over either size limit reports archive_failed and keeps the previous save. Contact us for higher limits.

Billing

Listing, inspecting, and deleting profiles do not require a positive balance. Creating profiles and running browser sessions do, at normal browser pricing. Refused connections are not billed.

What is restored

Cookies, localStorage, IndexedDB, service-worker registrations, and other persistent site data in the default browser context. A restored login can still expire or require verification under the website's own rules. Each connection starts a new browser session: open pages and running scripts do not resume, and cached resources may be downloaded again.

Status codes

HTTP statusMeaning
200Profile or list returned.
201Profile created or an idempotent create replayed.
202Deletion accepted; stored data is being removed.
400Invalid request.
401Missing or invalid API key.
402Insufficient balance for creation.
404Profile not found for this organization.
409Name conflict, create in progress, or profile busy.
429Profile, storage, or request-rate limit reached.
500Internal error.

WebSocket refusals use close frames; see GET /wss.