注册即送 1 个月专业版(价值 $10)加独享纯净美国 ISP 节点,联系微信客服领取

Automation API

Use Merca's local HTTP API to manage stores, nodes, team members, and credentials, then launch browsers and connect over CDP for web automation.

Use Merca's automation API to manage stores, network nodes, team members, and saved credentials, and to launch store browsers.

Reading pages, clicking, typing, and taking screenshots require CDP or a compatible automation tool after the browser starts. These actions are not HTTP endpoints in this API.

1. Before you start

  1. Open the Merca desktop app and sign in.
  2. Make sure your account has a paid plan.
  3. Enable the automation API in the app's settings, copy the API key, and check the port.
  4. Before launching a store, make sure the browser engine is ready and the store has a working node assigned.
  5. Keep the desktop app running and connected to the internet. The API uses the app's signed-in session; it does not provide a separate sign-in endpoint.

Default connection settings:

SettingDefault
HTTP base URLhttp://127.0.0.1:43210
Automation serverDisabled
Listen address127.0.0.1; changes to 0.0.0.0 when remote access is enabled
Port range1–65535, configurable in settings

For remote access, replace 127.0.0.1 with the address of the computer running Merca. Browser processes, debugging ports, and local file paths returned by the API always belong to that computer, not the caller's computer.

2. Conventions

2.1 Authentication

Every API request except GET /health requires this header:

Authorization: Bearer <API_KEY>

For HTTP requests with a body, also send:

Content-Type: application/json

The API key grants access to the local automation server. It does not replace account sign-in or grant additional account permissions.

2.2 Requests and responses

  • Request bodies must be JSON objects, not arrays, strings, or null.
  • Request bodies are limited to 256 KiB. An oversized request may have its connection closed directly.
  • All successful endpoints listed here return 200, including creation and deletion.
  • Responses use JSON and include Cache-Control: no-store.
  • Store, node, member, and credential path IDs are UUIDs. Invalid path IDs return 404.
  • Replace :id, :passwordId, :otpId, and :passkeyId with actual IDs.
  • List endpoints do not support general pagination parameters. Activity lists return up to 200 recent entries.
  • Store, node, and member PATCH requests preserve omitted fields. Clearing rules are described below. Do not use null in place of an omitted field.

Error response:

{
  "error": "Error description",
  "code": "not_found"
}

Handle errors using the HTTP status and code, not the wording or language of error.

2.3 Permissions

  • Creating or deleting stores, accessing node endpoints, and managing the team require an owner or admin account. A member needs an explicit store grant.
  • Store read access allows launching the store and reading its saved credentials.
  • Store full access also allows editing the store and its saved credentials. It does not grant team-management permissions.
  • Creating a regular member does not grant access to any store. Assign store access separately.

3. Quick start

These commands use Bash/zsh syntax. Replace the API key and store ID with your own values.

export MERCA_API_BASE='http://127.0.0.1:43210'
export MERCA_API_KEY='<API_KEY_FROM_MERCA_SETTINGS>'

curl -sS "$MERCA_API_BASE/health"

curl -sS "$MERCA_API_BASE/v1/engine" \
  -H "Authorization: Bearer $MERCA_API_KEY"

curl -sS "$MERCA_API_BASE/v1/stores" \
  -H "Authorization: Bearer $MERCA_API_KEY"

export STORE_ID='<STORE_UUID>'

curl -sS -X POST "$MERCA_API_BASE/v1/stores/$STORE_ID/launch" \
  -H "Authorization: Bearer $MERCA_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"startupUrl":"https://example.com","extraArgs":["--remote-debugging-port=9222"]}'

A successful launch returns:

{"ok": true}

This confirms that the browser process started, not that a page, website login, or CDP endpoint is ready. Once the debugging port is available, query it with:

curl -sS http://127.0.0.1:9222/json/version

Connect your automation tool using the returned webSocketDebuggerUrl, or the CDP HTTP address if your tool supports it. Port 43210 is the Merca API port; 9222 is the example browser debugging port. Use a different debugging port for each browser when running multiple stores in parallel.

