Pagewing API
Create short links, change where they go, set device and country redirects and read click stats from your own code, a spreadsheet script or an automation tool. The API is part of Pagewing Pro and works on the same links as the side panel and your links page.
Keys
Make a key under API keys on your account page and send it with every request as a bearer token. The key is shown once; keep it secret, since anyone with it can change your links. Give each app its own key so you can delete one without breaking the others. A key stops working while your account isn't on Pro, and you can have up to 10.
curl https://pagewing.appzlr.com/api/v1/account \
-H "Authorization: Bearer $PAGEWING_KEY"{
"id": "Xy12…",
"email": "you@example.com",
"plan": "pro"
}Every address below starts with https://pagewing.appzlr.com/api/v1. Requests and answers are JSON.
Limits
- 60 requests a minute per key. Over that, the answer is 429 with a
Retry-Afterheader in seconds. - 200 new links a day per account, counting links made anywhere (the side panel, the website and the API).
- 100 links per bulk create, and 100 links per page of a list.
- 20 redirect rules per link, and 60 countries per rule.
- Click stats go back up to 3 years.
Errors
A failed request answers with an HTTP error status and a JSON body: a message you can show and a code you can check.
{
"error": "That alias is taken. Try another.",
"code": "CONFLICT"
}| Status | Code | When |
|---|---|---|
| 400 | INVALID | Something in the request isn't right: a bad address, alias, date or rule. The message says what. |
| 401 | UNAUTHORIZED | No key, a mistyped key, or a deleted one. |
| 402 | PRO_REQUIRED | The account isn't on Pro (every request), or the change needs a feature of a plan it doesn't have. The answer names the feature and the plan. |
| 404 | NOT_FOUND | There's no link with that code on your account. |
| 409 | CONFLICT | That alias is taken. |
| 429 | LIMIT | Over 60 requests a minute (wait for the Retry-After seconds), or the day's 200 new links. |
The link object
| Field | What it is |
|---|---|
code | The part after the slash: an alias you chose, or seven random letters and digits. |
url | Where the link goes. |
shortUrl | The short link to share. |
clicks | Every click since it was made. |
createdAt, lastClickedAt | ISO 8601 times. lastClickedAt is missing until the first click. |
title | A name for the link, optional. |
tags | Lowercase labels, up to 10. |
disabled | Turned off: the short link says it isn't available instead of redirecting. |
expiresAt | When it stops redirecting, if it has an expiry. |
blocked | Turned off by Pagewing after an abuse report. Only present when true. |
rules | Device and country redirects, in the order they're checked. Missing when there are none (see Redirect rules). |
{
"code": "spring-sale",
"url": "https://example.com/sale",
"shortUrl": "https://pagewing.appzlr.com/spring-sale",
"clicks": 42,
"createdAt": "2026-10-29T09:30:00.000Z",
"title": "Spring sale",
"lastClickedAt": "2026-10-29T15:02:11.000Z",
"tags": [
"campaign"
],
"disabled": false,
"expiresAt": "2026-12-31T23:00:00.000Z",
"rules": [
{
"id": "k3m9qa",
"kind": "device",
"values": [
"ios"
],
"url": "https://apps.apple.com/app/id123"
},
{
"id": "p7d2xw",
"kind": "country",
"values": [
"DE",
"AT"
],
"url": "https://example.de/sale"
}
]
}Create a link
POST /links. Only url is required; the answer is the new link, with status 201.
curl -X POST https://pagewing.appzlr.com/api/v1/links \
-H "Authorization: Bearer $PAGEWING_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com/sale","alias":"spring-sale","title":"Spring sale","tags":["campaign"],"expiresAt":"2026-12-31T23:00:00Z"}'alias: 3–32 letters, numbers,-or_. Leave it out for a random code.title,tags,expiresAt(a future time, up to ten years away) andrulesare optional.- Addresses on Google's list of phishing and malware sites are refused, and so are other Pagewing short links.
List links
GET /links, newest first, 100 at most per page. Every parameter is optional:
q: search the code, destination, title and tags.url: only links to exactly this address (to check whether a page already has one).tag: only links with this tag.sort:newest,oldest,clicks,lastClickortitle.limit(1–100, 50 when left out) andcursor: pass the answer'scursorback for the next page; it'snullon the last one.
curl https://pagewing.appzlr.com/api/v1/links?tag=campaign&sort=clicks&limit=20 \
-H "Authorization: Bearer $PAGEWING_KEY"{
"links": [
"…link objects…"
],
"total": 240,
"matching": 12,
"cursor": null,
"tags": [
{
"tag": "campaign",
"count": 12
}
]
}Get a link
GET /links/:code answers with the link object.
curl https://pagewing.appzlr.com/api/v1/links/spring-sale \
-H "Authorization: Bearer $PAGEWING_KEY"Update a link
PATCH /links/:code with the fields to change: url, title (null clears it), tags(replaces them), disabled, expiresAt (null removes it) and rules (replaces them; [] removes them). The code, clicks and creation date stay. The answer is the link as it is now.
curl -X PATCH https://pagewing.appzlr.com/api/v1/links/spring-sale \
-H "Authorization: Bearer $PAGEWING_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com/sale-extended","expiresAt":null}'Delete a link
DELETE /links/:code answers 204. The short link stops working right away and its stats are deleted; this can't be undone.
curl -X DELETE https://pagewing.appzlr.com/api/v1/links/spring-sale \
-H "Authorization: Bearer $PAGEWING_KEY"Bulk create
POST /links/batch makes up to 100 links in one request. Each one that fails (a bad address, a taken alias, the day's limit) gets an error of its own, and the rest are still made. tags go on every link; skipExisting: true answers with the link an address already has instead of making a second one.
curl -X POST https://pagewing.appzlr.com/api/v1/links/batch \
-H "Authorization: Bearer $PAGEWING_KEY" \
-H "Content-Type: application/json" \
-d '{"links":[{"url":"https://example.com/a"},{"url":"https://example.com/b","alias":"launch-b","title":"B"}],"tags":["launch"],"skipExisting":true}'{
"results": [
{
"url": "https://example.com/a",
"link": "…link object…"
},
{
"url": "https://example.com/b",
"error": "That alias is taken. Try another.",
"errorCode": "CONFLICT"
}
],
"created": 1
}Click stats
GET /links/:code/stats?from=YYYY-MM-DD&to=YYYY-MM-DD (UTC days, both included; the last 30 days when left out) answers with clicks for every day in the range and the top referrers, countries (two-letter codes, "" for unknown), devices and destinations. No IP address or full referrer is ever kept.
curl https://pagewing.appzlr.com/api/v1/links/spring-sale/stats?from=2026-10-01&to=2026-10-29 \
-H "Authorization: Bearer $PAGEWING_KEY"{
"code": "spring-sale",
"from": "2026-10-01",
"to": "2026-10-29",
"days": [
{
"day": "2026-10-01",
"clicks": 3
},
"…"
],
"clicks": 42,
"allTime": 42,
"untracked": 0,
"referrers": [
{
"key": "news.ycombinator.com",
"clicks": 20
},
{
"key": "",
"clicks": 9
}
],
"countries": [
{
"key": "US",
"clicks": 25
},
{
"key": "DE",
"clicks": 11
}
],
"devices": [
{
"key": "desktop",
"clicks": 30
},
{
"key": "mobile",
"clicks": 12
}
],
"destinations": [
{
"key": "",
"clicks": 26
},
{
"key": "k3m9qa",
"clicks": 9
},
{
"key": "p7d2xw",
"clicks": 7
}
],
"limited": false
}destinations counts where clicks were sent: "" is the link's own address, anything else a rule's id. Device classes are desktop, mobile, tablet, bot (link previews and crawlers) and other.
Redirect rules
A rule sends some visitors to another address, by their device or their country. Rules are checked in order and the first one that matches wins; everyone else goes to the link's url, and link previews always do. Each rule has a kind, itsvalues and a url. Send rules without an id and each gets one; send the id back when you update, so the rule keeps its stats.
device:ios(iPhone and iPad),android(Android phones and tablets),mobile(any phone),tablet(any tablet),desktop(computers).iosandandroidgo by the operating system, so they cover tablets too.country: two-letter ISO 3166 codes such asUS,DEorJP, from where the visitor connects.
curl -X PATCH https://pagewing.appzlr.com/api/v1/links/spring-sale \
-H "Authorization: Bearer $PAGEWING_KEY" \
-H "Content-Type: application/json" \
-d '{"rules":[{"kind":"device","values":["ios"],"url":"https://apps.apple.com/app/id123"},{"kind":"device","values":["android"],"url":"https://play.google.com/store/apps/details?id=com.example"},{"kind":"country","values":["DE","AT"],"url":"https://example.de/sale"}]}'An iPad that asks for desktop websites (Safari's default) looks like a Mac, so it counts as a computer. Questions or something missing? Tell us.