Tới phần nội dung

Tài liệuTự động hóa

API cục bộ

Đối chiếu lần cuối với đặc tả: tháng 9 năm 2026

Đã phát hành, và có sẵn trong bản dựng bạn tải về được hôm nay — trên cả macOS lẫn Windows. Công tắc nằm trong phần cài đặt của ứng dụng và API vẫn tắt cho đến khi bạn bật nó. Bản dựng đó trả lời những lệnh gọi nào thì được đánh dấu ở từng thao tác và từng ví dụ bên dưới, chứ không viết thành một câu ở đây để rồi bản phát hành sau làm nó sai đi.

Một script trên cùng máy biến trình duyệt của một hồ sơ thành thứ nó có thể điều khiển. Giao diện này gồm một dịch vụ, một khóa và một tệp địa chỉ — được mô tả trước tiên, vì mọi thứ đều dựa trên chúng — cùng một vài tuyến bên trên: tìm một hồ sơ, mở nó, kết nối Puppeteer, Playwright hoặc Selenium tới địa chỉ nó trả về, rồi đóng lại.

Giao diện ứng dụng hiện chỉ có tiếng Anh và tiếng Nga. Tên nút và cài đặt trên trang này được ghi đúng như giao diện tiếng Anh hiển thị.

Bật API

Mặc định tắt, và chỉ truy cập được từ chính máy tính này.

Công tắc
Mở Settings → API & Automation và bật Local API. Chừng nào bạn chưa bật thì không có gì lắng nghe: cổng không được mở, và script nhận về một kết nối bị từ chối chứ không phải một phản hồi rỗng.
Chỉ máy tính này
Dịch vụ chỉ nhận kết nối trên 127.0.0.1, không ở đâu khác. Máy khác trong mạng của bạn không thể chạm tới nó, và không có cài đặt nào mở nó ra — script chạy bên cạnh Liminal, chứ không gọi tới nó qua mạng.
Cổng
Mặc định là 48361, một cổng riêng của Liminal để không đụng với trình duyệt khác đang lắng nghe trên máy. Hãy nhập một cổng khác, hoặc nhấn Random free port và để ứng dụng tự chọn. Thay đổi có hiệu lực ngay, không cần khởi động lại Liminal.
Khi cổng đã bị chiếm
Nếu không gắn được vào cổng, màn hình hiện Port in use và tệp địa chỉ báo port_in_use. Không có gì lắng nghe cho đến khi bạn chọn cổng khác, nên script đọc tệp sẽ phân biệt được “đang tắt” với “không khởi động được”.

Khóa

Một khóa, chỉ hiện một lần, được cất ở nơi hệ điều hành của bạn cất các bí mật.

Sao chép ngay khi nó hiện ra
Bật API sẽ cấp một khóa gồm 64 ký tự thập lục phân viết thường và chỉ hiện nó một lần. Rời màn hình là nó bị ẩn: ứng dụng không thể hiện lại, chỉ có thể cấp khóa mới.
Khóa được lưu ở đâu
Trong Keychain trên macOS và Credential Manager trên Windows — không bao giờ nằm trong tệp cài đặt, bản chụp đồng bộ hay nhật ký, và cũng không nằm trong tệp địa chỉ bên dưới. Hãy giữ bản sao của bạn như cách bạn giữ mọi thông tin xác thực khác mà script dùng.
Thu hồi khóa
Regenerate key cấp một khóa mới và ngừng nhận khóa cũ ngay lập tức, không cần khởi động lại ứng dụng và không đổi cổng. Script còn giữ khóa cũ sẽ bị từ chối từ yêu cầu kế tiếp.
Gửi khóa
Mọi yêu cầu đều mang khóa trong một header: Authorization: Bearer, theo sau là chính khóa đó. Không có tham số truy vấn và không có cookie, và yêu cầu thiếu header bị từ chối giống như yêu cầu mang khóa sai.

Script tìm địa chỉ ở đâu