Before using a member account for CDP page automation, turn off that member's “Disable developer tools” setting and restart the store browser.

Close the store launched through the API:

curl -sS -X POST "$MERCA_API_BASE/v1/stores/$STORE_ID/close" \
  -H "Authorization: Bearer $MERCA_API_KEY"

4. Service, account, and platforms

MethodPathPurposeSuccess response
GET/healthServer health; no authentication required{ "ok": true }
GET/v1/engineBrowser engine status{ "ready": true, "version": "..." }
GET/v1/sessionDesktop sign-in session status{ "session": "active" }
GET/v1/account/entitlementsAccount entitlements{ "entitlements": { ... } }
GET/v1/platformsPlatform and site catalog{ "platforms": [ ... ] }
GET/v1/activityCurrent account's recent activity{ "entries": [ ... ] }

4.1 Engine and session status

The version returned by GET /v1/engine is the browser engine version, not the desktop app version. It is an empty string when unavailable. ready does not indicate whether any store is running or whether a debugging port is available.

GET /v1/session returns one of these session values:

ValueMeaning
activeThe session is valid
revokedThe session has been revoked
unknownThe status could not be determined, for example because of a failed or timed-out request

A successful /health response does not confirm account sign-in, permissions, or engine readiness.

4.2 Account entitlements

The entitlements object contains:

FieldTypeDescription
tierstringPlan identifier
maxStoresnumberStore limit
usedStoresnumberStores currently counted toward the limit
renewsAtstring or nullRenewal time
cancelAtstring or nullCancellation time
statusstring or nullSubscription status

4.3 Platform catalog

Filter by a single platform with the optional platformId query parameter:

GET /v1/platforms?platformId=<PLATFORM_ID>

Response shape:

{
  "platforms": [
    {
      "id": "<PLATFORM_ID>",
      "name": "Platform name",
      "sites": [
        {"id": "<SITE_ID>", "name": "Site name"}
      ]
    }
  ]
}

Names follow the app's current language. Use the returned IDs, not display names, when creating a store. Filtering by an unknown platform returns 404.

4.4 Activity

GET /v1/activity and GET /v1/members/:id/activity return the same structure: up to 200 entries, newest first.

{
  "entries": [
    {
      "id": "<ACTIVITY_UUID>",
      "action": "Activity type",
      "details": {},
      "createdAt": "2026-09-17T08:00:00.000Z"
    }
  ]
}

details contains action-specific fields with string or boolean values.

5. Stores

MethodPathPurposeSuccess response
GET/v1/storesList stores visible to the current account{ "stores": [...] }
POST/v1/storesCreate a store{ "store": {...} }
GET/v1/stores/:idGet store details{ "store": {...} }
PATCH/v1/stores/:idUpdate a store{ "store": {...} }
DELETE/v1/stores/:idDelete a store and its corresponding local data and cache{ "ok": true }

5.1 Create and update fields

FieldTypeRequired on creationDescription
namestringYesNonempty after trimming; up to 120 characters
platformIdstringYesPlatform ID; cannot be changed after creation
siteIdsstring[]YesAt least one site ID; updates replace the entire array
usernamestringNoStore login account; up to 320 characters; defaults to an empty string. Nonempty values must be unique among undeleted stores on the same platform within the team
passwordstringNoStore login password; up to 512 characters; defaults to an empty string
nodeIdstringNoNode UUID; an empty string leaves the store unassigned or removes its assignment
fingerprintobjectNoFingerprint settings, listed below
appearanceLabelstringNoAppearance label; up to 120 characters; defaults to an empty string
appearanceBackgroundColorstringNoAppearance background color; defaults to an empty string
appearanceForegroundColorstringNoAppearance foreground color; defaults to an empty string

You can create a store without a node, but a node must be assigned before launching it or generating a launch command.

The fingerprint object accepts:

