Developer API

Short links,
from your code.

Create short links and QR codes, edit them, and read their stats with plain HTTP requests. Pick your language and every example on this page switches to it.

  • 13languages
  • 37endpoints
  • 120reads / min per key
On this page
  1. Get started
  2. The basics
  3. Your first request
  4. Links
  5. QR codes
  6. Analytics
  7. Permissions
  8. Errors
  9. Rate limits
  10. Keeping keys safe
  11. FAQ
Show code in
Quick start

Get started#

You need an API key, somewhere safe to keep it, and one request.

  1. step 1

    Create a key

    Open API Keys in your dashboard and click Create API key. Name it after whatever will use it (say, “billing-sync”), tick the permissions it needs, and copy the key when it appears. We only show it once.

    You have to be an Owner or Admin of the workspace.

  2. step 2

    Store it as a secret

    Put the key in an environment variable called USORT_API_KEY. Keep it out of your code, your repo and any app you ship. Every example on this page reads it from that variable.

  3. step 3

    Send a request

    Copy the first example below. If it returns your links, you're set up, and everything else on this page is a variation on it.

terminalset the key once per session
# macOS / Linux
export USORT_API_KEY="usk_paste_your_key_here"

# Windows PowerShell
$env:USORT_API_KEY = "usk_paste_your_key_here"
Basics

The basics#

Every request goes to the same base URL and carries the same header.

base urlhttps://api.usort.cc/api/v1/developer
auth headerAuthorization: Bearer usk_…your key…
bodiesContent-Type: application/json

A key acts as the person who created it, in the workspace it was created for. Anything it creates belongs to that person and is paid for with that workspace's credits, and it can only do what that person is allowed to do.

Successful responses look like { "success": true, "data": … }. Ids are opaque strings. Don't try to parse them.

First call

Your first request#

Start by listing your links. It only reads, so it's safe to run, and if it works then your key, your URL and your setup are all fine.

QR codes API

QR codes#

A QR code can point to a URL, plain text, an email address, a phone number, Wi-Fi details, a contact card and more.

POST/developer/qr

Create a QR code

Makes a QR code for a URL. We create a link behind it, which is how scans get counted.

needsqr:write

terminalNothing: curl comes with macOS, Linux and Windows 10+
curl -X POST "https://api.usort.cc/api/v1/developer/qr" \
  -H "Authorization: Bearer $USORT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "content_type": "url",
  "content_data": {
    "url": "https://example.com/menu"
  },
  "name": "Menu"
}'
Example response
response.jsonthe data field
{
  "qr": {
    "id": "7Zv0bM5L",
    "name": "Menu",
    "content_type": "url",
    "qr_url": "https://…/files/b98e8118700cfe095cb824f053e50a23",
    "scan_count": 0,
    "created_at": "2026-03-01T10:20:00.000Z"
  }
}

content_type is one of url, text, email, phone, sms, wifi, vcard, location, whatsapp, social_profile, and a few others. What goes in content_data depends on the type and matches the form in the dashboard. Colours, a logo and a frame are optional.

To make a QR code for a link you already have, add "link_id": "…"to the body. It's attached to that link, so clicks and scans are counted together.

GET/developer/qr?limit=5

List your QR codes

Returns your QR codes, newest first.

needsqr:read

terminalNothing: curl comes with macOS, Linux and Windows 10+
curl "https://api.usort.cc/api/v1/developer/qr?limit=5" \
  -H "Authorization: Bearer $USORT_API_KEY"
DELETE/developer/qr/YOUR_QR_ID

Delete a QR code

Deletes a QR code. The link behind it stays.

needsqr:delete

terminalNothing: curl comes with macOS, Linux and Windows 10+
curl -X DELETE "https://api.usort.cc/api/v1/developer/qr/YOUR_QR_ID" \
  -H "Authorization: Bearer $USORT_API_KEY"
Example response
response.jsonthe data field
{
  "message": "QR code deleted"
}
Analytics API

Analytics#

Clicks, unique visitors and when they happened. They're the same numbers you see in the dashboard.

  • Dates are ISO 8601, like 2026-03-31. If you send both from and to, they can be at most 366 days apart.
  • There are also breakdowns by day (/timeseries), country, device, browser and referrer, and totals for the whole workspace at /links/stats/overview and /qr/stats/overview.
  • If the workspace is out of credits, analytics return 402until it's topped up.
Access

Permissions#

You choose a key's permissions when you create it. Give it only what it needs: a script that creates links doesn't need to delete them.

I want toPermissionIt allows
Read linkslinks:readList links, get one link
Create and edit linkslinks:writeCreate, update, archive and restore links
Delete linkslinks:deleteDelete a link
Read QR codesqr:readList QR codes, get one QR code
Create and edit QR codesqr:writeCreate, rename, archive and restore QR codes
Delete QR codesqr:deleteDelete a QR code
Read click and scan statsanalytics:readEvery stats endpoint

A few calls do two things at once, so they need two permissions:

If you're doing thisThe key needs
Create a QR code while editing a link ("generateQr": true)links:write and qr:write
Delete a link together with its QR codelinks:delete and qr:delete
Create a QR code on an existing link ("link_id")qr:write and links:read
Change a QR code's destination, schedule or password when it sits on one of your linksqr:write and links:write
Errors

