API
Create short links and QR codes from your own software — or from your AI assistant. The REST API is unchanged from the previous version of PostMyLink, so existing integrations keep working without edits.
AI assistants (MCP)
PostMyLink is also a remote MCP server, so Claude, Cursor, ChatGPT and any other assistant that speaks the Model Context Protocol can shorten links, make QR codes and read your statistics in a conversation — and hand the QR artwork to other tools in the same session. It works for accounts on a paid plan. There is also a plain-language version for customers.
https://postmylink.com/api/mcpClaude, Claude Desktop, ChatGPT
Add a custom connector with the URL above. You will be sent to PostMyLink to sign in — the usual emailed link — and asked to approve the app. That is it: no key to copy.
Claude Code
claude mcp add --transport http postmylink https://postmylink.com/api/mcpThen run /mcp inside Claude Code to sign in. Or skip the browser and pass your API key instead:
claude mcp add --transport http postmylink https://postmylink.com/api/mcp --header "Authorization: Bearer YOUR_API_KEY"Anything else
Streamable HTTP at the URL above. OAuth 2.1 with PKCE; clients may identify themselves by URL or register dynamically at /oauth/register. A request with no token is answered 401 with the protected-resource metadata, so discovery is automatic. Your API key also works as the bearer token. POST JSON-RPC with Accept: application/json, text/event-stream, as the protocol requires. Calls count against the same rate limit as the REST API.
Tools
create_short_link- Shorten a URL, optionally with a custom alias, password, expiry and a QR code.
get_link- One link by id, short URL or alias, with its click totals.
list_links- Search and page through the account's links.
update_link- Change the destination, title, password, expiry or status.
delete_links- Delete up to 100 links at once.
get_link_stats- Clicks over time and by country, city, referrer, browser and OS.
create_qr_code- A saved QR code for a link, URL, text, email, phone or SMS — image and URLs returned.
get_qr_code- A saved code's image, as PNG at any size up to 2048px or as SVG.
list_qr_codes- The account's codes with scan counts.
update_qr_code- Rename, restyle or retarget a code.
delete_qr_codes- Delete up to 100 codes at once.
render_qr_code- A one-off QR image that is not saved and not counted.
get_account- Plan, usage against limits, and available domains.
list_domains- The domains this account can create links on.
Every write goes through the same checks as the dashboard: plan limits, your own domains, and the URL screening. Disconnect an assistant any time from your account page.
Authentication
Every request carries your API key, which you will find on your account page. Keep it secret: it grants full access to your links.
Authorization: Bearer YOUR_API_KEYA missing or unrecognised key answers 403.
{
"error": 1,
"message": "A valid API key is required to use this service."
}Responses
Every response is JSON with an error key: 0 on success, and a message alongside it when something went wrong.
{
"error": 1,
"message": "Please enter a valid URL."
}Rate limits
30 requests every 60 seconds per key, unlimited on Team. Every response reports your remaining budget.
{
"X-RateLimit-Limit": 30,
"X-RateLimit-Remaining": 29,
"X-RateLimit-Reset": 1789049111
}Going over answers 429, with the seconds to wait.
{
"error": 429,
"message": "Too Many API Requests.",
"Retry-After": 44
}Account
GET/api/accountRead the account behind the API key.
Response
{
"error": 0,
"data": {
"id": 42,
"email": "you@example.com",
"username": "you",
"avatar": "https://postmylink.com/static/images/user.png",
"status": "pro",
"planid": 4,
"expires": "2027-01-31 00:00:00",
"registered": "2021-12-18 21:12:51"
}
}PUT/api/account/updateChange the account email. The new address confirms the change by email before it takes effect.
email- The new address.
Request
{
"email": "new@example.com"
}Response
{
"error": 0,
"message": "Check the new email address to confirm the change."
}Links
GET/api/urlsList your links, newest first.
limit- Page size. Default 15, maximum 1000.
page- 1-based page number. Default 1.
order- "click" sorts by clicks; anything else sorts by date.
q- Search the target URL, alias and title.
Response
{
"error": 0,
"data": {
"result": 2,
"perpage": 15,
"currentpage": 1,
"nextpage": null,
"maxpage": 1,
"urls": [
{
"id": 96673,
"alias": "aB3xY",
"shorturl": "https://postmylink.com/aB3xY",
"longurl": "https://example.com/landing",
"title": "Landing page",
"description": null,
"clicks": 5,
"uniqueclicks": 4,
"domain": "https://postmylink.com",
"campaign": null,
"channel": [],
"date": "2026-08-01 09:15:00"
}
]
}
}POST/api/url/addCreate a short link. Posting the same URL twice returns the link you already have rather than making a second one.
url- Required. The destination.
custom- Your own alias, three characters or more.
domain- One of your short domains. Defaults to postmylink.com.
password- Ask visitors for this before redirecting.
expiry- Stop redirecting after this date, e.g. 2027-01-31.
metatitle- Title shown in the dashboard.
metadescription- Description shown in the dashboard.
status- "public" lists the link on your public profile.
Request
{
"url": "https://example.com/landing",
"custom": "spring"
}Response
{
"error": 0,
"id": 96673,
"shorturl": "https://postmylink.com/aB3xY"
}GET/api/url/{id}One link, with its click statistics.
Response
{
"error": 0,
"id": 96673,
"details": {
"id": 96673,
"alias": "aB3xY",
"shorturl": "https://postmylink.com/aB3xY",
"longurl": "https://example.com/landing",
"title": "Landing page",
"description": null,
"location": null,
"device": null,
"expiry": null,
"date": "2026-08-01 09:15:00"
},
"data": {
"clicks": 5,
"uniqueClicks": 4,
"topCountries": {
"United States": 3,
"Canada": 2
},
"topBrowsers": {
"Chrome": 4,
"Safari": 1
},
"topOs": {
"Mac OS X": 3,
"Windows": 2
},
"topReferrers": {
"www.google.com": 2
},
"socialCount": {
"facebook": 0,
"twitter": 0,
"instagram": 0,
"linkedin": 0
}
}
}PUT/api/url/{id}/updateChange a link. The alias and the domain stay fixed — changing them would break links already shared.
Request
{
"url": "https://example.com/new-landing"
}Response
{
"error": 0,
"id": 96673,
"shorturl": "https://postmylink.com/aB3xY"
}DELETE/api/url/{id}/deleteDelete a link. It stops redirecting immediately.
Response
{
"error": 0,
"message": "Link has been successfully deleted."
}QR codes
GET/api/qrList your QR codes. limit and page work as they do for links.
Response
{
"error": 0,
"data": {
"result": 1,
"perpage": 15,
"currentpage": 1,
"nextpage": null,
"maxpage": 1,
"qrs": [
{
"id": 3,
"name": "Poster",
"link": "https://postmylink.com/qr/3.png",
"scans": 12,
"date": "2026-02-01 10:34:41"
}
]
}
}POST/api/qr/addCreate a QR code. A link code points at a short link, so its scans are counted and you can change where it goes after it is printed.
type- link, text, email, phone or sms. Default link.
data- Required. The URL or text to encode.
name- A label for your own reference.
foreground- Hex colour of the modules, e.g. #000000.
background- Hex colour behind them, e.g. #ffffff.
Request
{
"type": "link",
"data": "https://example.com/menu",
"name": "Table card"
}Response
{
"error": 0,
"id": 3,
"link": "https://postmylink.com/qr/3.png"
}GET/api/qr/{id}One QR code and its scan counts.
Response
{
"error": 0,
"details": {
"id": 3,
"name": "Poster",
"link": "https://postmylink.com/qr/3.png",
"scans": 12,
"data": "2026-02-01 10:34:41"
},
"data": {
"clicks": 12,
"uniqueClicks": 9,
"socialCount": {
"facebook": 0,
"twitter": 0,
"instagram": 0,
"linkedin": 0
}
}
}PUT/api/qr/{id}/updateRename a code or change its colours.
Request
{
"name": "Table card v2"
}Response
{
"error": 0,
"message": "QR has been updated successfully."
}DELETE/api/qr/{id}/deleteDelete a QR code.
Response
{
"error": 0,
"message": "QR has been deleted successfully."
}Retired endpoints
Custom domains, campaigns, channels, splash pages, overlays, pixels and the old API’s OAuth client flow (/api/oauth/…) are not part of this version. Those paths answer 410, so a client can tell them apart from a typo. The MCP server above has its own, current OAuth.
{
"error": 1,
"message": "Not available"
}