API Reference
Short Links (API)
Deprecated Feature
Short links are deprecated and only available on Legacy plans. New users should focus on QR Codes and Microsites instead.
Create and manage short links with the Pxl API. Automate link generation, customize previews and track clicks to enhance your digital marketing efforts.
Related guide: Creating short links.
This feature is available on Legacy plans only.
The short object
Each short link has a variety of
id: string
Example: pxllnk.co/mylink
Unique identifier for each short link. The id is a text composite of the domain and route. Prepending https:// to the id will form a URL denoting the location of the short link.
route: string
Example: mylink
The route of the short link.
consent: boolean
Example: true
Specifies whether to request informed consent from visitors before storing cookies.
destination: string
Example: https://www.bbc.co.uk
Destination URL the short link will redirect to.
title: string
Example: My Title
Title text shown in link previews.
description: string
Example: My link description
Description text shown in link previews.
image: string
Example: https://www.example.com/image.png
URL location of the image shown in link previews.
favicon: string
Example: https://www.example.com/favicon.png
URL location of the favicon shown in browser for redirects.
traffic: number
Example: 16564
Total visits to the link in the last 30 days, scans and clicks together.
This is not the number the Traffic column of the link manager shows. That column reports the link's traffic over its whole lifetime, which for an established link is much the larger figure. The dashboard answers "how much has this link ever been used"; this field answers "how much in the last 30 days". Reconcile against /v1/insight, which uses the same window, rather than against the dashboard.
scans: number
Example: 16548
Visits in the last 30 days that arrived by scanning the link's QR code.
clicks: number
Example: 16
Visits in the last 30 days that arrived by following the link rather than scanning it. scans plus clicks always equals traffic.
clicks changed meaning, and two fields are new
clicks previously returned a cumulative count over the whole life of the link, counting scans and clicks together. It now counts only clicks, over the last 30 days, and the other two figures are reported separately.
Two things follow for anyone already reading this field. Values are much lower than before — for a QR code that is mostly scanned, clicks can fall by orders of magnitude, because the scans it used to include have moved to scans. And a link with no traffic in the last 30 days reports 0 even though it has been visited in the past. If you store these values, treat the change as a break in the series rather than a collapse in traffic.
If you want the closest equivalent of the old field, use traffic, but note it is still a 30-day window rather than a lifetime total.
null means the figure is unknown
All three fields are null, rather than 0, when the analytics backend could not be reached. 0 means the link genuinely had no traffic in the window; null means we could not tell. The request still succeeds. Do not treat null as zero when summing or ranking.
createdAt: date
Example: 2024-12-27 08:26:49.219717
Date representation of when the link was created in UTC+0.
updatedAt: date
Example: 2024-12-28 08:26:49.219717
Date representation of when the link was last updated in UTC+0. This attribute considers updates to the click count, and can therefore be used to infer the date and time of the most recent click.
Create a short link
To create a short link, you will make a POST request specifying the defining attributes.
POST https://api.pxl.to/api/v1/short
Parameters
destination: string — required
Example: https://www.bbc.co.uk
Destination URL of the new short link. The scheme is optional: example.com is accepted and stored as https://example.com, which is what the response returns.
It has to be a web address. An email address (support@example.com) or any scheme other than http, https and ftp answers 422 rather than being turned into a redirect to the bare domain.
domain: string
Example: mydomain.com
The domain or subdomain of the new short link. Defaults to pxllnk.co when omitted; pxl.to can still be requested explicitly. Custom domains must be added in the dashboard before they can be used. Related guide: Creating custom domains.
The default domain changed
Links created without a domain are now created on pxllnk.co; previously pxl.to. Existing links are unchanged and keep redirecting. Read a new link's address from id in the response rather than composing it from route, and pass "domain": "pxl.to" if you need the old default.
route: string
Example: mylink
The route of the new short link. Leave it empty only on a custom domain you own: on pxl.to and pxllnk.co the root is reserved and an empty route answers 403.
consent: boolean
Example: false
Specifies whether to request informed consent from visitors before storing cookies. Requires company information on the admin page.
title: string
Example: My Title
Title text shown in link previews.
description: string
Example: My link description
Description text shown in link previews.
image: string
Example: https://www.example.com/image.png
URL location of the image shown in link previews.
favicon: string
Example: https://www.example.com/favicon.png
URL location of the favicon shown in browser for redirects.
parameters: object
Example: {"utm_source": "flyer"}
Query parameters appended to the destination when the link redirects.
This one depends on your plan. If your plan does not include UTM parameters, a request that sets them answers 402 with an error field and the link is not created; send no parameters, or an empty object, to create the link without them.
Returns
Returns the short object.
Request
An example CURL request:
curl -X POST --compressed "https://api.pxl.to/api/v1/short" \
-H "Accept-Encoding: gzip" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <YOUR_ACCESS_TOKEN>" \
-d '{
"destination": "example.com",
"title": "Redirect",
"description": "Descriptive text",
"image": "https://example.com/image.png",
"favicon": "https://example.com/favicon.ico"
}'
Response
An example JSON response:
{
"data": {
"id": "pxllnk.co/x8djds",
"route": "x8djds",
"destination": "https://example.com",
"title": "Redirect",
"description": "Descriptive text",
"image": "https://example.com/image.png",
"favicon": "https://example.com/favicon.ico",
"consent": false,
"traffic": 0,
"scans": 0,
"clicks": 0,
"createdAt": "2022-07-27T15:17:03.525Z",
"updatedAt": "2022-07-27T15:17:03.525Z"
}
}
Read a short link
To return a specific short link object, you will make a GET request specifying the URL encoded short id.
GET https://api.pxl.to/api/v1/short/:id
Parameters
| Parameter | Description |
|---|---|
missing | How a link that is not there is reported. 404 (the default) answers 404 with an error field. empty answers 200 with a body of {} instead. Optional, and not case sensitive. |
Returns
Returns the short object.
If your workspace has no short with that id, the response is 404 with an error field. A short belonging to another workspace is reported the same way, so a 404 never reveals whether the id exists elsewhere. That holds for missing=empty too: an empty body is exactly as silent as the 404.
Checking whether a link exists before you create it
If your integration asks for a link and creates it when it is absent, add missing=empty:
GET https://api.pxl.to/api/v1/short/:id?missing=empty
A link that is not there then answers 200 with {} rather than 404, so an HTTP client that treats any non-2xx status as a failed step keeps running. Branch on whether the body has a data key:
const response = await fetch(url + '?missing=empty', { headers });
const body = await response.json();
if ('data' in body) {
// the link already exists - body.data is the short object
} else {
// create it with POST /api/v1/short
}
In n8n, either add missing=empty to the URL as above, or turn on Options → Response → Never Error on the HTTP Request node so the 404 reaches your workflow as a value instead of stopping the run. In Zapier and Make, the same applies: the parameter is usually the smaller change.
Test !("data" in body), not body.data === null. The second reads like the same check but never matches, because the empty response carries no data key at all — it is literally {}.
missing applies to GET only. PUT and DELETE always answer 404 for an id your workspace does not have.
The default changed on 15 September 2026
A missing short used to answer 200 with {} in every case. It now answers 404 by default, which is self-describing and agrees with what PUT and DELETE do for the same id.
If that broke an integration, missing=empty restores the old response exactly, and it is a supported option rather than a temporary one — you do not have to migrate off it.
Request
An example CURL request:
curl -X GET --compressed "https://api.pxl.to/api/v1/short/pxllnk.co%2Fx8djds" \
-H "Accept-Encoding: gzip" \
-H "Authorization: Bearer <YOUR_ACCESS_TOKEN>"
Response
An example JSON response:
{
"data": {
"id": "pxllnk.co/x8djds",
"route": "x8djds",
"destination": "https://example.com",
"title": "Redirect",
"description": "Descriptive text",
"image": "https://example.com/image.png",
"favicon": "https://example.com/favicon.ico",
"consent": false,
"traffic": 0,
"scans": 0,
"clicks": 0,
"createdAt": "2022-07-27T15:17:03.525Z",
"updatedAt": "2022-07-27T15:17:03.525Z"
}
}
Update a short link
To update attributes for a specific short, you will make a PUT request specifying the attributes to update and the URL encoded short id.
PUT https://api.pxl.to/api/v1/short/:id
Send only the attributes you want to change. Anything you leave out keeps its stored value.
Omitted attributes used to be reset, not kept
This is a fix, and it changes what a partial update does. Attributes you did not send were previously overwritten with the values a brand-new link would get, not left alone. A request sending only destination also cleared favourite, detached every tag and pixel, emptied parameters, and moved the link to the default QR design — and it answered 200, with a body that shows none of those fields.
title and description were affected in a way worth checking for specifically: the stored values survived, so this endpoint and the dashboard both kept reporting them correctly, but the hosted page was rebuilt without them. Its <title>, og:title and twitter:title fell back to Redirect, and the descriptions to the raw destination URL — so link previews in Slack, WhatsApp, LinkedIn and search results were wrong while every API response looked right. If you have run partial updates against links that rely on preview metadata, re-send title and description once to rebuild those pages.
You do not need to change anything: a request that already sends every attribute behaves exactly as before, because supplied values have always won.
One exception, for links the dashboard created as something other than a plain URL redirect: a read-modify-write now fails, because the body carries the destination it just read and this endpoint no longer accepts one for those types. Drop destination from the request for those links.
Parameters
destination: string
Example: https://www.bbc.co.uk
The link's destination URL. The scheme is optional, as on create.
Only for links whose type is a plain URL redirect. Links created in the dashboard as phone, SMS, WhatsApp, email, vCard, microsite or PDF links keep the destination the dashboard gave them, and sending one answers 422. Their other attributes can still be updated here. (AI-assistant links, and microsite links whose microsite has been deleted, cannot be updated through this endpoint at all — see below.)
The message names the link's type when the value you sent looks like a URL. When it does not — a phone number or a vCard, for instance — it is refused as an invalid destination before the link's type is consulted.
consent: boolean
Example: false
Specifies whether to request informed consent from visitors before storing cookies. Requires company information on the admin page.
title: string
Example: My Title
Title text shown in link previews.
description: string
Example: My link description
Description text shown in link previews.
image: string
Example: https://www.example.com/image.png
URL location of the image shown in link previews.
favicon: string
Example: https://www.example.com/favicon.png
URL location of the favicon shown in browser for redirects.
favourite: boolean
Example: true
Marks the link as a favourite in the dashboard.
tags: number[]
Example: [6, 14]
Ids of the tags attached to the link, up to 10. Replaces the current set; send [] to detach all of them. Tags are created in the dashboard.
pixels: number[]
Example: [3]
Ids of the retargeting pixels attached to the link, up to 5. Replaces the current set; send [] to detach all of them. Pixels are created in the dashboard.
qr: number | null
Example: 58
Id of the QR design attached to the link. Designs are created in the dashboard. Send null to detach the design and leave the link without one.
`null` used to be ignored
{"qr": null} previously answered 200 and changed nothing, so there was no way to remove a design through the API once one was attached. Quoted ids are now coerced to numbers before the design is looked up, which fixes {"qr": "1"}: the default design is the one design that is not owned by a workspace, and a quoted "1" missed that test and answered 500. Other quoted ids such as {"qr": "58"} are accepted and attach design 58, as they always did. A value that is not a whole number, such as {"qr": "abc"}, has always been a 422 validation error and still is.
parameters: object
Example: {"utm_source": "flyer"}
Query parameters appended to the destination when the link redirects. Replaces the current set; send {} to remove them.
This one depends on your plan. If your plan does not include UTM parameters, a request that adds one or changes one answers 402 with an error field and nothing is updated. Two things are deliberately still allowed on such a plan: sending back the parameters a link already has — so reading a link, changing its title and PUTting the whole object keeps working — and removing them, whether some or all.
Returns
Returns the short object.
A PUT only ever updates a link that already exists in your workspace. If your workspace has no short with that id, the response is 404 with an error field instead — the same answer a short belonging to another workspace gives, so a 404 never reveals whether the id exists elsewhere. To create a link, use POST.
This replaces a 200 that created the link
A PUT to an id that did not exist previously created it, returning 200 and the new short. That was never documented — this endpoint takes no domain or route parameter, so the link it created was whatever the id happened to spell — and it created links outside the plan limit that POST enforces. It is now a 404.
If you have an integration that relies on PUT to create, replace it with a POST: send route and domain as parameters rather than composing them into the id. If you do not know whether the link exists, GET it first and branch on the answer — and if your HTTP client stops the run on a non-2xx status, use ?missing=empty on that GET so an absent link comes back as 200 with {}. See Checking whether a link exists before you create it.
A short held by another workspace previously answered 401. It now answers 404, which is what GET already does.
route and domain cannot be changed. A link's id is its domain and route, so renaming one means creating the new link and deleting the old one — the redirect target changes, and anything already pointing at the old address keeps pointing there until you delete it.
A link the API cannot rebuild answers 422 rather than being changed. That covers link types this endpoint has no renderer for, such as AI assistant links, and microsite links whose microsite has since been deleted. Those requests previously returned 200 after quietly converting the link into a plain URL redirect, which broke it.
Request
An example CURL request:
curl -X PUT --compressed "https://api.pxl.to/api/v1/short/pxllnk.co%2Fx8djds" \
-H "Accept-Encoding: gzip" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <YOUR_ACCESS_TOKEN>" \
-d '{
"destination": "https://google.com"
}'
Response
An example JSON response:
{
"data": {
"id": "pxllnk.co/x8djds",
"route": "x8djds",
"destination": "https://google.com",
"title": "Redirect",
"description": "Descriptive text",
"image": "https://example.com/image.png",
"favicon": "https://example.com/favicon.ico",
"consent": false,
"traffic": 0,
"scans": 0,
"clicks": 0,
"createdAt": "2022-07-27T15:17:03.525Z",
"updatedAt": "2022-07-27T15:17:03.525Z"
}
}
Delete a short link
To delete a specific short link, you will make a DELETE request specifying the URL encoded short id.
DELETE https://api.pxl.to/api/v1/short/:id
Parameters
No parameters.
Returns
Returns the number of records affected, which for a successful delete is always 1.
If your workspace has no short with that id, the response is 404 with an error field instead. A short belonging to another workspace is reported the same way, so a 404 never reveals whether the id exists elsewhere.
This replaces a 200
Deleting a short that does not exist previously returned 200 with "affected": 0 — a success status for something that did not happen, which you had to read the body to notice. It is now a 404, matching GET and PUT.
A client that deletes and ignores the status will now treat a repeated delete as a failure. If your flow can delete the same id twice — a retry after a timeout, for instance — treat 404 as already-deleted rather than as an error.
Request
An example CURL request:
curl -X DELETE --compressed "https://api.pxl.to/api/v1/short/pxllnk.co%2Fx8djds" \
-H "Accept-Encoding: gzip" \
-H "Authorization: Bearer <YOUR_ACCESS_TOKEN>"
Response
An example JSON response:
{
"data": {
"raw": [],
"affected": 1
}
}
The link and your plan's usage counter are removed together, so a failure cannot leave one without the other. Removing the hosted page and the stored analytics happens afterwards and cannot be undone if it fails; when something there does fail, the response still reports 200 with the deletion, and adds a warnings array naming what was left behind.
{
"data": { "raw": [], "affected": 1 },
"warnings": ["analytics removal"]
}
The warnings key is absent when everything succeeded.
List short links
To return the short links associated with your organisation, you will make a GET request. Results are paginated — see Returns below for how to page through every link.
GET https://api.pxl.to/api/v1/short
Parameters
take: number
Example: 1000
The number of records to return, with a minimum value of 1 and a maximum value of 1000. Defaults to 50.
skip: number
Example: 1000
The number of records to skip, with a minimum value of 0. There is no practical upper limit, so every link in the workspace can be reached by paging.
order: string
Example: createdAt
The field records are ordered by, always descending. One of updatedAt (the default, unchanged) or createdAt.
Use order=createdAt when paging through every link
updatedAt changes whenever a link is edited or clicked, so under the default order an active workspace reorders itself while you are paging. A link that is clicked jumps to the front of the list, which pushes a link you have not read yet past your skip offset — so that link is never returned, and you will not get an error telling you it was missed.
createdAt never changes once a link is created. Pass order=createdAt for any full walk of a workspace, and the only anomaly left is a link created mid-walk appearing on two consecutive pages, which de-duplicating by id removes.
The default stays updatedAt so existing integrations keep the ordering they were built against.
Returns
Returns a data array of short objects, and count — the total number of short links in the workspace, ignoring take and skip.
To retrieve every link, request pages of up to 1000 records with order=createdAt, increasing skip by take each time, until skip reaches count. De-duplicate the results by id: links created while you are paging shift records towards the back of the list, so a record can appear on two consecutive pages. Do not use the default updatedAt order for this — see the warning above.
Requests are limited to 500 per day per token, so prefer large pages — a workspace of 25,000 links costs 25 requests at take=1000, but 500 requests at take=50.
Request
An example CURL request:
curl -X GET --compressed "https://api.pxl.to/api/v1/short?take=2&skip=10" \
-H "Accept-Encoding: gzip" \
-H "Authorization: Bearer <YOUR_ACCESS_TOKEN>" \
Response
An example JSON response:
{
"data": [
{
"id": "pxllnk.co/x8djds",
"route": "x8djds",
"destination": "https://example.com",
"title": "Redirect",
"description": "Descriptive text",
"image": "https://example.com/image.png",
"favicon": "https://example.com/favicon.ico",
"consent": false,
"traffic": 0,
"scans": 0,
"clicks": 0,
"createdAt": "2022-07-27T15:17:03.525Z",
"updatedAt": "2022-07-27T15:17:03.525Z"
},
{
"id": "pxl.to/bing/preview",
"route": "bing/preview",
"destination": "https://bing.com/search?q=preview",
"title": "Redirect",
"description": "Descriptive text",
"image": "https://bing.com/image.png",
"favicon": "https://bing.com/favicon.ico",
"consent": true,
"traffic": 32,
"scans": 24,
"clicks": 8,
"createdAt": "2022-02-21T12:11:03.545Z",
"updatedAt": "2022-02-21T12:11:03.545Z"
}
],
"count": 1043
}