ДокументаціяАвтоматизація
Автоматизація скриптами
Скрипт на тому самому комп’ютері може відкрити профіль і взяти на себе його браузер. Він отримує той самий профіль, який ви відкриваєте вручну, — з тим самим відбитком, з’єднанням і cookie.
Інтерфейс застосунку є лише англійською та російською. Кнопки й налаштування названо тут так, як їх показує англійський інтерфейс.
Що таке локальний API
Локальний API — невелика служба всередині застосунку. Вона відповідає лише на цьому комп’ютері й лише на запити з вашим ключем. Через неї скрипт може отримати список профілів, запускати й зупиняти їх і дізнатися адресу, за якою бібліотека автоматизації під’єднується до запущеного браузера.
- Будь-який тариф, безкоштовний теж.
- Лише цей комп’ютер: служба слухає
127.0.0.1, і відкрити її в мережу не можна. - Вимкнена, доки ви її не ввімкнете.
- Профілі створюються в застосунку. API запускає ті профілі, що у вас є; створювати й змінювати їх він не вміє.
Увімкнути
- Відкрийте Settings › API & Automation і ввімкніть Local API. Рядок поруч зміниться на Running on this computer only.
- Залиште Port —
48361, якщо ви його не змінювали, — або натисніть Random free port. - Одразу скопіюйте API key кнопкою Copy: сам ключ не показується, а після виходу зі сторінки скопіювати його вже не можна — лише замінити. Його довжина — 64 символи.

