Skip to content
jot.co.zm

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.

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.

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 /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.

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 /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 example https://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.