FieldTypeCreation defaultDescription
browserVersionstring""Reported version, up to 32 characters; empty or auto selects the highest available version matching the engine's major version
hardwareConcurrencynumberRandomCPU thread count; integer from 1 to 256
cpuPerformancenumberFrom random presetCPU performance tier; integer from 0 to 4
deviceMemorynumberRandomReported memory: 2, 4, 8, 16, or 32 GB; does not allocate physical memory
speechVoicesModestringoverridenone: no override; override: full override; append_google: append Google voices only
fontAllowlistOverridebooleantrueApply the preset font allowlist for the host system
disableCanvasNoisebooleanfalseDisable pixel noise in Canvas 2D readback and image exports; noise stays enabled by default. Does not affect WebGL, WebGPU, or other noise. Relaunch after changes
webrtcIpPolicystringdisable_non_proxied_udpWebRTC IP policy

Each creation randomly selects 8/12/16/20/24/32 CPU threads and 16/32 GB of memory. The random preset uses performance tier 3 for 8 threads and 4 otherwise. Explicit fields override the corresponding preset values. Updates preserve omitted fields, and launches do not randomize them again.

cpuPerformance tiers: 0 = Unknown, 1 = Low, 2 = Mid, 3 = High, 4 = Ultra.

Supported webrtcIpPolicy values:

  • disable_non_proxied_udp
  • default_public_interface_only
  • default_public_and_private_interfaces
  • default

UDP-enabled modes may expose direct IP addresses instead of the node IP. Close and relaunch the browser after changes.

Omitted fingerprint fields use the defaults above when creating a store. Updating fingerprint changes only the supplied subfields.

Example creation body; replace platform, site, and node IDs with values obtained from the API:

{
  "name": "Store A",
  "platformId": "<PLATFORM_ID>",
  "siteIds": ["<SITE_ID>"],
  "username": "seller@example.com",
  "password": "<STORE_PASSWORD>",
  "nodeId": "<NODE_UUID>",
  "fingerprint": {
    "hardwareConcurrency": 8,
    "deviceMemory": 8,
    "webrtcIpPolicy": "disable_non_proxied_udp"
  }
}

Example partial update:

PATCH /v1/stores/:id
{
  "name": "Store B",
  "fingerprint": {"hardwareConcurrency": 4}
}

5.2 Store response objects

Each store in a list response contains:

  • id, name, platformId, siteIds, username
  • nodeId, nodeBound
  • fingerprint
  • appearanceLabel, appearanceBackgroundColor, appearanceForegroundColor
  • createdAt, updatedAt, and optional lastLaunchedAt

nodeBound indicates whether nodeId is set. It does not indicate node connectivity or browser status.

Detail, creation, and update responses also include:

FieldTypeDescription
passwordstringStore login password
nodeobject or nullAssigned node summary, or null if none is assigned

The node summary contains name, ip, country, timezone, and acceptLanguages. It does not include the proxy password; use the node endpoints for connection details.

Store login fields are separate from the saved website password collection in section 9.

6. Browser lifecycle

MethodPathPurposeSuccess response
POST/v1/stores/:id/launchLaunch the store browser{ "ok": true }
POST/v1/stores/:id/commandGet a launch command without starting the browser{ "executable": "...", "args": [...] }
POST/v1/stores/:id/closeClose a browser started through launch{ "ok": true }

6.1 Launch options

launch and command accept the same body. Send {} if no custom options are needed.

FieldTypeRequiredDescription
startupUrlstringNoURL to open at startup; an empty string is treated as unset
extraArgsstring[]NoAdditional Chromium command-line arguments; defaults to an empty array
{
  "startupUrl": "https://example.com",
  "extraArgs": ["--remote-debugging-port=9222"]
}

Launches include the app's global launch arguments, followed by this call's extraArgs; an empty array does not clear the global arguments. extraArgs cannot override the store data directory or browser lifecycle arguments managed by Merca.

launch and command both enforce login protection. If the store's login account conflicts with the node's account records, the API returns 400 node_account_conflict. Review and resolve the account records in the app before retrying.

A successful launch only confirms browser startup. Calling it again may close and restart the store browser. Do not use it to poll browser status.

6.2 Launch command

POST /v1/stores/:id/command

Response fields:

