Tài liệu tham khảo API

REST API
Xây dựng trên nền liên kết của bạn.

Xác thực bằng API token, liệt kê và tạo liên kết, đọc lượt truy cập và đăng ký webhook.

URL gốc https://letl.ink/api/v1 Chỉ HTTPS

Gói Growth Ultimate
Trên trang này

Bắt đầu

REST API có trên gói Growth và Ultimate. Yêu cầu được gửi đến https://letl.ink/api/v1 qua HTTPS. Không gian làm việc dùng gói Free hoặc Solo sẽ nhận 403 plan_required từ /api/v1; endpoint MCP được giới hạn riêng cho gói Ultimate.

  1. Mở Tích hợp → REST API (chỉ chủ sở hữu và quản trị viên không gian làm việc).
  2. Tạo token. Token không hết hạn.
  3. Hãy bảo quản như mật khẩu. Một token dùng cho REST API, máy chủ MCP và mọi nền tảng tự động hóa kết nối với không gian làm việc này, nên tạo lại token sẽ vô hiệu hóa tất cả cùng lúc.

Thành viên có thể sao chép token hiện có nếu đã được tạo; chỉ chủ sở hữu và quản trị viên mới có thể thay đổi token.

Xác thực

Gửi token dưới dạng X-API-KEY hoặc Authorization: Bearer. Cả hai đều được chấp nhận trên /api/v1 và /mcp. Yêu cầu không có token nào sẽ trả về 401 unauthenticated.

GET Tôi là ai?
curl -H "X-API-KEY: YOUR_TOKEN" \
  https://letl.ink/api/v1/auth/me

Header tương đương: Authorization: Bearer YOUR_TOKEN

Quy ước

Các quy định này áp dụng cho mọi endpoint bên dưới.

Cấu trúc phản hồi

Mọi phản hồi thành công là một đối tượng JSON có khóa data, và khóa meta khi có thông tin bổ sung về kết quả — phân trang, khoảng ngày được áp dụng, có bao gồm địa lý hay không. Mọi lỗi là một đối tượng có khóa error chứa code ổn định, message dễ đọc và đôi khi có details. Hãy rẽ nhánh theo code, không bao giờ theo nội dung message.

Hình dạng
// success
{ "data": { … }, "meta": { … } }

// failure
{ "error": { "code": "invalid_parameter",
             "message": "sort must be one of: created_at, clicks.",
             "details": { "parameter": "sort" } } }

Phân trang

Các endpoint danh sách chấp nhận limit (1–200, mặc định 50) và offset. Khối meta trả về total, has_more và next_offset, nên bạn có thể phân trang mà không cần tính toán: tiếp tục gọi với offset = meta.next_offset cho đến khi has_more là false.

Duyệt qua mọi liên kết
offset=0
while [ "$offset" != "null" ]; do
  page=$(curl -s -H "X-API-KEY: $TOKEN" \
    "https://letl.ink/api/v1/links?limit=200&offset=$offset")
  echo "$page" | jq -c '.data[]'
  offset=$(echo "$page" | jq -r '.meta.next_offset')
done

Sắp xếp, lọc và tìm kiếm

GET /links chấp nhận sort (created_at, clicks, short_code, title, updated_at), direction (asc/desc), search (khớp với mã rút gọn, tiêu đề và đích đến), type, archived, favorite và domain. Giá trị không hợp lệ sẽ bị từ chối với invalid_parameter kèm danh sách giá trị được phép, thay vì bị bỏ qua âm thầm.

Khoảng thời gian

start_date và end_date dùng định dạng YYYY-MM-DD và bao gồm cả hai ngày. Khoảng thời gian bị giới hạn theo thời hạn lưu trữ của gói — yêu cầu dữ liệu một năm trên gói lưu trữ 30 ngày sẽ trả về 30 ngày hiện có, và meta.date_range cho biết khoảng thời gian thực tế được dùng.

