Kalkan Local API позволяет вашим собственным скриптам управлять профилями, прокси и папками, запускать и останавливать браузеры и управлять ими через Playwright, Puppeteer или Selenium. Это REST API, который предоставляет запущенное настольное приложение Kalkan на вашем компьютере:
http://127.0.0.1:50360/api/v1
Все запросы и ответы передаются в формате JSON. API входит в тарифы Starter, Pro и Unlimited (кроме пробного периода).
Включение и получение ключа
- Откройте Kalkan → Настройки → API и включите API.
- Измените порт, если 50360 на вашем компьютере занят.
- Скопируйте ключ API (он начинается с
kk_). Кнопка Перевыпустить создаёт новый ключ, а старый сразу перестаёт работать.
API доступен, пока открыто приложение Kalkan. Для серверов и скриптов без окна есть также команда для разработчиков adb api, которая предоставляет тот же API.
Аутентификация и безопасность
- Передавайте ключ в каждом запросе в заголовке
X-Token: <key>. Также принимаетсяAuthorization: Bearer <key>. - При отсутствии ключа или неверном ключе возвращается
401 unauthorized. После 10 неверных ключей в течение минуты API отвечает429 lockedв течение 60 секунд. - API слушает только
127.0.0.1и отвечает только на запросы, адресованные127.0.0.1илиlocalhost(иначе403 forbidden_host). С других компьютеров он недоступен. - API предназначен для скриптов, а не для веб-страниц: запросы с заголовком
Origin(его отправляют браузеры) отклоняются с403 forbidden_origin, CORS не поддерживается. - Ключ — единственный секрет. Не добавляйте его в репозитории кода; если он утёк, перевыпустите его.
- Если API не входит в ваш тариф, любой вызов возвращает
403 plan.GET /statusработает всегда и в полеapi_allowedсообщает, входит ли API в ваш тариф.
Ответы и ошибки
Успешный вызов возвращает {"ok": true, "data": …}; неудачный — {"ok": false, "error": {"code": "…", "message": "…"}}.
{"ok": true, "data": {"id": "…", "running": true, "ws": "ws://127.0.0.1:49731/devtools/browser/…", "debug_port": 49731, "pid": 12345}}
{"ok": false, "error": {"code": "unauthorized", "message": "missing or wrong API key (send it as the X-Token header)"}}
| Статус | Коды | Значение |
|---|---|---|
| 200 / 201 / 202 | Выполнено / создано / запущено в фоне (wait: false) | |
| 400 | bad_request | Отсутствует или некорректно поле |
| 401 | unauthorized | Ключ API отсутствует или неверен |
| 403 | plan, forbidden_host, forbidden_origin | Тариф без API, неверный Host, запрос с веб-страницы |
| 404 / 405 | not_found, method_not_allowed | Нет такого профиля / прокси / эндпоинта или неверный HTTP-метод |
| 409 | running, direct, ledger, cross_os | Профиль открыт, или для запуска требуется решение. direct: у профиля нет прокси (повторите с allow_direct: true). ledger: выходной IP прокси недавно использовался другими профилями; cross_os: ОС профиля не подходит для этого движка на данном компьютере (повторите с force: true). В сообщении поясняется конкретный случай. |
| 429 | locked | Слишком много неверных ключей, подождите 60 секунд |
| 500 | internal, launch_failed | Непредвиденная ошибка или браузер не удалось запустить |
| 504 | timeout | Браузер не запустился в течение timeout |
Движки
В Kalkan четыре движка: Kalkan Native (рекомендуется; наш собственный Chromium с более чем 150 патчами на C++, которые формируют отпечаток прямо в его коде; приложение устанавливает и обновляет его в фоне), Kalkan (патч Chrome: ваш настоящий Google Chrome с модифицированным драйвером), Kalkan Edge (тот же слой на установленном Microsoft Edge; при создании "engine": "edge") и Firefox (профили Windows, macOS и Linux). Профили Kalkan, Kalkan Edge и Kalkan Native запускаются с эндпоинтом DevTools (CDP) для автоматизации; профили Firefox запускаются без него (ws равен null).
Эндпоинты
Все пути указаны относительно http://127.0.0.1:50360/api/v1.
| Запрос | Что делает |
|---|---|
GET /status | Состояние приложения и API: app, version, api, api_allowed, plan, running (число открытых профилей). |
GET /profiles | Список профилей. Фильтры: folder, q (поиск), tag, engine (значение engine, которое профиль сообщает в этом списке), proxy (yes | no), status (running | used | new | frozen). Сортировка: sort (name | created | last_used | folder | engine | proxy), desc=1. Пагинация: page, page_size (по умолчанию 100, максимум 500). Возвращает {total, page, page_size, items}. |
POST /profiles | Создаёт профиль (201). Поля: name (обязательно), engine, os (windows | macos | linux), mode (native | balanced | maximum | gmail | custom; рекомендуется balanced), folder, tags, notes, start_url, color. Прокси: объект {type, host, port, username, password} (type: http | https | socks5), или URL вида "socks5://host:port" с proxy_user / proxy_pass, или pool_id (id сохранённого прокси или "auto"). |
GET /profiles/{id} | Один профиль со всеми данными. Пароли прокси никогда не возвращаются. |
PATCH /profiles/{id} | Изменяет name, notes, tags, color, start_url, folder, allow_direct, frozen, extensions, bookmarks, proxy (+ proxy_user, proxy_pass). Поля отпечатка и устройства изменить нельзя (400). |
DELETE /profiles/{id} | Удаляет профиль (409, пока он открыт). |
POST /profiles/{id}/clone | Копирует профиль, при необходимости с новым name (201). |
POST /profiles/{id}/start | Открывает профиль. Необязательные параметры: url, allow_direct, force, cdp (по умолчанию true), wait (по умолчанию true), timeout в секундах (5–180, по умолчанию 60). Возвращает id, name, engine, running, already_running, ws, cdp, debug_port, http, pid. При wait: false сразу отвечает 202 {starting: true}. |
POST /profiles/{id}/stop | Закрывает профиль: {stopped}. |
GET /profiles/{id}/status | running, ws, cdp, debug_port, warnings, errors. |
GET /profiles/{id}/cookies | Экспортирует cookies, ?format=json (по умолчанию) или netscape. Профиль должен быть закрыт (иначе 409). |
POST /profiles/{id}/cookies | Импортирует {cookies: …} в виде JSON-списка или текста Netscape и возвращает количество импортированных. Профиль должен быть закрыт. |
PUT /profiles/{id}/proxy | Задаёт прокси: {proxy_id} для сохранённого прокси, {proxy: {…}} для нового или {proxy: null}, чтобы удалить его. |
GET /running | Открытые профили: [{id, engine, cdp, debug_port, pid}]. |
POST /stop-all | Закрывает все открытые профили: {stopped: [ids]}. |
GET /foldersPOST /folders | Список папок / создание папки с {name, color?} (201). |
GET /proxies | Сохранённые прокси (пароли никогда не возвращаются). |
POST /proxies | Добавляет прокси (201): {text: "…"} по одному прокси на строку или {proxies: [...], scheme?, label?}. |
DELETE /proxies/{id} | Удаляет сохранённый прокси. Прокси, привязанный к профилям, удаляется только с ?force=true (эти профили остаются без прокси). |
POST /proxies/{id}/check | Проверяет прокси: выходной IP, страна, тип, задержка, что видят сайты-детекторы, и репутация. |
Примеры: curl
В PowerShell вводите curl.exe, так как curl там является псевдонимом Invoke-WebRequest. JSON-тела проще всего отправлять из Git Bash, cmd или скрипта.
curl -H "X-Token: kk_..." http://127.0.0.1:50360/api/v1/status
curl -H "X-Token: kk_..." "http://127.0.0.1:50360/api/v1/profiles?status=running&sort=last_used&desc=1"
curl -X POST -H "X-Token: kk_..." -H "Content-Type: application/json" \
-d '{"name": "shop-01", "os": "windows", "mode": "balanced", "proxy": "socks5://1.2.3.4:1080", "proxy_user": "user", "proxy_pass": "pass"}' \
http://127.0.0.1:50360/api/v1/profiles
curl -X POST -H "X-Token: kk_..." -H "Content-Type: application/json" \
-d '{"url": "https://example.com"}' \
http://127.0.0.1:50360/api/v1/profiles/PROFILE_ID/start
Примеры: Python + Playwright
Создаёт профиль с прокси, открывает его и подключается через connect_over_cdp (pip install requests playwright).
import requests
from playwright.sync_api import sync_playwright
API = "http://127.0.0.1:50360/api/v1"
H = {"X-Token": "kk_..."}
def call(method, path, **kw):
r = requests.request(method, API + path, headers=H, timeout=200, **kw).json()
if not r["ok"]:
raise RuntimeError(r["error"]["code"] + ": " + r["error"]["message"])
return r["data"]
prof = call("POST", "/profiles", json={
"name": "shop-01", "os": "windows", "mode": "balanced",
"proxy": {"type": "socks5", "host": "1.2.3.4", "port": 1080,
"username": "user", "password": "pass"},
})
run = call("POST", f"/profiles/{prof['id']}/start")
with sync_playwright() as pw:
browser = pw.chromium.connect_over_cdp(run["ws"])
ctx = browser.contexts[0] # the profile's own context
page = ctx.pages[0] if ctx.pages else ctx.new_page()
page.goto("https://example.com")
print(page.title())
call("POST", f"/profiles/{prof['id']}/stop")
Примеры: Node.js + Puppeteer
Используйте puppeteer-core: браузер запускает Kalkan, а Puppeteer только подключается к нему.
const puppeteer = require("puppeteer-core");
const API = "http://127.0.0.1:50360/api/v1";
const H = { "X-Token": "kk_...", "Content-Type": "application/json" };
(async () => {
const res = await fetch(`${API}/profiles/PROFILE_ID/start`, { method: "POST", headers: H, body: "{}" });
const { ok, data, error } = await res.json();
if (!ok) throw new Error(`${error.code}: ${error.message}`);
const browser = await puppeteer.connect({ browserWSEndpoint: data.ws, defaultViewport: null });
const [page] = await browser.pages();
await page.goto("https://example.com");
console.log(await page.title());
await browser.disconnect();
})();
Примеры: Selenium
Selenium подключается через debug_port. Основная версия chromedriver должна совпадать с версией браузера (Kalkan использует установленный у вас Chrome).
import requests
from selenium import webdriver
API = "http://127.0.0.1:50360/api/v1"
H = {"X-Token": "kk_..."}
run = requests.post(f"{API}/profiles/PROFILE_ID/start", headers=H, json={}).json()["data"]
opts = webdriver.ChromeOptions()
opts.debugger_address = f"127.0.0.1:{run['debug_port']}"
driver = webdriver.Chrome(options=opts)
driver.get("https://example.com")
print(driver.title)
Советы
- Не задавайте viewport из скрипта. Размер экрана профиля — часть его отпечатка. Используйте
defaultViewport: nullв Puppeteer и не передавайтеviewportв Playwright. - Используйте существующий контекст профиля. В Playwright берите
browser.contexts[0]вместоnew_context(): в новом контексте нет ни cookies, ни настроек профиля. - По завершении скрипта отключайтесь, а не закрывайте браузер, и останавливайте профиль через
POST /profiles/{id}/stop, чтобы Kalkan корректно сохранил сессию. - Запуск уже открытого профиля возвращает тот же эндпоинт с
already_running: true, поэтому скрипты могут безопасно вызыватьstartповторно. - Обрабатывайте 409 осознанно:
allow_directиforceсуществуют для того, чтобы скрипт никогда случайно не открыл профиль без прокси или на повторно используемом IP.