API Reference
Complete documentation for the Lygo REST APIs. Integrate shortlinks, analytics, and domain management directly into your code.
Authentication
Lygo supports two authentication methods. Choose the one best suited to your use case.
Bearer Token (JWT)
For authenticated users. Obtain the token from the admin panel or via POST /api/auth/login.
Authorization: Bearer eyJhbGci...
API Key
For service accounts and automations. Create a key from the Service Accounts panel.
x-api-key: slk_xxxxxxxxxxxxxxxx
Base URL
All endpoints are relative to the following base URL:
https://app.lygo.it/api
All requests and responses use Content-Type: application/json.
Endpoints
Creates a new shortlink in the specified tenant. Requires editor role or higher, or links:write scope for API keys.
Request Body
{
"tenantId": "tnt_abc123", // required
"originalUrl": "https://example.com/very/long/path", // required, http/https
"urlCode": "my-link", // optional, 3-64 chars: a-zA-Z0-9_-
// 8-char nanoid auto-generated if omitted
"redirectType": "301", // optional, "301"|"302" (string), default "301"
"domainId": "dom_xyz", // optional
"password": "secret", // optional, 4-128 chars
"redirectQueryParams": { "utm_source": "newsletter" }, // optional
"activeFrom": "2025-06-01T00:00:00Z", // optional, ISO 8601
"activeUntil": "2025-12-31T23:59:59Z" // optional, ISO 8601
}
Response 201
{
"tenantId": "tnt_abc123",
"urlCode": "my-link",
"originalUrl": "https://example.com/very/long/path",
"redirectType": "301", // always a string
"isPasswordProtected": false,
"redirectQueryParams": { "utm_source": "newsletter" },
"activeFrom": "2025-06-01T00:00:00Z", // string | null
"activeUntil": "2025-12-31T23:59:59Z", // string | null
"domainId": null,
"domain": null
}
Returns a paginated list of the tenant's links. Rate limit: 120 req / 60s per IP.
Query Parameters
?tenantId=tnt_abc123 // required
&paginated=1 // include pagination envelope
&limit=50 // optional, 10-100, default 50
&cursor=base64string // optional, opaque pagination cursor (base64)
&search=my-link // optional, filters by urlCode prefix
&redirectType=301 // optional, "301"|"302"
&sort=code // optional, code|recent|oldest|clicks (default: code)
Response 200
{
"tenantId": "tnt_abc123",
"role": "owner", // owner|admin|editor|viewer
"links": [
{
"urlCode": "my-link",
"originalUrl": "https://example.com/very/long/path",
"redirectType": "301", // always a string
"isPasswordProtected": false,
"redirectQueryParams": {},
"activeFrom": null,
"activeUntil": null,
"clickCount": 42,
"createdAt": "2025-03-01T12:00:00Z",
"createdBy": "usr_abc123",
"domainId": null,
"domain": null
}
],
"pagination": {
"limit": 50,
"hasMore": true,
"nextCursor": "eyJ1cmxDb2RlIjoibXktbGluayJ9", // string | null
"search": null,
"sort": "code",
"redirectType": ""
}
}
Returns all details for a single link.
Query Parameters
?tenantId=tnt_abc123 // required
&domainId=dom_xyz // optional
&domain=custom.example.com // optional
Example
GET /api/links/my-link?tenantId=tnt_abc123
Response 200
{
"urlCode": "my-link",
"originalUrl": "https://example.com/very/long/path",
"redirectType": "301",
"isPasswordProtected": false,
"redirectQueryParams": {},
"activeFrom": null,
"activeUntil": null,
"clickCount": 42,
"createdAt": "2025-03-01T12:00:00Z",
"createdBy": "usr_abc123",
"domainId": null,
"domain": null
}
Updates an existing link. The urlCode field cannot be changed. At least one field to update must be provided.
Query Parameters
?tenantId=tnt_abc123 // required
Request Body (all fields optional, at least one required)
{
"originalUrl": "https://new-destination.com", // optional
"redirectType": "302", // optional, "301"|"302" (string)
"password": "newsecret", // optional, 4-128 chars
"clearPassword": true, // optional, removes the password
"redirectQueryParams": { "ref": "email" }, // optional
"clearRedirectQueryParams": true, // optional, removes query params
"activeFrom": "2025-06-01T00:00:00Z", // optional, ISO 8601 | null
"activeUntil": "2025-12-31T23:59:59Z" // optional, ISO 8601 | null
}
Response 200
{ "message": "Link updated" }
Permanently deletes a link. This operation is irreversible.
Query Parameters
?tenantId=tnt_abc123 // required
&domainId=dom_xyz // optional
Example
DELETE /api/links/my-link?tenantId=tnt_abc123
Response 200
{ "message": "Link deleted" }
Rate Limiting
120 requests per 60 seconds per IP
The limit applies per IP address on /api/links* endpoints. When exceeded, the API responds with 429 Too Many Requests.
// Response when rate limit is exceeded
HTTP/1.1 429 Too Many Requests
{ "error": "Too many requests" }
Error Responses
All errors follow a consistent format with an error field that describes the problem in human-readable language.
{
"error": "Link not found"
}
| Code | Meaning |
|---|---|
| 400 | Bad Request — missing parameter, wrong type, or invalid value |
| 401 | Unauthorized — JWT token or API key missing, expired, or invalid |
| 403 | Forbidden — insufficient scope (e.g., missing links:write) or tenant access denied |
| 404 | Not Found — link or resource does not exist |
| 409 | Conflict — urlCode already in use within the tenant |
| 429 | Too Many Requests — rate limit exceeded (120 req/60s per IP) |
| 503 | Service Unavailable — temporary maintenance or service unreachable |
Code Examples
Create a shortlink with fetch (TypeScript)
// POST /api/links
const response = await fetch('https://app.lygo.it/api/links', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': 'slk_xxxxxxxxxxxxxxxx',
},
body: JSON.stringify({
tenantId: 'tnt_abc123',
originalUrl: 'https://example.com/very/long/url',
redirectType: '301', // string, not a number
}),
});
const link = await response.json();
// { urlCode: 'xK8mPqZw', redirectType: '301', ... }
console.log(link.urlCode);
List links with curl
curl -G https://app.lygo.it/api/links \
-H "x-api-key: slk_xxxxxxxxxxxxxxxx" \
-d "tenantId=tnt_abc123" \
-d "paginated=1" \
-d "limit=50" \
-d "sort=clicks"
Update a link with curl
curl -X PUT "https://app.lygo.it/api/links/my-link?tenantId=tnt_abc123" \
-H "x-api-key: slk_xxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"originalUrl":"https://new-destination.com","redirectType":"302"}'
// Response: { "message": "Link updated" }
Error handling with status check
const res = await fetch('https://app.lygo.it/api/links/my-link?tenantId=tnt_abc123', {
headers: { 'x-api-key': 'slk_xxxxxxxxxxxxxxxx' },
});
if (!res.ok) {
const { error } = await res.json();
// error: "Link not found"
throw new Error(`API error ${res.status}: ${error}`);
}
const link = await res.json();
Ready to get started?
Create your account and get an API key in under 2 minutes.