Giới hạn tốc độ

600 yêu cầu mỗi phút cho mỗi tài khoản, theo cửa sổ trượt. Mọi phản hồi đều kèm X-RateLimit-Limit, X-RateLimit-Remaining và X-RateLimit-Reset (dấu thời gian Unix). Vượt giới hạn sẽ trả về 429 rate_limited kèm header Retry-After tính bằng giây. Hãy chờ theo Retry-After thay vì thử lại ngay.

Header trên mọi phản hồi
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 586
X-RateLimit-Reset: 1789000816

Phiên bản

Đường dẫn chứa phiên bản. Việc thêm trường vào phản hồi hoặc thêm tham số tùy chọn mới không phải là thay đổi phá vỡ và có thể diễn ra bất cứ lúc nào, vì vậy hãy phân tích một cách phòng thủ và bỏ qua các khóa không nhận ra. Mọi thay đổi hoặc xóa trường hiện có sẽ được phát hành dưới tiền tố phiên bản mới.

Endpoint

Tài khoản

GET /me Kiểm tra kết nối

Danh tính, không gian làm việc, gói và mọi quyền lợi của gói đó. Cách nhanh nhất để xác minh token và biết tài khoản được phép làm gì trước khi thực hiện.

GET cURL
curl -H "X-API-KEY: YOUR_TOKEN" \
  https://letl.ink/api/v1/me
Phản hồi 200
{
  "data": {
    "user": { "id": 1, "email": "[email protected]", "name": "You" },
    "workspace": { "id": 12, "name": "My Workspace", "block_bots": true },
    "plan": {
      "id": "growth",
      "name": "Growth",
      "limits": {
        "links": 200,
        "monthly_clicks": 100000,
        "custom_domains": 3,
        "qr_codes": 30,
        "retention_days": 365
      },
      "features": {
        "api": true,
        "webhooks": true,
        "integrations": true,
        "ai_access": false,
        "geo_analytics": false,
        "smart_link_types": true
      }
    },
    "base_domain": "letl.ink"
  }
}
GET /usage

Mức đã dùng của từng hạn mức trong tháng thanh toán này. Dùng để cảnh báo khách hàng trước khi họ chạm trần lượt nhấp được theo dõi — vượt mức đó chuyển hướng vẫn hoạt động, nhưng lượt nhấp sẽ ngừng được ghi nhận.

Phản hồi 200
{
  "data": {
    "links": { "used": 42, "limit": 200, "remaining": 158, "percent_used": 21.0 },
    "clicks_this_month": { "used": 8140, "limit": 100000, "remaining": 91860, "percent_used": 8.1 },
    "custom_domains": { "used": 1, "limit": 3, "remaining": 2, "percent_used": 33.3 },
    "qr_codes": { "used": 4, "limit": 30, "remaining": 26, "percent_used": 13.3 }
  },
  "meta": { "plan": "growth", "period": "2026-09" }
}
GET /domains

Các tên miền tùy chỉnh trong không gian làm việc và trạng thái xác minh DNS của từng tên miền. Chỉ tên miền đã xác minh mới có thể dùng làm trường domain khi tạo liên kết.

GET /integrations

Mọi tích hợp sản phẩm cung cấp, gói của bạn có bao gồm hay không, không gian làm việc này đã kết nối chưa, và tình trạng gửi ra sao — trạng thái gần nhất, lỗi gần nhất và số lần thất bại liên tiếp. Hữu ích để giám sát nhiều không gian làm việc.

Liên kết

Phân tích

GET /analytics/summary

Các số liệu chính của toàn không gian làm việc: lượt nhấp từ trước đến nay và trong khoảng thời gian, khách truy cập duy nhất, lượt quét QR, ngày cao điểm, cùng phân bố theo giờ trong ngày và ngày trong tuần. Chấp nhận days (mặc định 30, giới hạn theo thời hạn lưu trữ).