FieldTypeDescription
executablestringPath to the browser executable
argsstring[]Complete launch arguments

Pass executable and args directly to a process-spawning API. Do not concatenate them into a shell command or override the store data directory.

command does not start the browser, but it updates the store's last-launch time and activity records. If the store has a nonempty login account, it may also add a node account record, affecting whether other accounts can use the same IP on that platform. These effects occur even if you never execute the returned command.

For --remote-debugging-pipe, use command and have the caller start the process with the debugging pipes connected correctly. launch does not return pipes to the caller.

6.3 Lifecycle

  • Keep the Merca desktop app running while using the browser.
  • close only manages browsers started through launch, not those launched from the app UI or processes started by the caller using command.
  • If no API launch is available to close, close returns 404 with code not_running. The first close request can still succeed after the browser has exited on its own, so do not use this endpoint to check whether it is running.
  • Successful closure means a close request was issued, not that the process has exited.
  • Each store uses its own local data directory. Do not launch multiple instances of the same store concurrently.

7. Network nodes

MethodPathPurposeSuccess response
GET/v1/nodesList nodes without proxy passwords{ "nodes": [...] }
POST/v1/nodesCreate a node{ "node": {...} }
GET/v1/nodes/:idGet node details, including its proxy password{ "node": {...} }
PATCH/v1/nodes/:idUpdate a node{ "node": {...} }
DELETE/v1/nodes/:idDelete a node{ "ok": true }

Unassign the node from all stores before deleting it; otherwise the API returns 400 node_in_use.

7.1 Create and update fields

FieldTypeRequired on creationDefault or description
namestringNoEmpty string; up to 120 characters
protocolstringNohttp, https, or socks5; defaults to http
hoststringYesProxy hostname or IP address; up to 255 characters
portnumberNoInteger from 1 to 65535; defaults to 8080
usernamestringNoEmpty string; up to 512 characters
passwordstringNoEmpty string; up to 512 characters
acceptProxyChainbooleanNoWhether to use the app's configured proxy chain; defaults to true
ipstringNoKnown exit IP; must be provided together with country
countrystringNoTwo-letter country or region code, such as us
timezonestringNoIANA timezone for manually supplied probe data, such as America/New_York
region, city, asn, orgstringNoAdditional manually supplied probe data
latitude, longitudenumberNoCoordinates for manually supplied probe data
{
  "name": "US node A",
  "protocol": "socks5",
  "host": "proxy.example.com",
  "port": 1080,
  "username": "<PROXY_USERNAME>",
  "password": "<PROXY_PASSWORD>",
  "acceptProxyChain": true
}

Usage constraints:

  • An exit IP can only be added once, even across accounts. Duplicate additions return node_already_added.
  • Provide both ip and country, or omit both. Supplying only one returns node_probe_required.
  • Manually supplied geographic fields, such as timezone, require both ip and country. You can update them without changing connection details. A nonempty timezone must be a valid IANA timezone; empty uses the host system timezone.
  • When updating connection details or probe data, the exit IP must remain the same as the original node's IP. Otherwise the API returns node_ip_changed. Create a new node to use a different exit IP.

7.2 Node responses

Node details contain:

id, name, protocol, host, port, username, password, acceptProxyChain, ip, country, timezone, and acceptLanguages.

List responses omit password; creation, update, and detail responses include it. Additional geographic fields accepted in a request are not necessarily included in responses.

8. Team members and store access

8.1 Members

MethodPathPurposeSuccess response
GET/v1/membersList members{ "members": [...] }
POST/v1/membersCreate a member{ "member": {...}, "downloadUrl": "..." }
GET/v1/members/:idGet member details{ "member": {...} }
PATCH/v1/members/:idUpdate a member{ "member": {...}, "downloadUrl": "..." }
DELETE/v1/members/:idDelete a member{ "ok": true }
PUT/v1/members/:id/disabledSet the disabled state{ "member": {...} }
GET/v1/members/:id/activityGet member activity{ "entries": [...] }

Member endpoints do not include the team owner. Member paths use the member object's id, not its userId.