Ứng dụng ghi địa chỉ của mình vào một tệp, để không phải nhập thứ gì hai lần.

Tệp
Màn hình cài đặt hiện đường dẫn tuyệt đối tới Local API/status.json bên trong thư mục dữ liệu người dùng của Liminal. Hãy đọc tệp thay vì viết cứng một cổng: nó đi theo cổng bạn đặt lẫn cổng mà Random free port đã chọn.
Trong tệp có gì
Bốn trường — enabled, status, address, port — và không có bí mật nào.
{"enabled":true,"status":"running","address":"http://127.0.0.1:48361","port":48361}
Khi API đang tắt
Tệp vẫn ở nguyên chỗ và nói rõ điều đó: off, địa chỉ rỗng và không có cổng. Cùng một hình dạng, khác trạng thái — script chỉ cần đọc một tệp là biết hết.
{"enabled":false,"status":"off","address":"","port":0}
Tệp được thay nguyên vẹn, không bao giờ ghi dở
Mọi thay đổi — bật hoặc tắt API, cổng mới, cổng không gắn được — đều ghi lại tệp theo kiểu nguyên tử. Script đọc tệp giữa lúc thay đổi sẽ thấy nội dung cũ hoặc nội dung mới, không bao giờ thấy một dòng bị cắt cụt.

Yêu cầu trạng thái

Lệnh gọi nên bắt đầu trước tiên: Liminal có đang chạy không, và đã có ai đăng nhập chưa.

Lệnh gọi
Một GET tới /v1/status tại địa chỉ lấy từ tệp, kèm khóa trong header.
curl -H 'Authorization: Bearer <key>' http://127.0.0.1:48361/v1/status
Phản hồi
HTTP 200, kèm phiên bản của bản dựng đang chạy và việc ứng dụng đã có người đăng nhập hay chưa. signedIn bằng false không phải là lỗi — API vẫn trả lời; chính các thao tác đọc danh mục hoặc mở hồ sơ mới cần tài khoản.
{"status":"running","version":"<version>","signedIn":true}
Khóa bị thiếu hoặc đã bị thu hồi
HTTP 401 và mã invalid_key — mã này cố định, và là thứ để rẽ nhánh xử lý. Thông báo đi kèm là lời của chính ứng dụng dành cho người đọc nhật ký; hãy coi nó là văn bản có thể thay đổi, không phải một phần của đặc tả.
{"error":{"code":"invalid_key","message":"Неверный ключ"}}
Gọi đúng như tệp ghi
Hãy dùng 127.0.0.1 và cổng lấy từ tệp. Yêu cầu tới với tên host khác sẽ không được trả lời, kể cả khi nó tới đúng cổng đó — dịch vụ chỉ trả lời cho địa chỉ của chính nó.
Liminal bản cũ hơn thì làm gì
Không làm gì: không cổng nào được mở, nên script kết nối tới sẽ bị từ chối. Lần từ chối đó là cách script phân biệt bản dựng không có API cục bộ với bản dựng đang tắt nó — ở trường hợp sau, tệp địa chỉ tồn tại và ghi off.

Phiên bản này làm gì và không làm gì

Nói thẳng, để không điều gì ở đây bị đọc thành một lời hứa mà nó không phải.

Một thao tác có mặt trên trang này trước khi có trong bản dựng của bạn
Giao diện lớn dần qua từng bản phát hành, và một lệnh gọi đã được viết thì được ghi lại ở đây trước cả bản dựng trả lời nó — có đánh dấu, không bao giờ giấu. Vì vậy hãy đọc dấu, đừng đọc lời hứa: dấu có ở từng thao tác và từng ví dụ bên dưới, và nó cho bạn biết thứ bạn sắp viết script có chạy trong ứng dụng bạn đang có hay không.
Dành cho gói nào
Mọi gói, kể cả gói miễn phí. API cục bộ không phải là một bậc trả phí: script chạy trên chính máy của bạn là cách nhiều người dùng trình duyệt chống phát hiện, và đặt nó sau một gói thì chẳng khác nào đánh thuế lên cách dùng đó.Gói
Script được hỏi thường xuyên đến mức nào
Không có hạn ngạch nào gắn với gói của bạn. Dịch vụ tự bảo vệ mình khỏi bị dội yêu cầu, điều mà một script viết cho nó sẽ không nhận thấy.
Hồ sơ đang được tự động hóa vẫn là chính hồ sơ đó
Nó giữ nguyên dấu vân tay, proxy và cookie, và ra mạng qua proxy của mình đúng như khi bạn mở nó bằng tay. API là một lối vào, không phải chế độ thứ hai của trình duyệt.Dữ liệu và bảo mật

