Links
Create, list, update, delete, and bulk-create links from the v1 API.
Every link belongs to your org, whether it's created from the dashboard or the API — there's no separate "API-only" pool. See Rate limits for how your plan's monthly link quota and per-second request limits interact.
Create a link
POST /v1/links — scope: write
{
"destinationUrl": "https://example.com/launch", // required, http(s)
"customBackHalf": "launch-2026" // optional, 3-30 chars [A-Za-z0-9_-]
}
{
"shortUrl": "https://jot.co.zm/aB3xQ",
"shortCode": "aB3xQ",
"destinationUrl": "https://example.com/launch",
"expiresAt": null,
"qrUrl": "https://jot.co.zm/api/qr/aB3xQ.svg"
}
Errors: 400 invalid URL or back-half, 409 back-half already taken,
422 monthly link quota exceeded (see Errors).
expiresAt is always null for an API-created link — expiry only
applies to the no-signup anonymous tool on the homepage, not links
created by an authenticated account.
List your links
GET /v1/links — scope: read
Keyset-paginated via ?cursor= (the opaque value from a previous
response's nextCursor), newest first.
{ "links": [ { "id": "...", "shortCode": "aB3xQ", "destinationUrl": "...", "isActive": true, "createdAt": 1759276800 } ], "nextCursor": "..." }
Bulk-create
POST /v1/links/bulk — scope: write
Creates up to 5,000 links in a single call. One bad item only fails that item — the rest of the batch still succeeds. Your plan sets the real cap on items per request: 100 on Plus, 1,000 on Team, 5,000 on Business/API (see Rate limits).
{
"items": [
{ "destinationUrl": "https://example.com/a" },
{ "destinationUrl": "https://example.com/b", "customBackHalf": "promo-b" }
]
}
{
"results": [
{ "status": "created", "index": 0, "shortUrl": "https://jot.co.zm/x1", "shortCode": "x1", "destinationUrl": "https://example.com/a" },
{ "status": "failed", "index": 1, "destinationUrl": "https://example.com/b", "code": "back_half_taken", "error": "That back-half is already taken" }
],
"createdCount": 1,
"failedCount": 1
}
A failed item's code is one of back_half_taken, invalid_url,
quota_exceeded, screening_rejected, reserved_slug, or
allocation_failed — see Errors for what triggers each
one.
Errors (whole-request, not per-item): 400 batch exceeds your plan's
per-request cap, 403 bulk-create requires the Plus plan or above.
Get a link
GET /v1/links/:id — scope: read
Returns the full link record. A link id that doesn't exist, or belongs
to a different org, both return 404 — never 403 — so a request can't
be used to confirm another org's link ids exist.
Update a link
PATCH /v1/links/:id — scope: write
{ "destinationUrl": "https://example.com/new-target" }
204 No Content on success. Only the destination can change — the
short code itself is permanent once created.
Delete a link
DELETE /v1/links/:id — scope: write
Soft-deletes (deactivates) the link — the short code immediately stops
redirecting. 204 No Content on success, 404 if it doesn't exist.
What visitors see
A short link normally redirects straight away (302). Two exceptions:
- Preview a link. Add
+to any short link, for examplehttps://jot.co.zm/aB3xQ+, to see where it goes, when it was made and by what kind of account, without being sent there. Anyone can do this with any Jot link. - Warning page. A link that's been reported, that our team has flagged, or that was made without an account while URL screening was unavailable shows a "check this link" page before the visitor continues. The link keeps working; the warning goes when the reports are dismissed or the link passes screening.
What counts as a click
Clicks are people. When someone pastes a link into WhatsApp, Facebook,
Slack, LinkedIn, Telegram, iMessage and similar apps, the app fetches it
to draw a preview card. Those fetches still get the redirect (so the card
works) but are counted as shares, by app, not as clicks. Other
automated visits (crawlers, scanners, scripts) are redirected and counted
on their own. clicks in GET /v1/usage counts
people only.