Create and update fields:

FieldTypeRequired on creationDescription
nicknamestringYesDisplay name; 1–32 characters
usernamestringYesTeam login account; 2–32 ASCII letters, digits, or underscores; trimmed, converted to lowercase, and unique within the team
passwordstringYesTeam login password; 8–128 characters; omitted or empty on update preserves the current password
rolestringNoadmin or member; defaults to member
disableDevtoolsbooleanNoDisable developer tools; defaults to true
passwordRevealBlockbooleanNoRestrict password display; defaults to true
{
  "nickname": "Operations 1",
  "username": "operator_01",
  "password": "<MEMBER_PASSWORD>",
  "role": "member",
  "disableDevtools": true,
  "passwordRevealBlock": true
}

Member objects contain id, userId, username, nickname, role, disableDevtools, passwordRevealBlock, disabled, and createdAt. They do not include the login password.

To disable a member, send:

{"disabled": true}

Send false to enable the member again. Disabling a member revokes their existing login sessions. You cannot change your own role, delete yourself, or call the disabled-state endpoint on yourself.

8.2 Store access grants

MethodPathPurposeSuccess response
GET/v1/stores/:id/membersList eligible members and their current access{ "members": [...] }
PUT/v1/stores/:id/membersReplace all explicit grants{ "members": [...] }

These endpoints require an owner or administrator and a paid plan. The list contains eligible member accounts only. Owners and administrators already have management access and are not included.

Each result contains userId, username, nickname, and access. Access is read, full, or null for no grant.

Replacement body:

{
  "grants": [
    {"userId": "<MEMBER_USER_UUID>", "access": "read"},
    {"userId": "<ANOTHER_MEMBER_USER_UUID>", "access": "full"}
  ]
}

Use userId here, not the member id used in member endpoint paths. Each user must appear in the eligible member list and must not be repeated.

This replaces the complete grant collection; it does not append. Existing grants omitted from the request are removed. Send { "grants": [] } to clear all regular-member grants for the store.

9. Saved website credentials

Reading credentials requires store read access. Writing or deleting them requires full access.

9.1 Website passwords

MethodPathPurposeSuccess response
GET/v1/stores/:id/passwordsList summaries without passwords{ "credentials": [...] }
POST/v1/stores/:id/passwordsSave a website password{ "ok": true }
GET/v1/stores/:id/passwords/:passwordIdGet a credential, including its password{ "credential": {...} }
PATCH/v1/stores/:id/passwords/:passwordIdUpdate an unlocked credential{ "ok": true }
DELETE/v1/stores/:id/passwords/:passwordIdDelete an unlocked credential{ "ok": true }
PUT/v1/stores/:id/passwords/:passwordId/lockedLock or unlock a credential{ "ok": true }

Save request:

{
  "origin": "https://example.com",
  "username": "seller@example.com",
  "password": "<WEBSITE_PASSWORD>"
}
FieldRules
originRequired HTTP/HTTPS URL; up to 2048 characters
usernameRequired and nonempty; up to 512 characters
passwordRequired and nonempty; up to 2048 characters

PATCH accepts one or more of these fields and requires at least one. Empty strings are not allowed. signon_realm is generated automatically and is not accepted as a request field.

Summaries contain id, origin, signon_realm, username, and locked. A single-credential response also contains password.

Saving updates an existing unlocked entry with the same sign-on realm and username. Locked entries cannot be overwritten. Saving returns no credential ID; query the list afterward to obtain it.

Lock request:

{"locked": true}

Send false to unlock. A lock prevents edits, deletion, and browser-sync overwrites. It does not prevent reading or using the password.

Changes are provided to the browser on a subsequent launch. Deletion does not change the website account or remove credentials already loaded by a running browser. Later browser sync may overwrite unlocked entries or recreate deleted ones.

9.2 TOTP codes