Errors#

Every error comes back with an HTTP status and a short code. Check the code, not the message text.

error.jsonan example error
{
  "success": false,
  "statusCode": 403,
  "error": "Forbidden",
  "code": "API_KEY_SCOPE_DENIED",
  "message": "This API key does not have the \"qr:write\" permission"
}
  • 400

    Invalid request

    A field is missing, has the wrong type, or isn't one we accept. The errors array lists each problem.

    Fix: Fix the fields it names and send the request again.

  • 401

    No key sent

    • API_KEY_MISSING

    The Authorization header is missing, or isn't in the form Bearer usk_…

    Fix: Add the header exactly as shown under Basics.

  • 401

    Key not recognised

    • API_KEY_INVALID

    The key is mistyped, cut off, or doesn't exist.

    Fix: Copy it again, or create a new one.

  • 401

    Key revoked or expired

    • API_KEY_REVOKED
    • API_KEY_EXPIRED

    Someone revoked it, or it passed its expiry date.

    Fix: Create a new key.

  • 402

    Out of credits

    • WALLET_EXHAUSTED

    The workspace has no credits left, so it can't create anything or read analytics.

    Fix: Top up under Billing. Nothing needs changing in your code.

  • 403

    Missing permission

    • API_KEY_SCOPE_DENIED

    The key doesn't have the permission this call needs.

    Fix: Create a key that has it (see Permissions).

  • 403

    The key's owner lost access

    • API_KEY_OWNER_ROLE_INSUFFICIENT
    • API_KEY_WORKSPACE_ACCESS_LOST
    • API_KEY_OWNER_INACTIVE
    • API_KEY_OWNER_EMAIL_UNVERIFIED
    • ORGANIZATION_DEACTIVATED

    A key acts as whoever created it. If that person was removed from the workspace, demoted, deactivated, or hasn't verified their email, the key stops working too.

    Fix: Restore their access, or create a key from an account that has it.

  • 404

    Not found

    The id doesn't exist, or it belongs to another workspace. You get the same answer either way.

    Fix: Check the id, and which workspace the key was created in.

  • 413

    Request too large

    • PAYLOAD_TOO_LARGE

    The body is over 1 MiB.

    Fix: Send less per request.

  • 429

    Too many requests

    • RATE_LIMITED

    You went over the per-minute limit. The Retry-After header says how many seconds to wait.

    Fix: Wait that long, then try again.

  • 503

    Keys unavailable

    • API_KEYS_UNAVAILABLE

    The server can't check keys right now.

    Fix: Try again in a minute. If it keeps happening, email us.

Limits

Rate limits#

Each key can make 120 read calls and 30 write calls per minute. Go over and you get a 429 with a Retry-After header that says how many seconds to wait.

  • Creating QR codes has a lower limit of its own, about 20a minute, same as in the dashboard. If you're making hundreds, pace them.
  • After a 429, wait and retry. We reject the call before doing anything, so retrying is always safe.
  • After a timeout, don't blindly retry a create. The first request may have worked, and sending it again makes a duplicate and uses credits twice. List your recent links first. Reads are always safe to repeat.
  • For a big batch, spread it out, or give it its own key so it doesn't crowd out your normal traffic.
Security

Keeping your key safe#

Anyone holding the key can do whatever its owner can, within the key's permissions.

  • Use an environment variable or a secret manager.Don't paste the key into code, chat, tickets or screenshots.
  • Call the API from your server, not from a browser or mobile app. Anything shipped to a device can be read by whoever holds it.
  • One key per integration, with as few permissions as possible. If it's temporary, set an expiry (up to 365 days).
  • To rotate a key, create the new one, switch your app to it, check it works, then revoke the old one.
  • If a key may have leaked, revoke it right away on the API Keys page. It stops working on the next request.
FAQ

FAQ#

Do I need an SDK?

No. It's HTTP and JSON, so any language with an HTTP client works. The examples use each language's standard library where there is one.

Can I call the API from a browser or a mobile app?

No. Anyone can read the key by opening dev tools or unpacking the app, and then act as you. Call the API from your backend and have your app talk to that.

Is there a test mode?

Not a separate one. Calls run against the real workspace the key belongs to. To test safely, create a spare workspace and make the key there.

Does the API cost extra?

No. Links and QR codes use credits exactly as they do in the dashboard, from the same wallet.

A request timed out. Can I just resend it?

Reads, yes. For a create, check first: it may have gone through, and resending would make a duplicate and use credits twice. List your latest links, then decide.

How do I swap a key without downtime?

Create the new key, switch your app to it, confirm it works, then revoke the old one. Revoking takes effect immediately, so it goes last.

My key stopped working. Why?

Look at the code in the error. The usual causes are that the key was revoked or expired, or that the person who created it was removed from the workspace or lost the permission it needs.

What are the rate limits?

Per key, per minute: 120 reads and 30 writes. Past that you get a 429 with a Retry-After header.

Something missing?

If a page here is unclear, or you hit something it doesn't cover, tell us what you were trying to do and what happened. That's the fastest way to get it fixed.