Local API
Last checked against the contract: September 2026
In development. The local API is not in the build you can download today: in every released Liminal nothing listens and a connection is refused. This page is the contract it is being built to, published so a script can be written before it ships. It says nothing about a date.
A script on the same machine turns a profile’s browser into something it can drive. The first version of the interface does one thing — it answers whether Liminal is running and whether anyone is signed in — and everything after it, starting a profile and the addresses Puppeteer, Playwright and Selenium connect to, is built on top of the same service, the same key and the same address file described here.
Turning it on
Off by default, and reachable from this computer only.
- The switch
- Open
Settings → More Settings → API & Automationand turn on Local API. Until you do, nothing listens: the port is not opened, and a script gets a refused connection rather than an empty answer. - This computer only
- The service accepts connections on
127.0.0.1and nowhere else. Another machine on your network cannot reach it, and there is no setting that opens it up — a script runs beside Liminal, not against it over the network. - The port
- The default is
48361, a port of Liminal’s own so it does not collide with another browser already listening on the machine. Type a different one, or press Random free port and let the app pick. The change takes effect at once, without restarting Liminal. - When the port is taken
- If the port cannot be bound, the screen says Port in use and the address file reports
port_in_use. Nothing listens until you choose another port, so a script that reads the file knows the difference between "off" and "could not start".
The key
One key, shown once, kept where your operating system keeps secrets.
- Copy it when it appears
- Enabling the API issues a key of
64lowercase hexadecimal characters and shows it once. Leave the screen and it is hidden: the app cannot show it again, it can only issue a new one. - Where it is stored
- In Keychain on macOS and Credential Manager on Windows — never in a settings file, a sync snapshot or a log, and never in the address file below. Keep your copy the way you keep any other credential your script uses.
- Revoking it
- Regenerate key issues a new key and stops accepting the old one immediately, with no restart of the app and no change to the port. A script still holding the old key is refused from the next request on.
- Sending it
- Every request carries the key in one header:
Authorization: Bearerfollowed by the key itself. There is no query parameter and no cookie, and a request without the header is refused like a request with a wrong key.
Where a script finds the address
The app writes its address to a file, so nothing has to be typed twice.
- The file
- The settings screen shows the absolute path to
Local API/status.jsoninside Liminal’s user-data directory. Read it instead of hardcoding a port: it follows the port you set and the one Random free port picked. - What it holds
- Four fields —
enabled, status, address, port— and no secret of any kind.{"enabled":true,"status":"running","address":"http://127.0.0.1:48361","port":48361} - When the API is off
- The file stays where it is and says so:
offwith an empty address and no port. The same shape, a different state — a script reads one file to learn everything.{"enabled":false,"status":"off","address":"","port":0} - It is replaced, never half-written
- Every change — turning the API on or off, a new port, a port that could not be bound — rewrites the file atomically. A script that reads it during a change sees the old contents or the new ones, never a truncated line.
The status request
One request in this version: is Liminal running, and is anyone signed in.
- The call
- A GET to
/v1/statusat the address from the file, with the key in the header.curl -H 'Authorization: Bearer <key>' http://127.0.0.1:48361/v1/status - The answer
- HTTP
200, with the running build’s version and whether the app has someone signed in. A falsesignedInis not an error — the API answers, and the profile operations that come later are what will need an account.{"status":"running","version":"<version>","signedIn":true} - A missing or revoked key
- HTTP
401, and the codeinvalid_key— permanent, and the thing to branch on. The message beside it is the app’s own wording for a person reading a log; treat it as text that can change, not as part of the contract.{"error":{"code":"invalid_key","message":"Неверный ключ"}} - Address it exactly as the file does
- Use
127.0.0.1and the port from the file. A request that arrives naming any other host is not answered, even when it reaches the same port — the service replies to its own address only. - What an older Liminal does
- Nothing: no port is opened, so a script connecting to it is refused. That refusal is how a script tells a build without the local API from one that has it turned off — in the second case the address file exists and says
off.
What this version does and does not do
Said plainly, so nothing here reads as a promise it is not.
- Status, and nothing else yet
- Listing profiles, starting and stopping one, and the CDP address that Puppeteer, Playwright and Selenium connect to are the next step on this same service. They are not in this version, and this page grows when they arrive.
- Which plans it is for
- Every plan, the free one included. The local API is not a paid tier: a script on your own machine is how many people use an anti-detect browser at all, and putting it behind a plan would be a tax on that.Plans
- How often a script may ask
- There is no quota tied to your plan. The service protects itself from being flooded, which a script written for it will not notice.
- A profile under automation is the same profile
- It keeps its fingerprint, its proxy and its cookies, and it reaches the network through its proxy exactly as it does when you open it by hand. The API is a way in, not a second mode of the browser.Data & Security
The release that turns this page from a contract into a feature will be in the changelog. A question about the contract before then, or a value here that does not match what your build does: [email protected].