Từng thao tác một

Mỗi tuyến dùng để làm gì, nhận gì, trả lời gì và mọi lời từ chối nó có thể đưa ra. Nhãn cạnh một thao tác cho biết bản dựng bạn tải về được hôm nay có trả lời nó hay không.

GET /v1/status

Có trong bản dựng đã phát hành

Liminal có đang chạy không, và đã có ai đăng nhập chưa

Lệnh gọi rẻ nhất, và là lệnh nên gọi lặp lại trong lúc bạn chờ ứng dụng. Nó trả lời cả khi không có tài khoản: signedIn bằng false là một trạng thái, không phải lỗi.

Tham số

Không có.

Phản hồi

{
  "status": "running",
  "version": "<version>",
  "signedIn": true
}

Từ chối

  • 401 invalid_key — Khóa bị thiếu, sai định dạng hoặc đã được tạo lại từ lúc đó.

GET /v1/profiles

Có trong bản dựng đã phát hành

Các hồ sơ trong danh mục, có lọc và phân trang

Đọc đúng danh mục mà ứng dụng hiển thị. Các bộ lọc kết hợp được với nhau, và bộ lọc nào cũng khớp chính xác trừ name: nó khớp với mọi hồ sơ có tên chứa nội dung bạn nhập, không phân biệt chữ hoa chữ thường.

Tham số

pagequery · integer · tùy chọn · từ 1 đến 1000000 · mặc định là 1
Đọc trang nào, đếm từ trang đầu tiên.
pageSizequery · integer · tùy chọn · từ 1 đến 100 · mặc định là 100
Một trang chứa bao nhiêu hồ sơ. Mức trần chính là giá trị mặc định.
workspaceIdquery · string · tùy chọn
Chỉ các hồ sơ của không gian làm việc đó.
folderIdquery · string · tùy chọn
Chỉ các hồ sơ của thư mục đó.
statusquery · string · tùy chọn
Chỉ các hồ sơ ở trạng thái đó; running là trạng thái của một hồ sơ đang mở.
namequery · string · tùy chọn
Chỉ các hồ sơ có tên chứa nội dung này, không phân biệt chữ hoa chữ thường.

Phản hồi

{
  "page": 1,
  "pageSize": 100,
  "total": 1,
  "items": [
    "<profile>"
  ]
}

Từ chối

  • 401 invalid_key — Khóa bị thiếu, sai định dạng hoặc đã được tạo lại từ lúc đó.
  • 403 not_signed_in — Chưa có ai đăng nhập vào ứng dụng; các thao tác với danh mục cần tài khoản.
  • 403 team_closed — Nhóm đang dùng đã bị đóng: hãy gia hạn gói của nhóm hoặc chuyển sang nhóm khác.
  • 503 catalog_loading — Danh mục hồ sơ vẫn đang tải. Hãy hỏi lại.
  • 503 catalog_unavailable — Ứng dụng không mở được danh mục hồ sơ.
  • 400 invalid_pagination — page hoặc pageSize nằm ngoài khoảng được chấp nhận.

GET /v1/profiles/{id}

Có trong bản dựng đã phát hành

Một hồ sơ, kèm proxy và dấu vân tay của nó

Cùng đối tượng mà danh sách trả về, cho một mã định danh. Địa chỉ proxy được hiển thị; mật khẩu của nó thì không bao giờ.

