API 레퍼런스

REST API
링크 위에 구축하세요.

API 토큰으로 인증하고, 링크를 조회·생성하며, 방문 기록을 읽고, 웹훅을 구독하세요.

기본 URL https://letl.ink/api/v1 HTTPS 전용

요금제 Growth Ultimate
이 페이지 내용

시작하기

REST API는 Growth 및 Ultimate에서 사용할 수 있습니다. 요청은 HTTPS를 통해 https://letl.ink/api/v1로 보냅니다. Free 또는 Solo 워크스페이스는 /api/v1에서 403 plan_required를 받으며, MCP 엔드포인트는 Ultimate에서 별도로 제한됩니다.

  1. 연동 → REST API를 여세요(워크스페이스 소유자와 관리자만 가능).
  2. 토큰을 생성하세요. 토큰은 만료되지 않습니다.
  3. 비밀번호처럼 보관하세요. 토큰 하나로 이 워크스페이스에 연결된 REST API, MCP 서버, 모든 자동화 플랫폼을 인증하므로, 다시 생성하면 모두 한꺼번에 무효화됩니다.

토큰이 생성되어 있으면 멤버도 복사할 수 있지만, 교체는 소유자와 관리자만 할 수 있습니다.

인증

토큰을 X-API-KEY 또는 Authorization: Bearer로 보내세요. /api/v1과 /mcp에서 둘 다 허용됩니다. 토큰이 전혀 없는 요청은 401 unauthenticated를 반환합니다.

GET 내 정보 확인
curl -H "X-API-KEY: YOUR_TOKEN" \
  https://letl.ink/api/v1/auth/me

동일한 헤더: Authorization: Bearer YOUR_TOKEN

규칙

아래의 모든 엔드포인트에 공통으로 적용됩니다.

응답 구조

성공한 응답은 모두 data 키를 가진 JSON 객체이며, 페이지네이션, 적용된 기간, 지역 정보 포함 여부 등 결과에 대한 추가 정보가 있으면 meta 키도 포함됩니다. 실패한 응답은 모두 고정된 code, 사람이 읽을 수 있는 message, 경우에 따라 details를 담은 error 키를 가진 객체입니다. 메시지 텍스트가 아니라 code로 분기하세요.

모양
// success
{ "data": { … }, "meta": { … } }

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

페이지네이션

목록 엔드포인트는 limit(1–200, 기본값 50)과 offset을 지원합니다. meta 블록에 total, has_more, next_offset이 포함되므로 직접 계산하지 않고도 페이지를 넘길 수 있습니다: has_more가 false가 될 때까지 offset = meta.next_offset으로 계속 호출하세요.

모든 링크 페이지 넘기기
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

정렬, 필터링, 검색

GET /links는 sort(created_at, clicks, short_code, title, updated_at), direction(asc/desc), search(단축 코드, 제목, 대상 URL 검색), type, archived, favorite, domain을 지원합니다. 알 수 없는 값은 조용히 무시되지 않고 invalid_parameter 오류와 함께 허용되는 값 목록이 반환됩니다.

기간 지정

start_date와 end_date는 YYYY-MM-DD 형식이며 두 날짜를 모두 포함합니다. 기간은 요금제의 보관 기간으로 제한됩니다. 보관 기간이 30일인 요금제에서 1년치 기록을 요청하면 보관된 30일치가 반환되며, meta.date_range에서 실제로 적용된 기간을 확인할 수 있습니다.

요청 제한

계정당 분당 600회 요청(슬라이딩 윈도 방식). 모든 응답에는 X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset(Unix 타임스탬프)이 포함됩니다. 한도를 초과하면 초 단위의 Retry-After 헤더와 함께 429 rate_limited가 반환됩니다. 바로 재시도하지 말고 Retry-After만큼 기다리세요.

모든 응답에 포함되는 헤더
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 586
X-RateLimit-Reset: 1789000816

버전 관리

경로에 버전이 포함됩니다. 응답에 필드를 추가하거나 새 선택적 매개변수를 추가하는 것은 호환성을 깨는 변경이 아니며 언제든 이루어질 수 있으므로, 방어적으로 파싱하고 모르는 키는 무시하세요. 기존 필드를 변경하거나 삭제하는 변경은 새 버전 접두사로 배포됩니다.

엔드포인트

계정

GET /me 연결 테스트

신원, 워크스페이스, 요금제, 그리고 해당 요금제의 모든 권한. 토큰을 확인하고, 작업을 시도하기 전에 계정에서 허용되는 작업을 파악하는 가장 간단한 방법입니다.

GET cURL
curl -H "X-API-KEY: YOUR_TOKEN" \
  https://letl.ink/api/v1/me
응답 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

이번 결제 월에 각 허용량이 얼마나 사용되었는지 보여 줍니다. 클릭 추적 한도에 도달하기 전에 고객에게 알리는 데 활용하세요 — 한도를 넘어도 리디렉션은 작동하지만 클릭은 더 이상 기록되지 않습니다.

응답 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

워크스페이스의 커스텀 도메인과 각 도메인의 DNS 인증 완료 여부. 링크를 만들 때 domain 필드에는 인증된 도메인만 사용할 수 있습니다.

GET /integrations

제품이 제공하는 모든 연동에 대해 요금제 포함 여부, 이 워크스페이스의 연결 여부, 전송 상태(마지막 상태, 마지막 오류, 연속 실패 횟수)를 보여 줍니다. 여러 워크스페이스를 모니터링할 때 유용합니다.

링크

분석

GET /analytics/summary

워크스페이스 전체의 핵심 수치: 전체 기간 및 기간 내 클릭, 순 방문자, QR 스캔, 피크 일, 시간대 및 요일 분포. days(기본값 30, 보관 기간으로 제한)를 지원합니다.