MethodPathPurposeSuccess response
GET/v1/stores/:id/otpList entry metadata{ "entries": [...] }
POST/v1/stores/:id/otpSave or update a TOTP entry{ "entry": {...} }
DELETE/v1/stores/:id/otp/:otpIdRemove an entry{ "ok": true }
GET/v1/stores/:id/otp/:otpId/codeGenerate the current code{ "id": "...", "code": "...", "expiresAt": "..." }

Save fields:

FieldTypeRequiredDescription
secretstringYesBase32 secret; up to 256 characters after removing whitespace and uppercasing; not an otpauth:// URL
labelstringNoUp to 120 characters; derived from issuer and account when omitted, or Authenticator if both are empty
issuerstringNoUp to 120 characters; defaults to an empty string
accountNamestringNoUp to 180 characters; defaults to an empty string
algorithmstringNoSHA1, SHA256, or SHA512; defaults to SHA1
digitsnumberNo6 or 8; defaults to 6
periodnumberNoPeriod in seconds; integer from 15 to 120; defaults to 30

List and save responses contain metadata only: id, label, issuer, accountName, algorithm, digits, and period. They do not return the secret.

Within the same store, the same normalized secret updates or restores the existing entry. A different secret creates a new entry. Omitted metadata and settings use defaults on each save; they do not preserve existing values.

Example code response:

{
  "id": "<OTP_UUID>",
  "code": "012345",
  "expiresAt": "2026-09-17T08:00:30.000Z"
}

code is a string; preserve leading zeroes. Codes use the clock on the computer running Merca. expiresAt is a UTC ISO timestamp. The API does not submit the code. Saving or deleting an entry does not enable or disable two-step verification on the website.

9.3 Passkeys

MethodPathPurposeSuccess response
GET/v1/stores/:id/passkeysList passkey metadata{ "entries": [...] }
DELETE/v1/stores/:id/passkeys/:passkeyIdRemove a saved entry{ "ok": true }

Each entry contains id, rpId, userName, and userDisplayName. Private keys are not returned.

The automation API has no endpoint for creating or importing passkeys. Deletion affects Merca's saved entry and subsequent launches. It does not unregister the credential on the website or remove copies already loaded by a running browser.

10. Common errors

HTTP statuscodeMeaning
400body_invalidInvalid JSON
400body_not_objectThe top-level request value is not an object
400input_invalidFingerprint, grants, passwords, or TOTP fields are invalid
400profile_name_requiredMissing store name
400platform_id_requiredMissing platform ID
400platform_id_immutableAttempt to change a store's platform
400site_ids_required / site_ids_invalidMissing or invalid site collection
400node_timezone_invalidNonempty node timezone is invalid
400startup_url_invalidInvalid startup URL type
400extra_args_invalidArguments are not a string array or include a protected argument
400node_host_requiredMissing node host
400node_port_invalidInvalid node port
400node_protocol_invalidUnsupported proxy protocol
400node_probe_requiredIncomplete node probe data
400node_probe_failedNode check failed
400node_ip_changedUpdated node exit IP differs from the original
400node_account_conflictThe store's login account conflicts with the node's account records
400node_in_useThe node is still assigned to a store and cannot be deleted
400node_already_addedThe exit IP has already been added
400organization_username_takenAnother member in the team already uses this login account
400store_username_takenAnother store on the same platform within the team already uses this login account
400account_lockedThe website password is locked and cannot be overwritten, edited, or deleted
400store_members_invalidIneligible or duplicate member in a grant request
400organization_member_disabled_invaliddisabled is not a boolean
400otp_code_failedFailed to generate a TOTP code
401unauthorizedMissing or incorrect API key
401not_authenticatedDesktop account is signed out or its session has expired
403paid_plan_requiredA paid plan is required
403forbiddenThe current account lacks permission
403store_quota_exceededStore limit reached
404not_foundResource or route not found, or an invalid path ID
404not_runningNo API launch available to close
405method_not_allowedUnsupported HTTP method
409profile_changedStore details changed during launch; refresh and try again
413body_too_largeRequest body exceeds the limit; the connection may close directly
500automation_api_errorRequest processing failed
503merca_console_session_not_readyThe desktop session is not ready
503browser_engine_not_readyThe browser engine is not ready

Related guides