Tham số

idpath · string · bắt buộc
Mã định danh của hồ sơ, đúng như các thao tác với danh mục trả về.

Phản hồi

{
  "id": "<profile-id>",
  "name": "Shop EU",
  "workspaceId": "<workspace-id>",
  "folderId": "<folder-id>",
  "status": "running",
  "health": "ok",
  "proxy": {
    "id": "<proxy-id>",
    "name": "Residential DE",
    "type": "socks5",
    "host": "proxy.example.net",
    "port": 1080,
    "working": true,
    "lastCheckedExternalIp": "203.0.113.10",
    "lastCheckedCountryCode": "DE"
  },
  "fingerprint": {
    "platform": "MacIntel",
    "language": "de-DE",
    "timezone": "Europe/Berlin"
  },
  "lastOpenedAt": "2026-09-30T08:15:00.000Z"
}

Từ chối

  • 401 invalid_key — Khóa bị thiếu, sai định dạng hoặc đã được tạo lại từ lúc đó.
  • 403 not_signed_in — Chưa có ai đăng nhập vào ứng dụng; các thao tác với danh mục cần tài khoản.
  • 403 team_closed — Nhóm đang dùng đã bị đóng: hãy gia hạn gói của nhóm hoặc chuyển sang nhóm khác.
  • 503 catalog_loading — Danh mục hồ sơ vẫn đang tải. Hãy hỏi lại.
  • 503 catalog_unavailable — Ứng dụng không mở được danh mục hồ sơ.
  • 404 profile_not_found — Không có hồ sơ nào mang mã định danh đó trong danh mục.

POST /v1/profiles/{id}/start

Có trong bản dựng đã phát hành

Mở một hồ sơ và nhận địa chỉ để driver kết nối

Khởi động hồ sơ theo cách ứng dụng vẫn làm — dấu vân tay, proxy, cookie của nó — và trả về địa chỉ CDP khi trình duyệt đã lên. Lệnh gọi này chờ; nó không trả về một tác vụ để bạn phải hỏi lại.

Tham số

idpath · string · bắt buộc
Mã định danh của hồ sơ, đúng như các thao tác với danh mục trả về.
debugPortbody · integer · tùy chọn · từ 1024 đến 65534 · mặc định là 0
Cổng mà điểm cuối CDP gắn vào. 0 để ứng dụng tự chọn một cổng trống; mọi giá trị khác phải là cổng mà người dùng của bạn gắn được.
timeoutSecondsbody · integer · tùy chọn · từ 1 đến 180 · mặc định là 60
Lệnh gọi chờ hồ sơ khởi động bao lâu trước khi bỏ cuộc.
maskAutomationbody · boolean · tùy chọn · mặc định là true
Chỉ chấp nhận true. Không thể tắt việc che giấu tự động hóa: hồ sơ do script điều khiển cũng chính là hồ sơ mà trang web thấy khi bạn mở nó bằng tay.

Phản hồi

{
  "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"
}

