API reference Create and manage short links and read their stats from your own code, with a JSON REST API.
Authentication Create a token under Settings, API. A token belongs to one workspace and either only reads or also writes. Send it as a bearer token with every request. Create a token
curl https://v2.exx.us/api/v1/links \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Accept: application/json" Limits and errors Each workspace can make 120 reading and 30 writing requests a minute. The X-RateLimit-Remaining header says how many are left. Clicks on your links are never limited.
Errors come as JSON with a message, and for invalid input an errors object per field.
401 No token, or not an API token.
403 The token is read-only, you left its workspace, or the plan has no API access.
404 No link in this workspace matches.
422 The input is not valid, or a plan limit is reached. errors lists each field.
429 Too many requests. Retry-After says how many seconds to wait. Endpoints Every path starts at https://v2.exx.us/api/v1.
The links in the workspace, newest first, with their all-time clicks.
q string Matches part of the slug, destination or page title.
status active | archived | blocked Only links with this status.
campaign_id integer Only links in this campaign.
channel_id integer Only links in this channel.
per_page integer 1 to 100, 25 by default.
cursor string meta.next_cursor from the previous page. {
"data": [
{ "id": 4182, "slug": "launch", "status": "active", "clicks": { "clicks": 1520, "unique_clicks": 1311 }, ... }
],
"meta": { "per_page": 25, "next_cursor": "eyJzaG9ydF91cmxzLmNyZWF0ZWRfYXQiOi..." }
} Answers 201 with the new link. Counts towards the new links a month of your plan.
destination_url string Required. Where the link points.
slug string A custom alias of 2 to 100 letters, numbers, hyphens or underscores. Left out, a random one.
domain_id integer From GET /domains. Left out, the default domain.
expires_at ISO 8601 When the link stops working.
click_limit integer The link stops working after this many clicks.
campaign_id integer Puts the link in this campaign.
channel_id integer Adds the link to this channel. {
"data": {
"id": 4182,
"short_url": "https://urlverkorten.nl/launch",
"slug": "launch",
"domain": "urlverkorten.nl",
"destination_url": "https://example.com/spring-sale",
"title": "Spring sale",
"status": "active",
"expires_at": null,
"click_limit": null,
"has_password": false,
"campaign_id": 12,
"channel_ids": [3],
"created_at": "2026-11-09T10:15:00+00:00"
}
} Get a link GET /links/{id}
One link, with its all-time clicks.
{
"data": {
"id": 4182,
"short_url": "https://urlverkorten.nl/launch",
"slug": "launch",
"domain": "urlverkorten.nl",
"destination_url": "https://example.com/spring-sale",
"title": "Spring sale",
"status": "active",
"expires_at": null,
"click_limit": null,
"has_password": false,
"campaign_id": 12,
"channel_ids": [3],
"created_at": "2026-11-09T10:15:00+00:00"
}
} Change a link PATCH /links/{id}
Only the fields you send change. Send null to clear expires_at, click_limit or campaign_id.
destination_url string Where the link points.
expires_at ISO 8601 | null When the link stops working.
click_limit integer | null The link stops working after this many clicks.
archived boolean Archived links keep working but leave the active list.
campaign_id integer | null Puts the link in this campaign.
channel_ids integer[] Replaces the channels of the link. {
"data": {
"id": 4182,
"short_url": "https://urlverkorten.nl/launch",
"slug": "launch",
"domain": "urlverkorten.nl",
"destination_url": "https://example.com/spring-sale",
"title": "Spring sale",
"status": "active",
"expires_at": null,
"click_limit": null,
"has_password": false,
"campaign_id": 12,
"channel_ids": [3],
"created_at": "2026-11-09T10:15:00+00:00"
}
} Delete a link DELETE /links/{id}
Answers 204. The link stops redirecting at once.
Link stats GET /links/{id}/stats
Clicks over a period: totals, the previous period, a daily trend, and breakdowns by country, device, browser and referrer. Bots are left out.
period 7, 30, 90, 365, 730 Days to cover, 30 by default. Your plan sets how far back you can look. {
"data": {
"period_days": 30,
"totals": { "clicks": 412, "unique_clicks": 377 },
"previous_totals": { "clicks": 365, "unique_clicks": 340 },
"daily": [{ "date": "2026-10-11", "clicks": 12, "unique_clicks": 11 }, ...],
"breakdowns": { "countries": [...], "devices": [...], "browsers": [...], "referrers": [...] },
"ab_variants": []
}
} Workspace analytics GET /analytics
Clicks across the whole workspace: a daily trend, the top links and the same breakdowns.
period 14, 30, 90, 365, 730 Days to cover, 14 by default. Your plan sets how far back you can look. The workspace of the token, its plan, and how much of each limit it uses.
{
"data": {
"id": 7,
"name": "Marketing",
"plan": { "key": "free", "name": "Free", "stats_days": 90 },
"usage": { "links_per_month": { "used": 12, "limit": 100 }, ... }
}
} The domains you can create links on: the shared ones and your own verified ones.
The campaigns in the workspace, starred first.
The channels in the workspace, starred first.