Phản hồi 200 (rút gọn)
{
  "data": {
    "window_days": 30,
    "clicks": {
      "total": 24592, "window": 8140, "last_7_days": 2103,
      "daily_average": 271,
      "peak_day": { "date": "Sep 02", "clicks": 612 }
    },
    "visitors": { "unique_total": 18204, "unique_window": 6021 },
    "qr": { "scans_total": 940, "scans_window": 310 },
    "timing": {
      "by_hour": [12, 4, 2, …],
      "by_weekday": [1420, 1610, 1585, 1490, 1310, 480, 245],
      "peak_hour": { "peakHour": 14, "peakValue": 812, "label": "14:00–14:59" },
      "peak_weekday": { "peakDay": 1, "peakValue": 1610, "label": "Tuesday" }
    },
    "links": { "active": 42, "limit": 200 }
  },
  "meta": { "plan": "growth", "geo_included": true, "retention_days": 365 }
}
GET /analytics/timeseries

Số lượt nhấp theo ngày, sẵn sàng để vẽ biểu đồ. Truyền link_id để giới hạn trong một liên kết, và days để đặt khoảng thời gian. Những ngày không có lưu lượng được trả về bằng 0 thay vì bị bỏ qua, nên chuỗi dữ liệu luôn liên tục.

Phản hồi 200
{
  "data": [
    { "date": "Sep 04", "clicks": 22 },
    { "date": "Sep 05", "clicks": 11 }
  ],
  "meta": { "window_days": 7, "total": 33, "link_id": null }
}
GET /analytics/breakdown

Lượt nhấp được nhóm theo một chiều dữ liệu, kèm tỷ lệ của từng dòng trên tổng số.

Tham số truy vấn

  • dimension string

    channel, referrer, device, browser, os, brand, variation hoặc country. browser, os, brand và variation cần có link_id.

    Bắt buộc
  • link_id integer

    Giới hạn trong một liên kết. Bỏ qua để lấy toàn bộ không gian làm việc.

    Tùy chọn
  • limit integer

    1–100. Mặc định là 10.

    Tùy chọn
Phản hồi 200
{
  "data": [
    { "label": "Search", "value": 215, "share": 33.1 },
    { "label": "Social", "value": 201, "share": 30.9 },
    { "label": "Direct / none", "value": 142, "share": 21.8 }
  ],
  "meta": { "dimension": "channel", "link_id": 49, "total": 650 }
}
GET /events Phân trang

Luồng lượt truy cập thô trên mọi liên kết trong không gian làm việc, mới nhất trước. Đây là phiên bản hiện đại thay thế cho GET /visits: cùng dữ liệu, lớp bọc chuẩn, hỗ trợ limit và offset.

Phản hồi 200
{
  "data": [
    {
      "id": 2691,
      "link_id": 49,
      "short_code": "spring",
      "occurred_at": "2026-09-10T22:45:33+00:00",
      "ip_address": "203.0.113.7",
      "referrer": "https://twitter.com/",
      "browser": "Chrome",
      "os": "iOS",
      "device_type": "mobile",
      "device_brand": "Apple",
      "country": "US",
      "variation": "control"
    }
  ],
  "meta": { "limit": 50, "returned": 50, "geo_included": true }
}

Webhook

GET /hooks

Các endpoint webhook đã đăng ký trên tài khoản này.

POST /hooks

Đăng ký một endpoint HTTPS để nhận dữ liệu JSON với mỗi lượt nhấp được ghi nhận. Tối đa 25 endpoint mỗi tài khoản. Các lần gửi thất bại được tự động thử lại.

Phần thân JSON

  • target_url

    Phải là https://.

    Bắt buộc
  • event

    Mặc định là link_visited, hiện là sự kiện duy nhất.

    Tùy chọn
POST cURL
curl -X POST https://letl.ink/api/v1/hooks \
  -H "X-API-KEY: YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"target_url": "https://example.com/hooks/clicks"}'
