API documentation

A REST API to create short links and read analytics. Easy to integrate from any language — just send your API key.

Quick start

  1. Sign up and open the API keys section in your dashboard.
  2. Create a key and copy it (shown only once).
  3. Send the key in the header Authorization.
bash
curl -X POST https://link.tj/api/v1/links \
  -H "Authorization: Bearer lnktj_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com/a/very/long/link"}'

Authentication

Every request must include the header Authorization: Bearer <key> (or X-Api-Key: <key>). Base URL:

text
https://link.tj/api/v1

Keys are tied to your account, so the same plan limits apply as in the UI. CORS is open (*); the API can be called from the browser.

Conditions & requirements

  • You need a link.tj account and a created API key (the “API keys” section).
  • The key is shown once — copy it right away.
  • Send the header Authorization: Bearer <key> or X-Api-Key: <key>.
  • Your plan limits apply.
  • Rate limit: 120 requests per minute per key (otherwise 429 with retryAfter).
  • CORS is open (*) — the API can be called from the browser.

App authentication (mobile)

Mobile and native apps obtain a token with email and password: POST /api/auth/token. The response contains a JWT that you pass to /api/v1/* exactly like an API key: Authorization: Bearer <token>. If 2FA is enabled, include a code field; without it the endpoint returns 401 with error: needs_2fa.

POST/api/auth/token

Email/password login → JWT for /api/v1/*. Body: email, password, code? (for 2FA).

How to use: Send email and password to obtain a JWT for the app.

bash
curl -X POST https://link.tj/api/auth/token \
  -H "Content-Type: application/json" \
  -d '{"email":"you@example.com","password":"••••••••"}'

Response

json
{
  "token": "eyJhbGciOi...",
  "user": { "id": "clx...", "email": "you@example.com", "name": "You", "role": "USER" }
}

Conditions & requirements

  • email and password are required (otherwise 400).
  • The account must not be blocked (otherwise 403).
  • If 2FA is enabled, add a code field; without it 401 with error: needs_2fa.
  • Limit: 30 attempts per 15 min per IP and 10 per email (otherwise 429).
  • Pass the returned JWT to /api/v1/* as Authorization: Bearer <token>.

Endpoints

GET/api/v1/me

Account, plan and monthly usage.

How to use: Request your profile, plan and current-month usage.

bash
curl https://link.tj/api/v1/me \
  -H "Authorization: Bearer lnktj_API_KEY"

Response

json
{
  "data": {
    "email": "you@example.com",
    "plan": { "name": "Free", "linkLimit": 5, "clickLimit": 1000 },
    "usage": { "linksUsed": 2, "linksRemaining": 3, "clicksUsed": 87 }
  }
}

Conditions & requirements

  • A valid API key or JWT is required.
  • The account must not be blocked.
  • GET only — data belongs to the key owner.
POST/api/v1/links

Create a short link. Fields: url (required for kind=url), slug (optional), title (optional), kind (optional: url | wifi | vcard | text), payload (object for non-url kinds).

wifi/vcard/text kinds require a paid plan (otherwise 403). Such a QR points to a tracked page /l/{slug}.

How to use: Send JSON with a url field (classic link) or kind + payload (typed link).

bash
curl -X POST https://link.tj/api/v1/links \
  -H "Authorization: Bearer lnktj_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com","slug":"promo","title":"Promo"}'

Example: Wi-Fi (paid plan)

bash
curl -X POST https://link.tj/api/v1/links \
  -H "Authorization: Bearer lnktj_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"kind":"wifi","title":"Guest Wi-Fi","payload":{"ssid":"MyNet","encryption":"WPA","password":"secret123"}}'

Response 201

json
{
  "data": {
    "id": "clx...",
    "slug": "promo",
    "shortUrl": "https://link.tj/promo",
    "kind": "url",
    "originalUrl": "https://example.com",
    "summary": null,
    "clicks": 0
  },
  "usage": { "linksUsed": 3, "linkLimit": 5, "linksRemaining": 2 }
}

Response 402 — plan limit reached

json
{
  "error": "limit_reached",
  "code": "limit_reached",
  "recommend": { "name": "Core", "priceMonthly": 8, "linkLimit": 100 }
}

Conditions & requirements

  • url is required and must be a valid http(s) URL when kind=url (otherwise 400).
  • slug is optional; if taken → 409, if empty it is generated automatically.
  • kind: url | wifi | vcard | text (default url).
  • wifi/vcard/text types require a paid plan (otherwise 403) and a payload field.
  • title is optional, max 200 characters.
  • When the plan link limit is reached you get 402 with a recommended plan.
GET/api/v1/links

A list of your links. Parameters: limit (1–100), offset.

How to use: Fetch your links with pagination.

bash
curl "https://link.tj/api/v1/links?limit=20&offset=0" \
  -H "Authorization: Bearer lnktj_API_KEY"

Response — list

json
{
  "total": 42,
  "limit": 20,
  "offset": 0,
  "data": [
    { "id": "clx...", "slug": "promo", "shortUrl": "https://link.tj/promo", "kind": "url",
      "originalUrl": "https://example.com", "clicks": 128, "isActive": true }
  ]
}

Conditions & requirements

  • limit from 1 to 100 (default 50).
  • offset ≥ 0 for pagination.
  • Only the key owner’s links are returned; the total field is the overall count.
GET/api/v1/links/{id_or_slug}

Link details and click analytics (by country, device, source).

How to use: Pass a link id or slug to get its details and click analytics.

bash
curl https://link.tj/api/v1/links/promo \
  -H "Authorization: Bearer lnktj_API_KEY"

Response — details & analytics

json
{
  "data": {
    "id": "clx...", "slug": "promo", "shortUrl": "https://link.tj/promo",
    "originalUrl": "https://example.com", "clicks": 128, "isActive": true,
    "analytics": {
      "byCountry":  [ { "name": "TJ", "count": 80 }, { "name": "RU", "count": 48 } ],
      "byDevice":   [ { "name": "mobile", "count": 95 }, { "name": "desktop", "count": 33 } ],
      "byReferrer": [ { "name": "t.me", "count": 60 }, { "name": "Unknown", "count": 68 } ]
    }
  }
}

Conditions & requirements

  • Either an id or a slug is accepted.
  • The link must belong to the key owner (otherwise 404).
  • The response includes an analytics block: by country, device and referrer.
DELETE/api/v1/links/{id_or_slug}

Delete a link and its analytics.

How to use: Pass an id or slug to delete the link.

bash
curl -X DELETE https://link.tj/api/v1/links/promo \
  -H "Authorization: Bearer lnktj_API_KEY"

Conditions & requirements

  • The link must belong to the key owner (otherwise 404).
  • Deletion is irreversible and removes the associated analytics.

Webhooks (outbound events)

Register a URL in the “Webhooks” section of your dashboard — we POST to your server when an event fires. Body: { event, data, ts }. Headers: X-LinkTJ-Event and X-LinkTJ-Signature.

How to use: Add a URL and subscribe to events in the “Webhooks” section, then verify the signature on your server.

Events

  • link.createdlink.created — a short link was created. data: { id, slug, url }.
  • subscription.createdsubscription.created — a subscription was started or renewed. data: { plan, amount, interval }.
http
POST https://your-server.example/webhook
X-LinkTJ-Event: link.created
X-LinkTJ-Signature: <hmac-sha256-hex>
Content-Type: application/json

{ "event": "link.created", "data": { "id": "clx...", "slug": "promo", "url": "https://example.com" }, "ts": "2026-01-01T12:00:00.000Z" }

The X-LinkTJ-Signature header is an HMAC-SHA256 of the raw request body signed with your webhook secret (whsec_…). Compare it to verify the request really came from link.tj.

Conditions & requirements

  • The URL must be public https — internal and local addresses are rejected (SSRF protection).
  • Subscribe to at least one event (link.created, subscription.created).
  • Verify X-LinkTJ-Signature = HMAC-SHA256(request body, secret whsec_…).
  • The host is re-checked on delivery (DNS-rebinding protection); redirects are not followed.
  • Delivery timeout is 5 seconds; respond with a 2xx code.

Scheduled jobs (cron)

An external scheduler can trigger subscription renewals and weekly reports by passing the secret: ?secret=<CRON_SECRET>. The task parameter accepts renew, weekly or all (default all).

How to use: Point an external scheduler at the endpoint with the secret.

POST/api/cron?secret=&task=renew|weekly|all
bash
curl -X POST "https://link.tj/api/cron?secret=CRON_SECRET&task=renew"

Conditions & requirements

  • Pass ?secret=<CRON_SECRET> (or call it from a superadmin session).
  • task: renew | weekly | all (default all).
  • Method GET or POST.

Status codes

200 / 201Success
400Bad request (e.g. invalid URL or slug)
401Missing or invalid API key
402Plan limit reached — upgrade required
403Forbidden (e.g. wifi/vcard/text link types on the Free plan)
404Link not found
409Slug already taken
429Too many requests — rate limit hit

Ready to start?

Create a free account and get an API key in a minute.

Get a key