Links
Create, list, read, update and delete short links.
Everything a link integration needs lives under one resource. Create a link, read it back, change where it points, switch it off, delete it. All five endpoints take and return the same link object, so once you have parsed one response you have parsed them all.
The examples on this page are real calls, captured against our own API and reprinted with the public host and a placeholder key. Responses are shown pretty-printed for reading; on the wire they are compact.
Create a link
/api/v1/links
Shorten one URL. This is the endpoint most integrations only ever need.
| Field | Type | Required | Description |
|---|---|---|---|
url |
string | required | The destination. Must be a well-formed http:// or https:// address. |
alias |
string | optional, Pro and above | Your own code instead of a generated one, e.g. "spring-sale". Already in use on that domain answers 409. |
expiresOn |
integer | optional, Pro and above | Unix seconds. The link stops resolving after this moment. |
password |
string | optional, Pro and above | Visitors must enter this before being forwarded. |
domain |
string | optional, Pro and above | A custom domain of yours, by host, e.g. "go.example.com". It must already be verified and active. |
curl -X POST "https://urlicer.com/api/v1/links" \
-H "Authorization: Bearer url_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com/blog/2026/the-long-article-title-goes-here"}'
{
"short": "https://ivi.to/mDpk",
"code": "mDpk",
"domain": null,
"url": "https://example.com/blog/2026/the-long-article-title-goes-here",
"clicks": 0,
"active": true,
"expiresOn": null,
"createdOn": 1787314771
}
| Field | Type | Description |
|---|---|---|
short |
string | The finished short link, absolute and ready to share. |
code |
string | Just the code part, without the domain. |
domain |
string or null | The custom domain the link lives on, or null when it uses the default ivi.to. |
url |
string | The destination the link currently points at. |
clicks |
integer | Clicks counted so far. Read live from the click counter, not a nightly total. |
active |
boolean | False for a link you have switched off. Visitors see the "link disabled" page instead of the destination. |
expiresOn |
integer or null | Unix seconds. After this moment the link stops resolving. Null means it never expires. |
createdOn |
integer | Unix seconds, UTC. |
- Codes are unique per domain. The same alias can exist once on ivi.to and once on each of your custom domains.
- Every created link counts against your plan's monthly link allowance, and the count does not go back down when you delete one.
- Omit "alias" and you get a generated code: shorter, and it can never collide.
Using every option at once
A create request can carry a custom alias, an expiry, a password and one of your own domains together. Each of those is a paid feature, and each is checked separately, so a request that asks for two features you have and one you do not is refused for the one you do not, and creates nothing.
curl -X POST "https://urlicer.com/api/v1/links" \
-H "Authorization: Bearer url_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/spring-sale",
"alias": "spring-sale",
"expiresOn": 1790000000,
"domain": "go.example.com"
}'
{
"short": "https://go.example.com/spring-sale",
"code": "spring-sale",
"domain": "go.example.com",
"url": "https://example.com/spring-sale",
"clicks": 0,
"active": true,
"expiresOn": 1790000000,
"createdOn": 1787314771
}
List links
/api/v1/links
Every link in the workspace the key belongs to, newest first.
| Field | Type | Required | Description |
|---|---|---|---|
page |
integer | optional | 1-based page number. Defaults to 1; anything lower is treated as 1. |
size |
integer | optional | Links per page. Defaults to 20, capped at 100. |
curl -X GET "https://urlicer.com/api/v1/links?page=1&size=3" \
-H "Authorization: Bearer url_live_YOUR_KEY"
{
"links": [
{
"short": "https://ivi.to/mDpk",
"code": "mDpk",
"domain": null,
"url": "https://example.com/blog/2026/the-long-article-title-goes-here",
"clicks": 0,
"active": true,
"expiresOn": null,
"createdOn": 1787314771
},
{
"short": "https://go.example.com/spring-sale",
"code": "spring-sale",
"domain": "go.example.com",
"url": "https://example.com/spring-sale",
"clicks": 128,
"active": true,
"expiresOn": 1790000000,
"createdOn": 1787314771
}
],
"total": 20077,
"page": 1,
"size": 3
}
| Field | Type | Description |
|---|---|---|
links |
array | The page of links. Each entry has the same shape as a create response. |
total |
integer | How many links the workspace holds in total, not just on this page. |
page |
integer | The page you asked for, after clamping. |
size |
integer | The page size actually used, after clamping to 100. |
- Ordering is newest first and stable, so paging through a workspace that is not being written to will not repeat or skip links.
- Click totals on a listing are read live for the whole page in one lookup, so listing 100 links costs no more than listing one.
Get one link
/api/v1/links/{code}
Look a single link up by its code, including its current click total.
| Field | Type | Required | Description |
|---|---|---|---|
code |
string | required | The code alone, never the whole short URL: "spring-sale", not "https://ivi.to/spring-sale". |
curl -X GET "https://urlicer.com/api/v1/links/spring-sale" \
-H "Authorization: Bearer url_live_YOUR_KEY"
{
"short": "https://go.example.com/spring-sale",
"code": "spring-sale",
"domain": "go.example.com",
"url": "https://example.com/spring-sale",
"clicks": 128,
"active": true,
"expiresOn": 1790000000,
"createdOn": 1787314771
}
| Field | Type | Description |
|---|---|---|
short |
string | The finished short link, absolute and ready to share. |
code |
string | Just the code part, without the domain. |
domain |
string or null | The custom domain the link lives on, or null when it uses the default ivi.to. |
url |
string | The destination the link currently points at. |
clicks |
integer | Clicks counted so far. Read live from the click counter, not a nightly total. |
active |
boolean | False for a link you have switched off. Visitors see the "link disabled" page instead of the destination. |
expiresOn |
integer or null | Unix seconds. After this moment the link stops resolving. Null means it never expires. |
createdOn |
integer | Unix seconds, UTC. |
- A code belonging to another workspace answers 404, exactly like one that does not exist. The API never confirms that someone else's link is real.
- If you hold the same alias on more than one of your domains, look the link up from a listing instead: a bare code cannot say which domain you meant.
Update a link
/api/v1/links/{code}
Change where a link points, switch it off, or set an expiry or password. Only the fields you send change.
| Field | Type | Required | Description |
|---|---|---|---|
code |
string | required | The code of the link to update. |
| Field | Type | Required | Description |
|---|---|---|---|
url |
string | optional | A new destination. Re-points the link everywhere it has already been shared. |
active |
boolean | optional | False switches the link off; visitors get the "link disabled" page. True switches it back on. |
expiresOn |
integer or null | optional | Unix seconds to set an expiry, or null to remove one. |
password |
string | optional | A new password, or an empty string to remove the one it has. |
curl -X PATCH "https://urlicer.com/api/v1/links/spring-sale" \
-H "Authorization: Bearer url_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com/spring-sale-2026"}'
{
"short": "https://go.example.com/spring-sale",
"code": "spring-sale",
"domain": "go.example.com",
"url": "https://example.com/spring-sale-2026",
"clicks": 128,
"active": true,
"expiresOn": 1790000000,
"createdOn": 1787314771
}
| Field | Type | Description |
|---|---|---|
short |
string | The finished short link, absolute and ready to share. |
code |
string | Just the code part, without the domain. |
domain |
string or null | The custom domain the link lives on, or null when it uses the default ivi.to. |
url |
string | The destination the link currently points at. |
clicks |
integer | Clicks counted so far. Read live from the click counter, not a nightly total. |
active |
boolean | False for a link you have switched off. Visitors see the "link disabled" page instead of the destination. |
expiresOn |
integer or null | Unix seconds. After this moment the link stops resolving. Null means it never expires. |
createdOn |
integer | Unix seconds, UTC. |
- The code itself cannot be changed. It is already out in the world on posters, emails and chat messages, so a new code means a new link.
- Sending no fields at all answers 400 rather than silently doing nothing.
- Changing the destination re-runs our safety check on the new address. A link we had merely flagged is cleared; one we had blocked outright stays blocked.
- If your plan no longer includes a feature, you can still edit links that already use it. You just cannot add it to a link that does not.
Delete a link
/api/v1/links/{code}
Remove a link for good. Anyone following it afterwards gets a 404.
| Field | Type | Required | Description |
|---|---|---|---|
code |
string | required | The code of the link to delete. |
curl -X DELETE "https://urlicer.com/api/v1/links/spring-sale" \
-H "Authorization: Bearer url_live_YOUR_KEY"
{"success": true}
- Deleting is immediate and cannot be undone. To take a link out of circulation while keeping the option to bring it back, PATCH it to "active": false instead.
- Deleting does not give back monthly link allowance: the allowance counts links created, not links held.
- A custom alias becomes free again once the link is deleted, so you can create a new link on the same code. A generated code is never handed out twice.
- Click history already recorded stays in your analytics, shown against the deleted link.
Times and click counts
Every time in a response is unix seconds in UTC, including
createdOn and expiresOn, and including the expiry you send when
creating a link. Milliseconds will be read as a date tens of thousands of years from now,
which is a valid expiry and will not be rejected, so it is worth checking that conversion
first when an expiry behaves oddly.
clicks is read live at the moment you ask, from the same counter the redirect
itself increments, so it includes clicks from seconds ago. It counts redirects served, which
is not the same as people: one person following a link twice is two clicks, and a link
opened from a preview-generating chat app may be counted without anyone having read it.