Từ chối

  • 401 invalid_key — Khóa bị thiếu, sai định dạng hoặc đã được tạo lại từ lúc đó.
  • 403 not_signed_in — Chưa có ai đăng nhập vào ứng dụng; các thao tác với danh mục cần tài khoản.
  • 503 runtime_unavailable — Lúc này ứng dụng không thể khởi động hoặc dừng hồ sơ.
  • 403 team_closed — Nhóm đang dùng đã bị đóng: hãy gia hạn gói của nhóm hoặc chuyển sang nhóm khác.
  • 503 catalog_loading — Danh mục hồ sơ vẫn đang tải. Hãy hỏi lại.
  • 503 catalog_unavailable — Ứng dụng không mở được danh mục hồ sơ.
  • 404 profile_not_found — Không có hồ sơ nào mang mã định danh đó trong danh mục.
  • 409 profile_already_running — Hồ sơ đã mở sẵn, một lần khởi động nó vẫn đang chạy, hoặc địa chỉ driver cấp trước đó vẫn còn dùng được.
  • 400 invalid_start_options — Phần thân không phải là một đối tượng JSON.
  • 400 invalid_port — debugPort không phải 0 cũng không phải một cổng trong khoảng được chấp nhận.
  • 400 invalid_start_timeout — timeoutSeconds nằm ngoài khoảng được chấp nhận.
  • 400 masking_unavailable — maskAutomation được gửi là false, điều mà ứng dụng không hỗ trợ.
  • 409 port_in_use — Cổng gỡ lỗi bạn yêu cầu đã bị chiếm.
  • 409 listen_failed — Không gắn được vào cổng gỡ lỗi vì một lý do khác.
  • 409 start_failed — Hồ sơ không khởi động được. Trường detail an toàn nêu tên giai đoạn khi ứng dụng nói được.
  • 409 proxy_unavailable — Hồ sơ bắt buộc dùng proxy của nó, mà proxy không trả lời.
  • 502 start_failed — Việc chuẩn bị hồ sơ thất bại trước khi trình duyệt được yêu cầu khởi động.
  • 503 cdp_unavailable — Hồ sơ đã khởi động nhưng điểm cuối CDP của nó không lên.
  • 504 start_timeout — Hồ sơ không khởi động xong trong khoảng timeoutSeconds.

POST /v1/profiles/{id}/attach-automation

Có trong bản dựng đã phát hành

Lấy địa chỉ driver cho một hồ sơ đã mở sẵn

Dành cho hồ sơ bạn đã mở bằng tay và giờ muốn điều khiển. Nó không khởi động lại gì cả: phiên, các thẻ và cookie vẫn giữ nguyên.

Tham số

idpath · string · bắt buộc
Mã định danh của hồ sơ, đúng như các thao tác với danh mục trả về.
debugPortbody · integer · tùy chọn · từ 1024 đến 65534 · mặc định là 0
Cổng mà điểm cuối CDP gắn vào. 0 để ứng dụng tự chọn một cổng trống; mọi giá trị khác phải là cổng mà người dùng của bạn gắn được.
timeoutSecondsbody · integer · tùy chọn · từ 1 đến 180 · mặc định là 60
Lệnh gọi chờ hồ sơ khởi động bao lâu trước khi bỏ cuộc.
maskAutomationbody · boolean · tùy chọn · mặc định là true
Chỉ chấp nhận true. Không thể tắt việc che giấu tự động hóa: hồ sơ do script điều khiển cũng chính là hồ sơ mà trang web thấy khi bạn mở nó bằng tay.

Phản hồi

{
  "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"
}

Từ chối

  • 401 invalid_key — Khóa bị thiếu, sai định dạng hoặc đã được tạo lại từ lúc đó.
  • 403 not_signed_in — Chưa có ai đăng nhập vào ứng dụng; các thao tác với danh mục cần tài khoản.
  • 503 runtime_unavailable — Lúc này ứng dụng không thể khởi động hoặc dừng hồ sơ.
  • 403 team_closed — Nhóm đang dùng đã bị đóng: hãy gia hạn gói của nhóm hoặc chuyển sang nhóm khác.
  • 503 catalog_loading — Danh mục hồ sơ vẫn đang tải. Hãy hỏi lại.
  • 503 catalog_unavailable — Ứng dụng không mở được danh mục hồ sơ.
  • 404 profile_not_found — Không có hồ sơ nào mang mã định danh đó trong danh mục.
  • 409 profile_already_running — Hồ sơ đã mở sẵn, một lần khởi động nó vẫn đang chạy, hoặc địa chỉ driver cấp trước đó vẫn còn dùng được.
  • 400 invalid_start_options — Phần thân không phải là một đối tượng JSON.
  • 400 invalid_port — debugPort không phải 0 cũng không phải một cổng trong khoảng được chấp nhận.
  • 400 invalid_start_timeout — timeoutSeconds nằm ngoài khoảng được chấp nhận.
  • 400 masking_unavailable — maskAutomation được gửi là false, điều mà ứng dụng không hỗ trợ.
  • 409 port_in_use — Cổng gỡ lỗi bạn yêu cầu đã bị chiếm.
  • 409 listen_failed — Không gắn được vào cổng gỡ lỗi vì một lý do khác.
  • 409 start_failed — Hồ sơ không khởi động được. Trường detail an toàn nêu tên giai đoạn khi ứng dụng nói được.
  • 409 proxy_unavailable — Hồ sơ bắt buộc dùng proxy của nó, mà proxy không trả lời.
  • 502 start_failed — Việc chuẩn bị hồ sơ thất bại trước khi trình duyệt được yêu cầu khởi động.
  • 503 cdp_unavailable — Hồ sơ đã khởi động nhưng điểm cuối CDP của nó không lên.
  • 504 start_timeout — Hồ sơ không khởi động xong trong khoảng timeoutSeconds.
  • 409 profile_not_running — Hồ sơ chưa mở, nên không có gì để gắn vào.

