14 Use the Script API
14.1 Goal
Describe the window.api capabilities available to Sa2web page scripts and standard interface interception scripts, so scripts in site configuration, inner site configuration, account configuration, and plugin scripts can be written more reliably.
The Script API has five main categories:
| API | Purpose |
|---|---|
api.config |
Reads custom configuration; currently valid only under collaboration links |
api.http |
Sends HTTP requests while bypassing page CORS restrictions |
api.user |
Reads and writes data scoped by user, site, account, or device |
api.dom |
Queries DOM, checks visibility, listens for element changes, and creates overlays |
api.utils |
Waits for conditions and runs JavaScript in the page |
api.header() |
Reads recorded request or response headers |
Page scripts and standard interface interception scripts can use window.api. SSE scripts only guarantee that data is passed in and usually do not depend on window.api.
14.2 Overall Structure
In scripts, you can use api directly or access it through window.api:
interface Window {
api: {
user: UserApi;
config: Record<string, unknown>;
http: HttpApi;
dom: DomApi;
utils: UtilsApi;
header(headerName: string, isRequestHeader: boolean): Promise<string | string[] | undefined>;
};
}Example:
const apiBase = api.config.apiBase;
const userToken = await api.user.get('token');
const submit = api.dom.querySelector(document, 'button[type="submit"]');
const profile = await api.http.ajax({ url: `http://192.168.1.111:8080/profile` });Prefer Sa2web’s api.dom query tools in scripts. They support both CSS selectors and XPath selectors, making them more suitable for complex business pages than document.querySelector() alone.
14.3 api.config
api.config reads custom configuration from the site script environment.
config: Record<string, unknown>Example:
const apiBase = api.config.apiBase;
const featureEnabled = api.config.featureEnabled === true;14.4 api.http
api.http provides HTTP request tools for user scripts. api.http.ajax executes the actual request in the browser main process, so it is not restricted by the page’s CORS policy.
14.4.1 api.http.ajax(options)
Sends an HTTP request.
ajax(options: {
url: string;
method?: string;
data?: any;
headers?: Record<string, string>;
timeout?: number;
dataType?: 'json' | 'text' | 'html' | 'arrayBuffer';
contentType?: string;
processData?: boolean;
}): Promise<{
ok: boolean;
status: number;
statusText: string;
data?: any;
error?: string;
timeout?: boolean;
}>Parameters:
| Option | Type | Default | Description |
|---|---|---|---|
url |
string |
- | Request URL. |
method |
string |
GET |
HTTP method. |
data |
any |
- | Request data. GET and HEAD requests are serialized into the query string; other methods write it into the request body. |
headers |
Record<string, string> |
{} |
Request headers. |
timeout |
number |
- | Timeout in milliseconds. Only takes effect when greater than 0. |
dataType |
'json' \| 'text' \| 'html' \| 'arrayBuffer' |
json |
Response parsing mode. |
contentType |
string |
application/x-www-form-urlencoded; charset=UTF-8 |
Request body Content-Type. |
processData |
boolean |
true |
Whether to serialize data automatically. When set to false, data is passed directly as the request body. |
Return value:
| Field | Type | Description |
|---|---|---|
ok |
boolean |
true for HTTP 2xx responses; false for parse errors, HTTP errors, timeouts, aborts, or network errors. |
status |
number |
HTTP status code. 0 means timeout, abort, or network-layer failure. |
statusText |
string |
HTTP status text. For non-HTTP failures, it is timeout, abort, or error. |
data |
any |
Parsed response data. |
error |
string |
Error message for parse failures or non-HTTP failures. |
timeout |
boolean |
true when the request was aborted because of the configured timeout. |
GET request example:
const ret = await api.http.ajax({
url: 'https://example.com/api/profile',
method: 'GET',
dataType: 'json',
timeout: 10000
});
if (ret.ok) {
console.log(ret.data);
}POST request example:
const ret = await api.http.ajax({
url: 'https://example.com/api/items',
method: 'POST',
contentType: 'application/json',
data: { name: 'demo' }
});
if (!ret.ok) {
console.warn(ret.status, ret.error || ret.statusText);
}14.5 api.user
api.user reads and writes user-related data during script execution. It can isolate storage scope across different dimensions.
Common parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
site |
boolean |
false |
Whether to isolate by site |
account |
boolean |
false |
Whether to isolate by site account or workspace name |
did |
boolean |
false |
Whether to isolate by device |
Dimension recommendations:
| Scenario | Recommendation |
|---|---|
| Share data for the same user across sites | site=false |
| Store data independently for each site | site=true |
| Isolate different accounts under the same site | account=true |
| Isolate the same user across different devices | did=true |
14.5.1 api.user.put()
Saves a key-value pair.
put(
name: string,
value: string,
site?: boolean,
account?: boolean,
did?: boolean
): Promise<{ status: boolean }>Example:
await api.user.put('token', 'abc123');
await api.user.put('draft:lastOrderId', 'A-1001', true, true);14.5.2 api.user.get()
Reads a specified key.
get(
name: string,
site?: boolean,
account?: boolean,
did?: boolean
): Promise<{ value: string | null, status: boolean }>Example:
const ret = await api.user.get('token');
if (ret.status && ret.value) {
console.log(ret.value);
}14.5.3 api.user.remove()
Deletes a specified key.
remove(
name: string,
site?: boolean,
account?: boolean,
did?: boolean
): Promise<{ status: boolean }>Example:
await api.user.remove('token');
await api.user.remove('draft:lastOrderId', true, true);14.5.4 api.user.incr()
Increments the numeric value of a specified key by a step. If the key does not exist, it is created.
incr(
name: string,
step?: number,
site?: boolean,
account?: boolean,
did?: boolean
): Promise<{ status: boolean, value: number | string }>Example:
const ret = await api.user.incr('submitCount', 1, true);
console.log(ret.value);14.5.5 api.user.decr()
Decrements the numeric value of a specified key by a step. If the key does not exist, it is created with a negative initial value.
decr(
name: string,
step?: number,
site?: boolean,
account?: boolean,
did?: boolean
): Promise<{ status: boolean, value: number | string }>Example:
const ret = await api.user.decr('remainingQuota', 1, true, true);
console.log(ret.value);14.5.6 api.user.startsWith()
Finds data by key-name prefix.
startsWith(
prefix: string,
site?: boolean,
account?: boolean,
did?: boolean
): Promise<Array<{ name: string, value: string }>>Example:
const items = await api.user.startsWith('cache:', true);
for (const item of items) {
console.log(item.name, item.value);
}14.5.7 api.user.countAll()
Counts records for a specified key name.
countAll(
name: string,
site?: boolean,
account?: boolean
): Promise<{ value: number, status: boolean }>Example:
const ret = await api.user.countAll('token', true);
console.log(ret.value);14.5.8 api.user.sumAll()
Calculates the numeric sum of values for a specified key name.
sumAll(
name: string,
site?: boolean,
account?: boolean
): Promise<{ value: number, status: boolean }>Example:
const ret = await api.user.sumAll('score', true);
console.log(ret.value);14.6 api.dom
api.dom provides DOM query, visibility checking, connection listeners, resize listeners, and overlay creation.
Selector support:
| Syntax | Description |
|---|---|
.button.primary |
CSS selector |
xpath://div[@id="app"] |
XPath selector |
.dialog:p |
Returns the parent element of the matched element |
.dialog:p2 |
Returns the ancestor two levels above the matched element |
.header:bottom |
Uses the lower boundary of an element in overlay boundary methods |
.sidebar:right |
Uses the right boundary of an element in overlay boundary methods |
See also Section 12.15.
14.6.1 api.dom.createMutationObserver()
Creates and caches a MutationObserver. If an observer with the same binding name already exists on the target element, the existing observer is returned directly.
createMutationObserver(
ele: Element,
bindStr: string,
childList: boolean,
subtree: boolean,
attributes: boolean,
characterData: boolean,
fn: (mutations: MutationRecord[]) => void
): MutationObserverExample:
api.dom.createMutationObserver(
document.body,
'__bodyObserver__',
true,
true,
false,
false,
(mutations) => console.log(mutations)
);14.6.2 api.dom.querySelector()
Queries the first matching element.
querySelector(doc: Document, cssOrXPathSelector: string): HTMLElement | nullExample:
const el = api.dom.querySelector(
document,
'xpath://button[contains(.,"Submit")]'
);14.6.3 api.dom.querySelectorAll()
Queries all matching elements.
querySelectorAll(doc: Document, cssOrXPathSelector: string): HTMLElement[]Example:
const buttons = api.dom.querySelectorAll(document, 'button.primary');
buttons.forEach((button) => console.log(button.textContent));14.6.4 api.dom.isVisible()
Checks whether an element is in a visible intersection area.
isVisible(ele: HTMLElement): Promise<boolean>Example:
const el = api.dom.querySelector(document, '.submit');
if (el && await api.dom.isVisible(el)) {
console.log('visible');
}14.6.5 api.dom.getVisibleRect()
Gets the element’s current visible rectangle.
getVisibleRect(ele: HTMLElement): Promise<DOMRectReadOnly>Example:
const el = api.dom.querySelector(document, '.panel');
if (el) {
const rect = await api.dom.getVisibleRect(el);
console.log(rect.left, rect.top, rect.width, rect.height);
}14.6.6 api.dom.getConnectListeners()
Gets the current connection listener list.
getConnectListeners(): Array<{
querySelector: string;
callback: (isConnected: boolean) => void;
isConnected?: boolean;
}>Example:
api.dom.addConnectListener('.modal', () => {});
console.log(api.dom.getConnectListeners());14.6.7 api.dom.addConnectListener()
Listens for an element appearing in or disappearing from the document.
addConnectListener(
cssOrXPathSelector: string,
callback: (isConnected: boolean) => void
): voidExample:
api.dom.addConnectListener('.dialog', (isConnected) => {
console.log('dialog:', isConnected);
});14.6.8 api.dom.removeConnectListener()
Removes connection listeners for specified selectors.
removeConnectListener(cssOrXPathSelectors: string[]): voidExample:
api.dom.removeConnectListener(['.dialog', '.toast']);14.6.9 api.dom.addResizeListener()
Listens for size and position changes of the target element. If the element does not exist, the callback receives an empty rectangle.
addResizeListener(
cssOrXPathSelector: string,
bindWindowStr: string,
callback: (rect: DOMRect) => void,
createObserver?: boolean,
delayTime?: number
): ResizeObserver | (() => void)Example:
api.dom.addResizeListener('.target', '__targetResize__', (rect) => {
console.log(rect.width, rect.height);
});14.6.10 api.dom.createOverlayBy()
Creates a fixed-position overlay that follows the visible area of a target element.
createOverlayBy(
cssOrXPathSelector: string,
bindWindowStr: string,
createObserver?: boolean,
delayTime?: number,
fn?: (rect: DOMRectReadOnly) => void
): HTMLElementExample:
const overlay = api.dom.createOverlayBy('.target', '__overlay__');
overlay.style.border = '2px solid #f00';
overlay.style.pointerEvents = 'none';
overlay.style.zIndex = '999999';14.6.11 api.dom.createOverlayByBorder()
Creates a fixed-position overlay from top, right, bottom, and left boundaries. Boundary values can be pixel numbers or selectors.
createOverlayByBorder(
bindWindowStr: string,
top: string | number,
right: string | number,
bottom: string | number,
left: string | number,
createObserver?: boolean,
delayTime?: number
): HTMLElementExample:
const panel = api.dom.createOverlayByBorder(
'__centerPanel__',
'header:bottom',
20,
'footer:top',
'.sidebar:right'
);
panel.style.background = 'rgba(0,0,0,.08)';14.7 api.utils
api.utils provides common helper capabilities for waiting on page conditions and executing code in the page context.
14.7.1 api.utils.wait()
Polls until the condition function returns a truthy value. Throws an error on timeout.
wait(
fn: () => boolean,
timeoutMs: number,
intervalMs?: number
): Promise<void>Example:
await api.utils.wait(
() => !!api.dom.querySelector(document, '.ready'),
10000,
200
);14.7.2 api.utils.runScript()
Runs JavaScript code in the current page and returns the result.
runScript(
code: string,
callback?: (result: any, error: Error) => void
): Promise<any>Example:
const title = await api.utils.runScript('document.title');
console.log(title);Callback form:
await api.utils.runScript('document.body.innerText.slice(0, 100)', (result, error) => {
if (error) {
console.error(error);
return;
}
console.log(result);
});14.8 api.header()
api.header(headerName, isRequestHeader) reads request or response headers recorded by the remote browser.
header(
headerName: string,
isRequestHeader: boolean
): Promise<string | string[] | undefined>Parameters:
| Parameter | Type | Description |
|---|---|---|
headerName |
string |
Request or response header name. It is converted to lowercase when read |
isRequestHeader |
boolean |
true reads request headers; false reads response headers |
Example:
const cookie = await api.header('cookie', true);
const setCookie = await api.header('set-cookie', false);Notes:
- Only request or response headers that have been added in site configuration are recorded.
- Request headers come from requests sent by the remote browser.
- Response headers come from responses received by the remote browser.
- Response headers may return a string array.
14.9 Common Script Combinations
14.9.1 Wait for an Element and Click It
await api.utils.wait(
() => !!api.dom.querySelector(document, '.login-button'),
10000,
200
);
const button = api.dom.querySelector(document, '.login-button');
button?.click();14.9.3 Save Page State
const count = await api.user.incr('visitCount', 1, true, true);
await api.user.put('lastTitle', document.title, true, true);
console.log(count.value);14.9.4 Add an Overlay to an Area of Interest
const overlay = api.dom.createOverlayBy('.customer-phone', '__phoneMask__');
overlay.style.background = '#fff';
overlay.style.pointerEvents = 'none';
overlay.style.zIndex = '999999';14.10 Usage Principles
Manage scripts like business code:
- Limit matching URLs so scripts do not run on unrelated pages.
- Give scripts clear names for rollback and troubleshooting.
- Verify first on a test site or test account.
- Regression-test scripts that depend on DOM selectors.
- Use clear prefixes for stored data, such as
cache:,draft:, andstate:. - Do not hard-code secrets, long-lived tokens, or account passwords into scripts.
- Record the reason for script changes and perform code review when necessary.
14.11 Troubleshooting Checklist
| Problem | What to check |
|---|---|
api does not exist |
Confirm whether the script type supports window.api; SSE scripts should not depend on it |
| Element cannot be found | Check whether the URL matches, the page has finished loading, and the selector has changed |
| XPath does not work | Confirm that the xpath:// prefix is used |
| Header cannot be read | Add the corresponding request or response header name in site configuration first |
| User data mixes across scopes | Check the site, account, and did dimension parameters |
| Overlay position is abnormal | Check whether the target element is visible and whether boundary selectors are correct |
| Script affects other pages | Narrow the matching URL or add page-condition checks at the start of the script |