응답 200(요약)
{
  "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

차트용 일별 클릭 수. link_id로 특정 링크를 지정하고 days로 기간을 설정하세요. 트래픽이 없는 날도 생략하지 않고 0으로 반환하므로 시계열이 항상 연속됩니다.

응답 200
{
  "data": [
    { "date": "Sep 04", "clicks": 22 },
    { "date": "Sep 05", "clicks": 11 }
  ],
  "meta": { "window_days": 7, "total": 33, "link_id": null }
}
GET /analytics/breakdown

한 가지 기준으로 묶은 클릭과 각 행의 전체 대비 비율.

쿼리 매개변수

  • dimension string

    channel, referrer, device, browser, os, brand, variation 또는 country. browser, os, brand, variation에는 link_id가 필요합니다.

    필요
  • link_id integer

    하나의 링크로 범위를 제한합니다. 생략하면 워크스페이스 전체입니다.

    선택 사항
  • limit integer

    1–100. 기본값은 10입니다.

    선택 사항
응답 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 페이지 단위

워크스페이스 내 모든 링크의 원시 방문 피드로, 최신순으로 정렬됩니다. GET /visits를 대체하는 최신 엔드포인트로, 데이터는 같고 표준 응답 형식과 limit, offset을 지원합니다.

응답 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 }
}

웹훅

GET /hooks

이 계정에 등록된 웹훅 엔드포인트입니다.

POST /hooks

기록된 클릭마다 JSON 페이로드를 받을 HTTPS 엔드포인트를 등록하세요. 계정당 최대 25개. 실패한 전송은 자동으로 재시도됩니다.

JSON 본문

  • target_url

    https://여야 합니다.

    필요
  • event

    기본값은 link_visited이며, 현재 유일한 이벤트입니다.

    선택 사항
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}

웹훅 엔드포인트를 삭제합니다. POST /hooks/subscribe와 DELETE /hooks/unsubscribe/{id}는 Zapier의 REST Hook 흐름을 위해 계속 사용할 수 있습니다.

레거시

GET /auth/me 레거시

기존 연결 테스트: 사용자 ID, 이메일, 워크스페이스 ID/이름을 평면 객체로 반환합니다. GET /me는 같은 신원 정보에 요금제 한도와 기능 플래그까지 반환하므로 새 코드에는 이쪽이 더 적합합니다.

GET cURL
curl -H "X-API-KEY: YOUR_TOKEN" \
  https://letl.ink/api/v1/auth/me
응답 200
{
  "id": 1,
  "email": "[email protected]",
  "workspace_id": 12,
  "workspace_name": "My Workspace"
}
GET /visits 레거시

워크스페이스 전체의 최근 방문 50개를 visit_id, visit_time, link_shortcode, visitor_* 필드 이름을 사용한 순수 JSON 배열(래퍼 없음)로 반환합니다. Zapier REST Hook의 “perform list” 단계가 읽는 형식이므로 구조가 고정되어 있습니다. 새 작업에는 GET /events를 사용하세요.

POST /hooks/subscribe Zapier

REST Hook 구독. target_url과 선택적 event(기본값 link_visited)를 받아, 생성된 구독을 래퍼 없이 평면 구조로 반환합니다. POST /hooks는 표준 형태로 같은 작업을 합니다.

DELETE /hooks/unsubscribe/{id} Zapier

REST Hook 구독 해지. {"success": true}를 반환합니다. DELETE /hooks/{id}는 표준 형태의 동일한 작업입니다.

오류

  • 400 invalid_request

    본문이 JSON 객체가 아니거나 필수 필드가 누락되었습니다.

  • 400 invalid_parameter

    쿼리 매개변수의 형식이 잘못되었거나 허용 범위를 벗어났습니다. details.parameter에 문제가 된 매개변수가 표시됩니다.

  • 401 unauthenticated

    토큰이 전송되지 않았습니다. X-API-KEY 또는 Authorization: Bearer를 추가하세요.

  • 401 invalid_token

    토큰과 일치하는 계정이 없습니다. 토큰이 재생성되었을 수 있습니다.

  • 403 plan_required

    계정의 요금제에 이 엔드포인트나 기능이 포함되어 있지 않습니다. details.upgrade_url이 결제 페이지를 가리킵니다.

  • 403 forbidden

    인증되었지만 권한이 없습니다 — 예: 멤버가 소유자 전용 작업을 시도하는 경우.

  • 404 not_found

    이 워크스페이스에 해당 리소스가 없습니다. 다른 워크스페이스의 ID는 권한 없음이 아니라 찾을 수 없음으로 보고됩니다.

  • 409 conflict

    요청이 현재 상태와 충돌합니다 — 웹훅 한도에 도달했거나 계정에 아직 워크스페이스가 없습니다.

  • 422

    POST /links/bulk에서 배치의 모든 행이 실패했을 때 반환됩니다. meta.errors를 확인하세요.

  • 429 rate_limited

    최근 1분간 요청이 600회를 넘었습니다. Retry-After초만큼 기다리세요.

레거시 GET /visits와 GET /links/{id}/visits 엔드포인트는 자체 필드 이름을 가진 순수 JSON 배열을 반환하며, 이미 이를 기반으로 만든 Zapier 연동을 위해 고정되어 있습니다. 새 코드에는 표준 래퍼와 페이지네이션을 지원하는 GET /events와 GET /links/{id}/events를 사용하세요.

아직 궁금한 점이 있으신가요?

도와드리겠습니다. 연락 주시면 최대한 빨리 답변드리겠습니다.