POST /v1/profiles/{id}/stop

Có trong bản dựng đã phát hành

Đóng một hồ sơ theo cách cửa sổ của nó vẫn đóng

Để phiên ghi xong trạng thái của mình. Đây là lệnh nên gọi giữa các lần chạy script trên cùng một hồ sơ.

Tham số

idpath · string · bắt buộc
Mã định danh của hồ sơ, đúng như các thao tác với danh mục trả về.

Phản hồi

{
  "id": "<profile-id>",
  "status": "closed"
}

Từ chối

  • 401 invalid_key — Khóa bị thiếu, sai định dạng hoặc đã được tạo lại từ lúc đó.
  • 403 not_signed_in — Chưa có ai đăng nhập vào ứng dụng; các thao tác với danh mục cần tài khoản.
  • 503 runtime_unavailable — Lúc này ứng dụng không thể khởi động hoặc dừng hồ sơ.
  • 403 team_closed — Nhóm đang dùng đã bị đóng: hãy gia hạn gói của nhóm hoặc chuyển sang nhóm khác.
  • 503 catalog_loading — Danh mục hồ sơ vẫn đang tải. Hãy hỏi lại.
  • 503 catalog_unavailable — Ứng dụng không mở được danh mục hồ sơ.
  • 404 profile_not_found — Không có hồ sơ nào mang mã định danh đó trong danh mục.
  • 409 profile_not_running — Hồ sơ chưa mở, nên không có gì để đóng.
  • 409 stop_failed — Ứng dụng không đóng được hồ sơ.

POST /v1/profiles/{id}/force-stop

Có trong bản dựng đã phát hành

Đóng một hồ sơ không chịu đóng

Cùng phản hồi đó, nhưng không chờ phiên ổn định lại. Hãy dùng nó khi một lệnh dừng không trả về, đừng dùng như thói quen.

Tham số

idpath · string · bắt buộc
Mã định danh của hồ sơ, đúng như các thao tác với danh mục trả về.

Phản hồi

{
  "id": "<profile-id>",
  "status": "closed"
}

Từ chối

  • 401 invalid_key — Khóa bị thiếu, sai định dạng hoặc đã được tạo lại từ lúc đó.
  • 403 not_signed_in — Chưa có ai đăng nhập vào ứng dụng; các thao tác với danh mục cần tài khoản.
  • 503 runtime_unavailable — Lúc này ứng dụng không thể khởi động hoặc dừng hồ sơ.
  • 403 team_closed — Nhóm đang dùng đã bị đóng: hãy gia hạn gói của nhóm hoặc chuyển sang nhóm khác.
  • 503 catalog_loading — Danh mục hồ sơ vẫn đang tải. Hãy hỏi lại.
  • 503 catalog_unavailable — Ứng dụng không mở được danh mục hồ sơ.
  • 404 profile_not_found — Không có hồ sơ nào mang mã định danh đó trong danh mục.
  • 409 profile_not_running — Hồ sơ chưa mở, nên không có gì để đóng.
  • 409 stop_failed — Ứng dụng không đóng được hồ sơ.