DELETE /hooks/{id}

Xóa một endpoint webhook. POST /hooks/subscribe và DELETE /hooks/unsubscribe/{id} vẫn khả dụng cho luồng REST Hook của Zapier.

Cũ

GET /auth/me Cũ

Lệnh kiểm tra kết nối ban đầu: trả về id người dùng, email và id/tên không gian làm việc dưới dạng đối tượng phẳng. GET /me trả về cùng danh tính cộng với giới hạn gói và cờ tính năng, và là lựa chọn tốt hơn cho mã mới.

GET cURL
curl -H "X-API-KEY: YOUR_TOKEN" \
  https://letl.ink/api/v1/auth/me
Phản hồi 200
{
  "id": 1,
  "email": "[email protected]",
  "workspace_id": 12,
  "workspace_name": "My Workspace"
}
GET /visits Cũ

50 lượt truy cập gần nhất trên toàn không gian làm việc dưới dạng mảng JSON thuần — không có lớp bọc — dùng các tên trường visit_id, visit_time, link_shortcode và visitor_*. Đây là dữ liệu mà bước "perform list" của REST Hook Zapier đọc, nên cấu trúc được giữ cố định. Hãy dùng GET /events cho các tích hợp mới.

POST /hooks/subscribe Zapier

Đăng ký REST Hook. Nhận target_url và event tùy chọn (mặc định link_visited) và trả về đăng ký đã tạo ở dạng phẳng, không có lớp bọc. POST /hooks là thao tác tương tự theo cấu trúc chuẩn.

DELETE /hooks/unsubscribe/{id} Zapier

Hủy đăng ký REST Hook, trả về {"success": true}. DELETE /hooks/{id} là thao tác tương đương theo cấu trúc chuẩn.

Lỗi

  • 400 invalid_request

    Phần thân không phải là đối tượng JSON, hoặc thiếu trường bắt buộc.

  • 400 invalid_parameter

    Một tham số truy vấn bị sai định dạng hoặc nằm ngoài tập giá trị cho phép. details.parameter cho biết tham số gây lỗi.

  • 401 unauthenticated

    Không có token nào được gửi. Hãy thêm X-API-KEY hoặc Authorization: Bearer.

  • 401 invalid_token

    Token không khớp với tài khoản nào. Có thể token đã được tạo lại.

  • 403 plan_required

    Gói của tài khoản không bao gồm endpoint hoặc tính năng này. details.upgrade_url trỏ tới trang thanh toán.

  • 403 forbidden

    Đã xác thực nhưng không được phép — ví dụ một thành viên thực hiện thao tác chỉ dành cho chủ sở hữu.

  • 404 not_found

    Không có tài nguyên này trong không gian làm việc. Id từ không gian làm việc khác được báo là không tìm thấy thay vì bị cấm.

  • 409 conflict

    Yêu cầu xung đột với trạng thái hiện tại — đã đạt giới hạn webhook, hoặc tài khoản chưa có không gian làm việc.

  • 422

    Được trả về bởi POST /links/bulk khi mọi dòng trong lô đều thất bại. Kiểm tra meta.errors.

  • 429 rate_limited

    Vượt quá 600 yêu cầu trong phút vừa qua. Hãy chờ số giây ghi trong Retry-After.

Các endpoint cũ GET /visits và GET /links/{id}/visits trả về mảng JSON thuần với tên trường riêng và được giữ cố định cho các tích hợp Zapier đã xây dựng trên đó. Mã mới nên dùng GET /events và GET /links/{id}/events, vốn dùng cấu trúc phản hồi chuẩn và hỗ trợ phân trang.

Vẫn còn thắc mắc?

Chúng tôi luôn sẵn sàng hỗ trợ. Hãy liên hệ và chúng tôi sẽ phản hồi sớm nhất có thể.