مرجع 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

قراردادها

این موارد برای همه اندپوینت‌های زیر صادق است.

پوشش پاسخ

هر پاسخ موفق یک شیء JSON با کلید data است، و یک کلید meta هر وقت چیزی درباره نتیجه برای گفتن باشد — صفحه‌بندی، بازه تاریخ تعیین‌شده، اینکه جغرافیا لحاظ شده یا نه. هر خطا یک شیء با کلید error است که یک code ثابت، یک message قابل فهم برای انسان و گاهی details دارد. تصمیم‌گیری را بر اساس 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 را گزارش می‌کند تا بدون محاسبه صفحه‌بندی کنید: با offset = meta.next_offset به فراخوانی ادامه دهید تا has_more برابر false شود.

پیمایش همه لینک‌ها
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 (در کد کوتاه، عنوان و مقصد جست‌وجو می‌کند)، type، archived، favorite و domain را می‌پذیرد. مقادیر ناشناخته به جای نادیده گرفته شدن بی‌صدا، با invalid_parameter و فهرست مقادیر مجاز رد می‌شوند.

بازه‌های تاریخ

start_date و end_date از قالب YYYY-MM-DD استفاده می‌کنند و هر دو روز را شامل می‌شوند. بازه زمانی به حداقل دوره نگهداری پلن شما محدود می‌شود — اگر از پلنی با نگهداری 30 روزه یک سال تاریخچه بخواهید، همان 30 روز موجود برگردانده می‌شود و meta.date_range بازه‌ای را که واقعاً استفاده شده به شما می‌گوید.

محدودیت نرخ

600 درخواست در دقیقه برای هر حساب، با پنجره لغزان. هر پاسخ شامل X-RateLimit-Limit، X-RateLimit-Remaining و X-RateLimit-Reset (یک مهر زمانی Unix) است. عبور از این محدودیت، پاسخ 429 rate_limited را همراه با هدر Retry-After بر حسب ثانیه برمی‌گرداند. به جای تلاش دوباره فوری، به اندازه 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 را برای تعیین بازه ارسال کنید. روزهای بدون ترافیک به جای حذف شدن با مقدار صفر برگردانده می‌شوند، بنابراین سری همیشه پیوسته است.

پاسخ 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

یک اندپوینت HTTPS ثبت کنید تا با هر کلیک ثبت‌شده یک داده JSON دریافت کند. حداکثر 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} برای جریان REST Hook در Zapier همچنان در دسترس هستند.

قدیمی

GET /auth/me قدیمی

آزمون اتصال اولیه: شناسه کاربر، ایمیل و شناسه/نام فضای کاری شما را به‌صورت یک شیء تخت برمی‌گرداند. 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 بازدید اخیر در کل فضای کاری به‌صورت یک آرایه JSON خام — بدون پوشش — با نام فیلدهای visit_id، visit_time، link_shortcode و visitor_*. این همان چیزی است که مرحله "perform list" در REST Hook در Zapier می‌خواند، بنابراین ساختار آن ثابت است. برای کارهای جدید از 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

    چنین منبعی در این فضای کاری وجود ندارد. شناسه‌های فضای کاری دیگر به جای «ممنوع» به‌صورت «پیدا نشد» گزارش می‌شوند.

  • 409 conflict

    درخواست با وضعیت فعلی تداخل دارد — به سقف وب‌هوک‌ها رسیده‌اید یا حساب هنوز فضای کاری ندارد.

  • 422

    وقتی همه ردیف‌های دسته در POST /links/bulk ناموفق باشند برگردانده می‌شود. meta.errors را بررسی کنید.

  • 429 rate_limited

    بیش از 600 درخواست در دقیقه گذشته. به اندازه Retry-After ثانیه صبر کنید.

اندپوینت‌های قدیمی GET /visits و GET /links/{id}/visits یک آرایه JSON خام با نام فیلدهای خودشان برمی‌گردانند و برای یکپارچه‌سازی‌های Zapier که روی آن‌ها ساخته شده‌اند ثابت نگه داشته شده‌اند. کد جدید باید از GET /events و GET /links/{id}/events استفاده کند که از پوشش استاندارد استفاده می‌کنند و صفحه‌بندی را پشتیبانی می‌کنند.

هنوز سؤالی دارید؟

ما برای کمک اینجا هستیم. با ما تماس بگیرید تا در اولین فرصت پاسخ دهیم.