GET /v1/running-profiles

Có trong bản dựng đã phát hành

Những gì đang mở lúc này, và những gì điều khiển được

Mọi hồ sơ đang mở, dù do cửa sổ hay do lệnh gọi mở ra — startedBy cho biết bên nào — kèm địa chỉ driver khi có. Hãy kết nối lại từ đây sau khi script của bạn khởi động lại.

Tham số

Không có.

Phản hồi

{
  "items": [
    {
      "id": "<profile-id>",
      "workspaceId": "<workspace-id>",
      "status": "running",
      "automationAvailable": true,
      "startedBy": "api",
      "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"
    }
  ]
}

Từ chối

  • 401 invalid_key — Khóa bị thiếu, sai định dạng hoặc đã được tạo lại từ lúc đó.
  • 403 not_signed_in — Chưa có ai đăng nhập vào ứng dụng; các thao tác với danh mục cần tài khoản.
  • 403 team_closed — Nhóm đang dùng đã bị đóng: hãy gia hạn gói của nhóm hoặc chuyển sang nhóm khác.
  • 503 catalog_loading — Danh mục hồ sơ vẫn đang tải. Hãy hỏi lại.
  • 503 catalog_unavailable — Ứng dụng không mở được danh mục hồ sơ.

Điều khiển hồ sơ từ script của riêng bạn

Mỗi khối đều trọn vẹn: nó đọc tệp địa chỉ, gọi API và kết nối driver. Sao chép một khối, điền tên hồ sơ của bạn vào, và nó chạy. Nhãn cho biết bản dựng bạn tải về được hôm nay có trả lời mọi thứ khối đó cần hay không.

Puppeteer (JavaScript)

Có trong bản dựng đã phát hành

Kết nối qua CDP tới địa chỉ mà lệnh khởi động trả về — đúng cách một script Puppeteer tiếp cận một trình duyệt đang chạy sẵn.

Gọi /v1/profiles · /v1/profiles/{id}/start

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()

Playwright (JavaScript)

Có trong bản dựng đã phát hành

Cùng kết nối đó qua lối vào CDP của Playwright, vào ngữ cảnh hồ sơ đã có sẵn chứ không phải một ngữ cảnh mới.

Gọi /v1/profiles · /v1/profiles/{id}/start

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()

Playwright (Python)

Có trong bản dựng đã phát hành

Phía Python của cùng việc đó, đọc tệp địa chỉ mà không cần gì ngoài thư viện chuẩn.

Gọi /v1/profiles · /v1/profiles/{id}/start

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())

Selenium (Python)

Có trong bản dựng đã phát hành

Selenium gắn vào một trình duyệt đang chạy sẵn, theo địa chỉ trình gỡ lỗi, và dùng chromedriver mà phản hồi chỉ tới — không tải gì về và không phải đoán gì.

Gọi /v1/profiles · /v1/profiles/{id}/start

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()

Selenium (JavaScript)

Có trong bản dựng đã phát hành

Tương tự cho Node: cả địa chỉ trình gỡ lỗi lẫn đường dẫn tới chromedriver phù hợp đều lấy từ phản hồi của lệnh khởi động.

Gọi /v1/profiles · /v1/profiles/{id}/start

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()

Bản mô tả mà công cụ có thể nhập vào

Cùng đặc tả đó dưới dạng tệp, ở một địa chỉ không đổi: OpenAPI, dạng JSON, sinh ra từ chính dữ liệu trang này hiển thị. Hãy nhập nó vào Postman hoặc Insomnia, hoặc sinh một client từ nó. Mỗi thao tác trong đó mang cùng mức sẵn có như nhãn ở trên.

openapi.json

Bản phát hành đã mang nó tới, và mọi bản phát hành mở rộng nó, đều nằm trong mục Có gì mới. Một câu hỏi về đặc tả, hoặc một giá trị ở đây không khớp với những gì bản dựng của bạn làm: [email protected].