API Reference
Programmatically create and manage short links with the Weeny API.
Base URL: https://weeny.app
All endpoints under /api/v1 require API key authentication and return JSON.
Authentication
Create an API key in Workspace Settings → API Keys, then send it on every request:
x-api-key: wn_your_api_key_here
Or as a Bearer token:
Authorization: Bearer wn_your_api_key_here
Keys are scoped to a single workspace. Requests only see and mutate links in that workspace.
CORS
Browser clients can call the API from any origin. Allowed headers include Content-Type, Authorization, x-api-key, and x-workspace-id.
Links
List links
GET /api/v1/links
Returns a paginated list of links in the authenticated workspace.
Query parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
page | number | 1 | Page number (minimum 1) |
limit | number | 10 | Page size (maximum 100) |
search | string | — | Filter by title, short URL, or destination URL |
status | string | all | all, active, or inactive |
tag | string | — | Filter links that include this tag |
sortBy | string | createdAt | createdAt, updatedAt, title, or clicks |
sortOrder | string | desc | asc or desc |
Example request
curl "https://weeny.app/api/v1/links?page=1&limit=10&status=active" \
-H "x-api-key: wn_your_api_key_here"
Example response
{
"data": [
{
"id": "clx...",
"title": "Campaign landing",
"shortUrl": "abc123",
"originalUrl": "https://example.com/landing",
"isActive": true,
"expiresAt": null,
"tags": ["campaign", "spring"],
"password": { "isProtected": false },
"utm": {
"source": "newsletter",
"medium": "email",
"campaign": "spring-sale",
"term": null,
"content": null
},
"meta": {
"title": null,
"url": null,
"description": null
},
"domainId": null,
"domain": null,
"clicks": 42,
"uniqueVisitors": 30,
"publicAnalytics": false,
"createdAt": "2026-07-01T12:00:00.000Z",
"updatedAt": "2026-07-02T09:30:00.000Z"
}
],
"pagination": {
"page": 1,
"limit": 10,
"totalCount": 1,
"totalPages": 1,
"hasNextPage": false,
"hasPreviousPage": false
}
}
Create a link
POST /api/v1/links
Creates a new short link. Subject to your plan’s active links limit.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
originalUrl | string | Yes | Destination URL |
title | string | No | Display title |
shortUrl | string | No | Custom slug. Auto-generated if omitted |
utm | object | No | UTM params: source, medium, campaign, term, content |
password | object | No | { "isProtected": true, "password": "secret" } to lock the link |
expiresAt | string | No | ISO 8601 expiry datetime |
tags | string[] | No | Tags for organization and filtering |
metaTitle | string | No | Open Graph / social title |
metaUrl | string | No | Open Graph / social image URL |
domain | string | No | Custom domain hostname (must belong to the workspace) |
domainId | string | No | Custom domain ID (must belong to the workspace) |
publicAnalytics | boolean | No | Whether public analytics are enabled (default false) |
If both domain and domainId are provided, they must refer to the same custom domain.
Example request
curl -X POST "https://weeny.app/api/v1/links" \
-H "Content-Type: application/json" \
-H "x-api-key: wn_your_api_key_here" \
-d '{
"originalUrl": "https://example.com/landing",
"title": "Campaign landing",
"shortUrl": "spring",
"tags": ["campaign"],
"utm": {
"source": "newsletter",
"medium": "email",
"campaign": "spring-sale"
}
}'
Example response (201)
{
"data": {
"id": "clx...",
"title": "Campaign landing",
"shortUrl": "spring",
"originalUrl": "https://example.com/landing",
"isActive": true,
"expiresAt": null,
"tags": ["campaign"],
"password": { "isProtected": false },
"utm": {
"source": "newsletter",
"medium": "email",
"campaign": "spring-sale",
"term": null,
"content": null
},
"meta": {
"title": null,
"url": null,
"description": null
},
"domainId": null,
"domain": null,
"clicks": 0,
"uniqueVisitors": 0,
"publicAnalytics": false,
"createdAt": "2026-07-18T12:00:00.000Z",
"updatedAt": "2026-07-18T12:00:00.000Z"
}
}
Get a link
GET /api/v1/links/{identifier}
Fetches a single link by ID or short URL slug within the workspace.
Example request
curl "https://weeny.app/api/v1/links/spring" \
-H "x-api-key: wn_your_api_key_here"
Example response
{
"data": {
"id": "clx...",
"title": "Campaign landing",
"shortUrl": "spring",
"originalUrl": "https://example.com/landing",
"isActive": true,
"expiresAt": null,
"tags": ["campaign"],
"password": { "isProtected": false },
"utm": {
"source": "newsletter",
"medium": "email",
"campaign": "spring-sale",
"term": null,
"content": null
},
"meta": {
"title": null,
"url": null,
"description": null
},
"domainId": null,
"domain": null,
"clicks": 42,
"uniqueVisitors": 30,
"publicAnalytics": false,
"createdAt": "2026-07-18T12:00:00.000Z",
"updatedAt": "2026-07-18T12:00:00.000Z"
}
}
Errors
Error responses use this shape:
{
"error": "Human-readable message",
"details": {}
}
| Status | Meaning |
|---|---|
400 | Invalid request (missing fields, invalid URL, bad datetime, etc.) |
401 | Missing or invalid API key |
403 | Plan limit reached (e.g. active links) |
404 | Link or custom domain not found |
409 | Short URL already taken on that domain |
500 | Unexpected server error |
When the active links limit is exceeded, details may include:
{
"code": "ACTIVE_LINKS_LIMIT_EXCEEDED",
"plan": "starter",
"limit": 100,
"currentCount": 100
}
Getting started
- Sign in to Weeny and open your workspace settings.
- Create an API key under API Keys.
- Call
GET /api/v1/linksorPOST /api/v1/linkswith your key. - Need help? Contact us or visit the Help Center.