- Address
- Де відповідає служба, з кнопкою Copy — типово
http://127.0.0.1:48361. - Regenerate…
- Створює новий ключ. Старий одразу перестає працювати, а з ним і адреси браузерів, видані з цим ключем.
- API documentation
- Відкриває довідник.
Примітка. Ключ зберігається в системному сховищі облікових даних, а не у файлі. Скрипт має читати адресу з файлу, який записує застосунок, — Local API/status.json у папці даних Liminal, — а не прописувати порт у коді; довідник пояснює, де цей файл.
Перший запит
Кожен запит несе ключ у заголовку. Запитайте службу, чи вона працює:
curl -H 'Authorization: Bearer <key>' http://127.0.0.1:48361/v1/status{"status":"running","version":"<version>","signedIn":true}signedIn показує, чи хтось увійшов у застосунок. Операціям, що працюють із профілями, це потрібно: без цього вони відповідають not_signed_in.
Знайти профіль і запустити його
Отримайте список профілів — відповідь надходить сторінками, її можна фільтрувати за назвою, робочим простором, папкою і статусом:
curl -H 'Authorization: Bearer <key>' http://127.0.0.1:48361/v1/profilesВізьміть id потрібного профілю і запустіть його:
curl -X POST -H 'Authorization: Bearer <key>' http://127.0.0.1:48361/v1/profiles/<profile-id>/start{
"id": "<profile-id>",
"status": "running",
"cdpDiscoveryUrl": "http://127.0.0.1:48361/devtools/<token>/json/version",
"cdpBrowserUrl": "ws://127.0.0.1:9222/devtools/browser/<token>",
"seleniumDebuggerUrl": "127.0.0.1:9222",
"chromedriverPath": "/Applications/Liminal.app/.../chromedriver",
"debugPort": 9222,
"debugAddress": "127.0.0.1:9222",
"externalIp": "203.0.113.10",
"countryCode": "DE"
}cdpBrowserUrl- Адреса, до якої під’єднуються Puppeteer і Playwright.
seleniumDebuggerUrl,chromedriverPath- Те, що потрібно Selenium: адреса налагоджувача і драйвер, який відповідає браузеру й постачається із застосунком.
externalIp,countryCode- Яким було з’єднання профілю на момент запуску.
Застосунок запускає профіль так, ніби ви натиснули Open. Уже відкритий профіль не запускається вдруге — довідник описує, як під’єднатися до запущеного.
Під’єднати бібліотеку
Кожен приклад цілий: він читає файл з адресою, знаходить профіль за назвою, запускає його і відкриває сторінку.
import { readFileSync } from 'node:fs'
import puppeteer from 'puppeteer-core'
// Local API/status.json, inside Liminal's user-data directory. The settings
// screen shows the absolute path; that path and the key are your script’s to
// keep — the app puts neither into the environment for you.
const { address } = JSON.parse(readFileSync(process.env.LIMINAL_ADDRESS_FILE, 'utf8'))
async function call(path, init) {
const response = await fetch(address + path, {
...init,
headers: { Authorization: `Bearer ${process.env.LIMINAL_API_KEY}` },
})
const body = await response.json()
if (!response.ok) throw new Error(body.error.code)
return body
}
const { items } = await call('/v1/profiles?name=' + encodeURIComponent('Shop EU'))
const started = await call(`/v1/profiles/${items[0].id}/start`, { method: 'POST' })
const browser = await puppeteer.connect({ browserWSEndpoint: started.cdpBrowserUrl })
const page = await browser.newPage()
await page.goto('https://example.com')
console.log(await page.title())
await browser.disconnect()import { readFileSync } from 'node:fs'
import { chromium } from 'playwright'
// Local API/status.json, inside Liminal's user-data directory. The settings
// screen shows the absolute path; that path and the key are your script’s to
// keep — the app puts neither into the environment for you.
const { address } = JSON.parse(readFileSync(process.env.LIMINAL_ADDRESS_FILE, 'utf8'))
async function call(path, init) {
const response = await fetch(address + path, {
...init,
headers: { Authorization: `Bearer ${process.env.LIMINAL_API_KEY}` },
})
const body = await response.json()
if (!response.ok) throw new Error(body.error.code)
return body
}
const { items } = await call('/v1/profiles?name=' + encodeURIComponent('Shop EU'))
const started = await call(`/v1/profiles/${items[0].id}/start`, { method: 'POST' })
const browser = await chromium.connectOverCDP(started.cdpBrowserUrl)
const page = await browser.contexts()[0].newPage()
await page.goto('https://example.com')
console.log(await page.title())
await browser.close()import json
import os
import urllib.parse
import urllib.request
from playwright.sync_api import sync_playwright
# Local API/status.json, inside Liminal's user-data directory. The settings
# screen shows the absolute path; that path and the key are your script’s to
# keep — the app puts neither into the environment for you.
with open(os.environ["LIMINAL_ADDRESS_FILE"], encoding="utf-8") as handle:
address = json.load(handle)["address"]
def call(path, method="GET"):
request = urllib.request.Request(address + path, method=method)
request.add_header("Authorization", "Bearer " + os.environ["LIMINAL_API_KEY"])
with urllib.request.urlopen(request) as response:
return json.load(response)
profile = call("/v1/profiles?name=" + urllib.parse.quote("Shop EU"))["items"][0]
started = call(f"/v1/profiles/{profile['id']}/start", method="POST")
with sync_playwright() as playwright:
browser = playwright.chromium.connect_over_cdp(started["cdpBrowserUrl"])
page = browser.contexts[0].new_page()
page.goto("https://example.com")
print(page.title())import json
import os
import urllib.parse
import urllib.request
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
# Local API/status.json, inside Liminal's user-data directory. The settings
# screen shows the absolute path; that path and the key are your script’s to
# keep — the app puts neither into the environment for you.
with open(os.environ["LIMINAL_ADDRESS_FILE"], encoding="utf-8") as handle:
address = json.load(handle)["address"]
def call(path, method="GET"):
request = urllib.request.Request(address + path, method=method)
request.add_header("Authorization", "Bearer " + os.environ["LIMINAL_API_KEY"])
with urllib.request.urlopen(request) as response:
return json.load(response)
profile = call("/v1/profiles?name=" + urllib.parse.quote("Shop EU"))["items"][0]
started = call(f"/v1/profiles/{profile['id']}/start", method="POST")
options = webdriver.ChromeOptions()
options.debugger_address = started["seleniumDebuggerUrl"]
driver = webdriver.Chrome(service=Service(started["chromedriverPath"]), options=options)
driver.get("https://example.com")
print(driver.title)
driver.quit()import { readFileSync } from 'node:fs'
import { Builder } from 'selenium-webdriver'
import chrome from 'selenium-webdriver/chrome.js'
// Local API/status.json, inside Liminal's user-data directory. The settings
// screen shows the absolute path; that path and the key are your script’s to
// keep — the app puts neither into the environment for you.
const { address } = JSON.parse(readFileSync(process.env.LIMINAL_ADDRESS_FILE, 'utf8'))
async function call(path, init) {
const response = await fetch(address + path, {
...init,
headers: { Authorization: `Bearer ${process.env.LIMINAL_API_KEY}` },
})
const body = await response.json()
if (!response.ok) throw new Error(body.error.code)
return body
}
const { items } = await call('/v1/profiles?name=' + encodeURIComponent('Shop EU'))
const started = await call(`/v1/profiles/${items[0].id}/start`, { method: 'POST' })
const driver = await new Builder()
.forBrowser('chrome')
.setChromeOptions(new chrome.Options().debuggerAddress(started.seleniumDebuggerUrl))
.setChromeService(new chrome.ServiceBuilder(started.chromedriverPath))
.build()
await driver.get('https://example.com')
console.log(await driver.getTitle())
await driver.quit()Порада. Під’єднуйтеся до браузера, який уже є в профілю. Не дозволяйте бібліотеці запускати власний браузер — у ньому не буде ні відбитка, ні cookie профілю.
Зупинити профіль
curl -X POST -H 'Authorization: Bearer <key>' http://127.0.0.1:48361/v1/profiles/<profile-id>/stopЗупинка закриває профіль так само, як Close. Є й примусова зупинка — для профілю, який не закривається.
Корисно знати
| Питання | Відповідь |
|---|---|
| Чи бачить скрипт інший браузер, ніж я? | Ні. Відбиток і з’єднання застосовуються точно так само, як коли ви відкриваєте профіль вручну, і діють ті самі захисти. |
| Чи бачать сайти, що браузер автоматизовано? | Звичні ознаки автоматизації приховано для профілю, запущеного через API. Вимкнути це не можна. |
| Чи є режим без вікна (headless)? | Ні. API запускає профіль так само, як застосунок. |
| Чи є ліміт на кількість запитів? | Квоти, прив’язаної до тарифу, немає. |
| Чи є інструмент командного рядка або записані сценарії? | Ні. Спосіб автоматизації — локальний API. |
| Що я отримаю, якщо щось пішло не так? | Статус і короткий code, за яким можна розгалужувати логіку, — invalid_key, not_signed_in, profile_already_running та інші. У довіднику їх перелічено для кожної операції. |
Звірено з випущеним застосунком: жовтень 2026. Якщо застосунок поводиться не так, як тут написано, — помиляється сторінка: напишіть на [email protected]. Що змінювалося в кожній версії — у розділі «Що нового».