Build with the LinkShort API.
Programmatically create short links, track clicks, manage custom domains, and receive real-time webhook events. Every endpoint, parameter, and response — documented.
https://api.tooldit.com/api/v133 endpoints
REST · JSON
8 webhook events
HMAC-signed
Global CDN
< 50ms latency
Bulk operations
1,000 per request
Introduction
The LinkShort API lets you programmatically create and manage short links, track analytics, and receive real-time webhook notifications. All responses are JSON.
Base URL
https://api.tooldit.com/api/v1All requests must be made over HTTPS. HTTP requests will be rejected.
Authentication
The API supports two authentication methods. API Keys are recommended for server-to-server integrations.
Method 1 — API Key (Recommended)
Generate an API key from your dashboard and pass it in the Authorization header:
curl https://api.tooldit.com/api/v1/links \
-H "Authorization: sk_your_api_key_here"Method 2 — JWT Token
For dashboard integrations, use a JWT obtained from the login endpoint:
curl https://api.tooldit.com/api/v1/links \
-H "Authorization: Bearer your_jwt_token"Get your API key from your API Keys dashboard.
Rate Limits
Rate limits are applied per API key and reset every minute.
| Plan | Requests / minute |
|---|---|
| Free | 30 |
| Pro | 200 |
| Business | 1,000 |
Rate limit info is returned in response headers:
X-RateLimit-Limit: 30
X-RateLimit-Remaining: 25
X-RateLimit-Reset: 1234567890When exceeded you will receive a 429 response:
{
"success": false,
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Too many requests"
}
}Error Handling
All errors follow a consistent format:
{
"success": false,
"data": null,
"error": {
"code": "ERROR_CODE",
"message": "Human readable message"
},
"timestamp": "2026-04-29T12:00:00Z"
}| Error Code | HTTP | Description |
|---|---|---|
| LINK_NOT_FOUND | 404 | Link does not exist |
| LINK_EXPIRED | 410 | Link has expired |
| SLUG_ALREADY_EXISTS | 409 | Custom slug is already taken |
| INVALID_URL | 400 | URL format is invalid |
| PLAN_LIMIT_EXCEEDED | 402 | Monthly link or click limit reached |
| API_KEY_INVALID | 401 | Invalid or revoked API key |
| RATE_LIMIT_EXCEEDED | 429 | Too many requests per minute |
| UNAUTHORIZED | 401 | Missing or invalid authentication |
Link Expiry Format
The expiresIn field accepts duration strings:
| Value | Description |
|---|---|
| "30m" | 30 minutes |
| "1h" | 1 hour |
| "12h" | 12 hours |
| "1d" | 1 day |
| "7d" | 7 days |
| "30d" | 30 days |
| "3mo" | 3 months |
| "1y" | 1 year |
| "10y" | 10 years |
| "never" | Never expires (Pro/Business only) |
Free plan: maximum expiry is 7 days. Values exceeding this are clamped.
Create Link
/api/v1/linksCreates a new short link.
Request body
| Parameter | Type | Required | Description |
|---|---|---|---|
| originalUrl | string | Required | The destination URL to shorten |
| customSlug | string | Optional | Custom short code (alphanumeric, hyphens, underscores) |
| title | string | Optional | Human-readable label for the link |
| expiresIn | string | Optional | Expiry duration — see Expiry Format |
| singleUse | boolean | Optional | Auto-disable after the first click |
| domain | string | Optional | Custom domain hostname (must be verified) |
Request example
curl -X POST https://api.tooldit.com/api/v1/links \
-H "Authorization: sk_your_key" \
-H "Content-Type: application/json" \
-d '{
"originalUrl": "https://youtube.com/watch?v=abc",
"customSlug": "my-video",
"title": "My YouTube Video",
"expiresIn": "7d",
"singleUse": false
}'Response — 200 OK
{
"success": true,
"data": {
"id": 1,
"shortCode": "my-video",
"shortUrl": "https://go.tooldit.com/my-video",
"originalUrl": "https://youtube.com/watch?v=abc",
"title": "My YouTube Video",
"status": "ACTIVE",
"singleUse": false,
"usedCount": 0,
"expiresAt": "2026-05-06T12:00:00Z",
"createdAt": "2026-04-29T12:00:00Z",
"source": "API"
}
}List Links
/api/v1/linksReturns a paginated list of your links.
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| page | number | Default: 0 | Page number (zero-based) |
| size | number | Default: 20 | Items per page (max 100) |
| search | string | Optional | Search by short code, URL, or title |
| status | string | Default: ALL | Filter: ACTIVE, EXPIRED, USED, DISABLED |
Response — 200 OK
{
"success": true,
"data": {
"content": [
{
"id": 1,
"shortCode": "my-video",
"shortUrl": "https://go.tooldit.com/my-video",
"originalUrl": "https://youtube.com/watch?v=abc",
"title": "My YouTube Video",
"status": "ACTIVE",
"usedCount": 42,
"createdAt": "2026-04-29T12:00:00Z"
}
],
"page": 0,
"size": 20,
"total": 150,
"totalPages": 8
}
}Get Link
/api/v1/links/{shortCode}Returns a single link by its short code.
Path parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| shortCode | string | Required | The short code of the link |
Response — 200 OK
{
"success": true,
"data": { ...link object }
}Update Link
/api/v1/links/{shortCode}Updates an existing link. All fields are optional — only provided fields are changed.
Request body
| Parameter | Type | Required | Description |
|---|---|---|---|
| originalUrl | string | Optional | New destination URL |
| title | string | Optional | New title |
| expiresIn | string | Optional | New expiry duration |
| singleUse | boolean | Optional | Toggle single-use mode |
| status | string | Optional | ACTIVE or DISABLED |
Response — 200 OK
{
"success": true,
"data": { ...updated link object }
}Delete Link
/api/v1/links/{shortCode}Permanently deletes a link and all its analytics data.
Response — 200 OK
{
"success": true,
"data": null,
"message": "Link deleted successfully"
}Bulk Create Links
/api/v1/links/bulkCreates multiple links in a single request. Plan limits: Free — 10, Pro — 100, Business — 1,000.
Request body
{
"links": [
{
"originalUrl": "https://example.com/1",
"customSlug": "link-1"
},
{
"originalUrl": "https://example.com/2"
}
]
}Response — 200 OK
{
"success": true,
"data": {
"created": [
{
"shortCode": "link-1",
"shortUrl": "https://go.tooldit.com/link-1",
"originalUrl": "https://example.com/1"
}
],
"failed": [
{
"index": 2,
"error": "Slug already exists"
}
],
"totalCreated": 9,
"totalFailed": 1
}
}Bulk Action
/api/v1/links/bulk-actionPerform an action on multiple links at once.
Request body
| Parameter | Type | Required | Description |
|---|---|---|---|
| shortCodes | string[] | Required | Array of short codes to act on |
| action | string | Required | Action: DELETE, DISABLE, ENABLE |
Response — 200 OK
{
"success": true,
"data": {
"processed": 5,
"failed": 0
}
}Overview Analytics
/api/v1/analytics/overviewReturns aggregated analytics across all your links.
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| range | string | Default: 7d | Time range: 7d, 30d, 90d, all |
Response — 200 OK
{
"success": true,
"data": {
"summary": {
"totalClicks": 1240,
"uniqueClicks": 980,
"totalLinks": 45,
"activeLinks": 38
},
"clicksOverTime": [
{ "date": "2026-04-22", "clicks": 180 },
{ "date": "2026-04-23", "clicks": 210 }
],
"topLinks": [
{
"shortCode": "my-video",
"shortUrl": "https://go.tooldit.com/my-video",
"clicks": 320
}
],
"topCountries": [
{ "name": "United States", "clicks": 450 }
],
"topCities": [
{ "name": "New York", "clicks": 120 }
],
"topDevices": [
{ "name": "Mobile", "clicks": 680 }
],
"topBrowsers": [
{ "name": "Chrome", "clicks": 590 }
],
"topReferrers": [
{ "name": "twitter.com", "clicks": 230 }
]
}
}Link Analytics
/api/v1/analytics/links/{shortCode}Returns detailed analytics for a specific link.
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| shortCode | string | Required | The link short code |
| range | string | Default: 7d | Time range: 7d, 30d, 90d, all |
Response — 200 OK
{
"success": true,
"data": {
"link": { ...link object },
"summary": { "totalClicks": 320, "uniqueClicks": 280 },
"clicksOverTime": [ ... ],
"topCountries": [ ... ],
"topDevices": [ ... ],
"topReferrers": [ ... ]
}
}Generate Key
/api/v1/keysGenerates a new API key. The raw key value is only returned once.
Request body
| Parameter | Type | Required | Description |
|---|---|---|---|
| name | string | Required | A human-readable label for the key |
Response — 200 OK
{
"success": true,
"data": {
"id": 1,
"name": "Production key",
"key": "sk_live_abc123...",
"createdAt": "2026-04-29T12:00:00Z"
}
}List Keys
/api/v1/keysReturns all API keys for the authenticated user. Key values are masked.
Response — 200 OK
{
"success": true,
"data": [
{
"id": 1,
"name": "Production key",
"keyPreview": "sk_live_abc1...xyz9",
"lastUsedAt": "2026-04-28T10:00:00Z",
"createdAt": "2026-04-29T12:00:00Z"
}
]
}Delete Key
/api/v1/keys/{id}Permanently revokes and deletes an API key. All requests using it will fail immediately.
Response — 200 OK
{
"success": true,
"data": null
}Regenerate Key
/api/v1/keys/{id}/regenerateIssues a new secret value for an existing key. The old value is immediately invalidated. The new key is returned only once.
Response — 200 OK
{
"success": true,
"data": {
"id": 1,
"name": "Production key",
"key": "sk_live_newvalue...",
"createdAt": "2026-04-29T12:00:00Z"
}
}Add Domain
/api/v1/domainsRegisters a custom domain for use with your short links.
Request body
| Parameter | Type | Required | Description |
|---|---|---|---|
| hostname | string | Required | The domain hostname (e.g. go.yourapp.com) |
Response — 200 OK
{
"success": true,
"data": {
"id": 1,
"hostname": "go.yourapp.com",
"isVerified": false,
"verificationMethod": "DNS_TXT",
"verificationToken": "linkshort-verify=abc123",
"createdAt": "2026-04-29T12:00:00Z"
}
}List Domains
/api/v1/domainsReturns all custom domains for the authenticated user.
Response — 200 OK
{
"success": true,
"data": [
{
"id": 1,
"hostname": "go.yourapp.com",
"isVerified": true,
"sslStatus": "ACTIVE",
"createdAt": "2026-04-29T12:00:00Z"
}
]
}Delete Domain
/api/v1/domains/{id}Removes a custom domain. Links using it will fall back to the default domain.
Response — 200 OK
{
"success": true,
"data": null
}Verify Domain
/api/v1/domains/{id}/verifyTriggers a DNS verification check. Add the TXT record to your DNS before calling this endpoint.
Response — 200 OK
{
"success": true,
"data": {
"id": 1,
"hostname": "go.yourapp.com",
"isVerified": true,
"verifiedAt": "2026-04-29T12:30:00Z"
}
}Register Endpoint
/api/v1/webhooksRegisters a new webhook endpoint to receive event notifications.
Request body
| Parameter | Type | Required | Description |
|---|---|---|---|
| url | string | Required | HTTPS URL that will receive events |
| description | string | Optional | Human-readable label |
| events | string[] | Required | Array of event types to subscribe to |
| isActive | boolean | Default: true | Enable or disable the endpoint |
Request example
{
"url": "https://yourapp.com/webhooks/linkshort",
"description": "Production webhook",
"events": ["link.clicked", "link.created"],
"isActive": true
}Response — 200 OK
{
"success": true,
"data": {
"id": 1,
"url": "https://yourapp.com/webhooks/linkshort",
"description": "Production webhook",
"secret": "whsec_abc123...",
"events": ["link.clicked", "link.created"],
"isActive": true,
"failureCount": 0,
"createdAt": "2026-04-29T12:00:00Z"
}
}secret securely — it is only returned at creation time and used to verify webhook signatures.List Endpoints
/api/v1/webhooksReturns all webhook endpoints for the authenticated user.
Response — 200 OK
{
"success": true,
"data": [
{
"id": 1,
"url": "https://yourapp.com/webhooks/linkshort",
"description": "Production webhook",
"events": ["link.clicked", "link.created"],
"isActive": true,
"failureCount": 0,
"createdAt": "2026-04-29T12:00:00Z"
}
]
}Update Endpoint
/api/v1/webhooks/{id}Updates an existing webhook endpoint configuration.
Request body
| Parameter | Type | Required | Description |
|---|---|---|---|
| url | string | Optional | New destination URL |
| description | string | Optional | New description |
| events | string[] | Optional | Updated list of subscribed events |
| isActive | boolean | Optional | Enable or disable the endpoint |
Response — 200 OK
{
"success": true,
"data": { ...updated endpoint object }
}Delete Endpoint
/api/v1/webhooks/{id}Permanently removes a webhook endpoint and stops all future deliveries.
Response — 200 OK
{
"success": true,
"data": null
}Rotate Secret
/api/v1/webhooks/{id}/rotate-secretGenerates a new signing secret. Update your verification logic immediately — the old secret is invalidated.
Response — 200 OK
{
"success": true,
"data": {
"id": 1,
"secret": "whsec_newvalue123..."
}
}View Deliveries
/api/v1/webhooks/{id}/deliveriesReturns the delivery history for a webhook endpoint.
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| page | number | Default: 0 | Page number |
| size | number | Default: 20 | Items per page |
| status | string | Optional | Filter: SUCCESS, FAILED, PENDING |
Response — 200 OK
{
"success": true,
"data": {
"content": [
{
"id": 1,
"eventType": "link.clicked",
"status": "SUCCESS",
"httpStatus": 200,
"attemptCount": 1,
"createdAt": "2026-04-29T12:00:00Z",
"deliveredAt": "2026-04-29T12:00:01Z"
}
],
"page": 0,
"total": 45
}
}Retry Delivery
/api/v1/webhooks/{id}/deliveries/{deliveryId}/retryManually retries a failed or pending webhook delivery.
Response — 200 OK
{
"success": true,
"data": {
"id": 1,
"status": "SUCCESS",
"httpStatus": 200,
"attemptCount": 2
}
}Test Endpoint
/api/v1/webhooks/{id}/testSends a test event to verify your endpoint is reachable and your signature verification works.
Response — 200 OK
{
"success": true,
"data": {
"httpStatus": 200,
"responseTime": 124,
"message": "Test delivery successful"
}
}link.clicked
Fired whenever someone clicks a short link.
{
"id": "evt_01abc123def456",
"event": "link.clicked",
"timestamp": "2026-04-29T12:00:00Z",
"data": {
"shortCode": "my-video",
"shortUrl": "https://go.tooldit.com/my-video",
"originalUrl": "https://youtube.com/watch?v=abc",
"country": "Pakistan",
"city": "Lahore",
"device": "Mobile",
"browser": "Chrome",
"referrer": "https://twitter.com",
"clickedAt": "2026-04-29T12:00:00Z"
}
}link.expired
Fired when a link passes its expiry time and becomes inactive.
{
"id": "evt_01abc123def457",
"event": "link.expired",
"timestamp": "2026-05-06T00:00:00Z",
"data": {
"shortCode": "my-video",
"shortUrl": "https://go.tooldit.com/my-video",
"originalUrl": "https://youtube.com/watch?v=abc",
"expiredAt": "2026-05-06T00:00:00Z"
}
}link.used
Fired when a single-use link is consumed (first click triggers disable).
{
"id": "evt_01abc123def458",
"event": "link.used",
"timestamp": "2026-04-29T14:00:00Z",
"data": {
"shortCode": "promo-2026",
"shortUrl": "https://go.tooldit.com/promo-2026",
"originalUrl": "https://example.com/promo",
"country": "United States",
"device": "Desktop",
"usedAt": "2026-04-29T14:00:00Z"
}
}link.created
Fired when a new short link is created via API or dashboard.
{
"id": "evt_01abc123def459",
"event": "link.created",
"timestamp": "2026-04-29T10:00:00Z",
"data": {
"shortCode": "my-video",
"shortUrl": "https://go.tooldit.com/my-video",
"originalUrl": "https://youtube.com/watch?v=abc",
"title": "My YouTube Video",
"source": "API",
"createdAt": "2026-04-29T10:00:00Z"
}
}link.deleted
Fired when a link is permanently deleted.
{
"id": "evt_01abc123def460",
"event": "link.deleted",
"timestamp": "2026-04-29T18:00:00Z",
"data": {
"shortCode": "old-link",
"shortUrl": "https://go.tooldit.com/old-link",
"originalUrl": "https://example.com/old",
"deletedAt": "2026-04-29T18:00:00Z"
}
}link.disabled
Fired when a link is manually disabled or auto-disabled after single use.
{
"id": "evt_01abc123def461",
"event": "link.disabled",
"timestamp": "2026-04-29T16:00:00Z",
"data": {
"shortCode": "my-video",
"shortUrl": "https://go.tooldit.com/my-video",
"originalUrl": "https://youtube.com/watch?v=abc",
"reason": "manual",
"disabledAt": "2026-04-29T16:00:00Z"
}
}plan.limit_reached
Fired when a usage limit (links or clicks) is 100% reached for the billing period.
{
"id": "evt_01abc123def462",
"event": "plan.limit_reached",
"timestamp": "2026-04-29T12:00:00Z",
"data": {
"plan": "FREE",
"limitType": "links",
"limit": 100,
"used": 100,
"resetAt": "2026-05-01T00:00:00Z"
}
}plan.limit_warning
Fired when usage reaches 90% of a plan limit, giving time to upgrade or pause campaigns.
{
"id": "evt_01abc123def463",
"event": "plan.limit_warning",
"timestamp": "2026-04-29T11:00:00Z",
"data": {
"plan": "FREE",
"limitType": "clicks",
"limit": 1000,
"used": 900,
"percentUsed": 90,
"resetAt": "2026-05-01T00:00:00Z"
}
}Verifying Webhook Signatures
Every webhook request includes a signature header so you can confirm it originated from LinkShort:
X-LinkShort-Signature: t=1234567890,v1=abc123def456...Verification steps:
- Extract
t(timestamp) andv1(signature) from the header - Build the signed string:
timestamp + "." + raw_request_body - Compute HMAC-SHA256 of the signed string using your webhook secret
- Compare with
v1using a constant-time comparison
const crypto = require('crypto')
function verifyWebhook(rawBody, header, secret) {
const parts = header.split(',')
const timestamp = parts[0].split('=')[1]
const signature = parts[1].split('=')[1]
const signed = timestamp + '.' + rawBody
const expected = crypto
.createHmac('sha256', secret)
.update(signed)
.digest('hex')
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signature)
)
}Postman Collection
Download the official Postman collection to test every API endpoint instantly. All 33 endpoints are pre-configured with variable placeholders.
Download Postman CollectionSetup instructions
- Download the collection and import it into Postman
- Create a Postman environment with these variables:Text
base_url = https://api.tooldit.com api_key = sk_your_key_here - Select your environment and run any request immediately
What's included
- Links — 7 endpoints (create, list, get, update, delete, bulk create, bulk action)
- Analytics — 3 endpoints (overview, link analytics, account stats)
- API Keys — 5 endpoints (generate, list, delete, regenerate, usage)
- Domains — 4 endpoints (add, list, delete, verify)
- Webhooks — 9 endpoints (register, list, get, update, delete, rotate, deliveries, retry, test)
- Auth — 5 endpoints (login, register, verify email, forgot password, reset password)
View the full API reference on this page .