Free URL Shortener API: Keys, Endpoints, Pagination
How to create a SendURL API key, choose workspace or project access, call the REST API for links and clicks, and page through results with cursors.
SendURL's free URL shortener API creates, lists, changes and deletes short links and returns every click on them, on every plan, Free included. It speaks JSON over HTTPS and authenticates with an API key. This post covers getting a key and the first calls; the full reference, with every field and error code, is in the API docs.
Create an API key
Keys are made in the console, on the API keys page in the sidebar.
- Open console.sendurl.to/api-keys and choose Create key.
- Give it a Name you'll recognize later, such as "Production server".
- Pick its Access: Whole workspace, or a single project.
- Pick when it Expires: Never, 30 days, 90 days or 1 year.
- Copy the key and store it as an environment variable named
SENDURL_API_KEY.
A key starts with snu_. You can copy it again later from the list of keys, which also shows when each was last used. A workspace can have up to 25 active keys. If a key leaks, revoke it in the same list: it's refused from the very next request.
Keep keys on your server. Anyone who can read a key can use it, so never put one in a web page, a mobile app or a public repository.
Workspace keys and project keys
- A workspace key works across every project: it creates links anywhere, lists all of them, and lists or creates projects.
- A project key is fenced into one project. It creates links there and sees only that project's links and clicks; any other link is a 404.
Project keys suit a script or integration that only needs one slice of your links. More in Workspaces and projects.
Your first request
Every request sends the key as a Bearer token. GET /me is a cheap test: it returns the key's workspace and project, the plan, and how many links the workspace uses.
curl https://api.sendurl.to/api/v1/me \
-H "Authorization: Bearer $SENDURL_API_KEY"To shorten a URL, send POST /links. Only destinationUrl is required:
curl -X POST https://api.sendurl.to/api/v1/links \
-H "Authorization: Bearer $SENDURL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"destinationUrl": "https://example.com/blog/launch", "title": "Launch post", "expiresAt": "2030-12-31T23:59:59Z"}'The same call from Node.js 18 or later:
const res = await fetch("https://api.sendurl.to/api/v1/links", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.SENDURL_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ destinationUrl: "https://example.com/blog/launch" }),
});
if (!res.ok) throw new Error(`HTTP ${res.status}: ${await res.text()}`);
const link = await res.json();
console.log(link.shortUrl);Save it as an .mjs file, or run it in an ES module, for the top-level await. The response is the new link with its shortUrl and qrUrl. Leave out slug for a random five-character one, or send one that follows the slug rules. The body also accepts expiresAt, password and projectId. See Create a link.
What the API covers
- Links: create (
POST /links), list (GET /links), read one (GET /links/:id), update (PATCH /links/:id), delete (DELETE /links/:id) - Clicks: every click of a link (
GET /links/:id/clicks), one click in full (GET /links/:id/clicks/:clickId), clicks per day (GET /links/:id/stats) - Workspace: list and create projects (
GET /projects,POST /projects), and check your key (GET /me)
PATCH changes only the fields you send. It's how you change a link's destination, set or remove an expiry or a password, move a link to another project, or switch a link off with "active": false. DELETE removes a link and its clicks for good and frees its slug for anyone, so switching off is usually safer.
Pagination with cursors
Lists return at most 10 items per page, newest first for links and clicks. While pagination.hasMore is true, pass pagination.nextCursor back as cursor. This Python loop reads every click of one link:
import os
import requests
API = "https://api.sendurl.to/api/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['SENDURL_API_KEY']}"}
params = {"limit": 10}
clicks = []
while True:
res = requests.get(f"{API}/links/LINK_ID/clicks", headers=HEADERS, params=params, timeout=10)
res.raise_for_status()
page = res.json()
clicks.extend(page["clicks"])
if not page["pagination"]["hasMore"]:
break
params["cursor"] = page["pagination"]["nextCursor"]
print(len(clicks), "clicks")Cursors mark a position rather than a count, so pages don't shift while new clicks arrive. There is no offset parameter; sending one is an error. The links list also carries a summary (total links, active links, total clicks) and the workspace's usage. See Pagination. For longer scripts, such as storing clicks or creating links from a spreadsheet, see the URL shortener API in Python and JavaScript guide.
Errors
Errors share one JSON shape: an error code that is safe to branch on, a message for people, and sometimes issues. Common ones:
401 invalid_api_key: the key is unknown, revoked or expired403 link_limit_reached: the workspace has used all the links on its plan404 link_not_found: no such link in the key's scope409 slug_taken: another link already uses that slug
Check the HTTP status before parsing the body: a request refused for sending too many requests can come back as an HTML page rather than JSON. The Errors section lists every code.
Plans and limits
The API is part of every plan. What differs is the workspace's link limit: 100 links on Free and 1,000 on Pro at $5 a month, counted across all projects. Keys and projects have fixed caps: 25 active keys and 100 projects per workspace.
Read the API reference for every field, then try the calls above with a key of your own.
Start tracking links free.
Create your first short link with click analytics in seconds. No credit card, no sign-up to start.
Shorten a link