urlu

API Docs

QR generation and URL shortening over REST. No API key required, and every response includes Access-Control-Allow-Origin: * so you can call it from the browser.

Base URL

https://urlu.cc

QR code generation

GET/api/qr
POST/api/qr

Both methods accept the same parameters. GET uses the query string; POST uses a JSON body. The default response is raw image bytes, so you can put the URL straight into an <img src>.

Common options

NameTypeDefaultDescription
typestringurlurl · text · email · tel · sms · wifi · vcard · geo
formatstringpngpng or svg. Other formats are not supported.
sizeint51264–2000. Output edge length in pixels (including margin).
marginint20–20. Quiet zone in modules.
eccstringML·M·Q·H. Error correction. Prefer H for print.
darkstring#000000Foreground. #rgb · #rrggbb · #rrggbbaa.
lightstring#ffffffBackground. Use #ffffff00 for transparent PNG.
responsestringbinarySet to json to receive a JSON body with dataUrl.
downloadboolfalseWhen true, adds Content-Disposition: attachment.

Content parameters by type

Flat (not nested) so query strings and JSON bodies look the same.

typerequiredoptional
urldata—
textdata—
emailtosubject, body
telphone—
smsphonemessage
wifissidpassword, encryption(WPA·WEP·nopass), hidden
vcardAt least one of name, phone, or emailfirstName, lastName, organization, title, phone, email, website, address, note
geolatitude, longitude—

Prefer short URLs in QR codes

Longer URLs make denser patterns and fail more often in print or low light. Shorten with /api/shorten first, then pass the short URL to /api/qr?data=.... The web UI QR tab can do the same.

# 1) shorten a long URL
curl -X POST "https://urlu.cc/api/shorten" \
  -H 'Content-Type: application/json' \
  -d '{"url":"https://example.com/really/long/path?utm_source=newsletter&utm_medium=email"}'

# response
{
  "shortUrl": "https://urlu.cc/s/abc1234",
  ...
}

# 2) encode shortUrl in a QR
curl "https://urlu.cc/api/qr?data=https://urlu.cc/s/abc1234&size=512&ecc=H" --output short-link-qr.png

Examples

# simplest — raw PNG bytes
curl "https://urlu.cc/api/qr?data=https://example.com" --output qr.png

# transparent SVG, no margin
curl "https://urlu.cc/api/qr?data=https://example.com&format=svg&light=%23ffffff00&margin=0" \
  --output qr.svg

# Wi-Fi QR
curl "https://urlu.cc/api/qr?type=wifi&ssid=MyCafe&password=hunter2&encryption=WPA" \
  --output wifi.png

# POST + JSON dataUrl
curl -X POST "https://urlu.cc/api/qr" \
  -H 'Content-Type: application/json' \
  -d '{"type":"vcard","firstName":"Ada","lastName":"Lovelace","phone":"+1-555-0100",
       "ecc":"H","size":800,"response":"json"}'

Embed in HTML

<img src="https://urlu.cc/api/qr?data=https://example.com&size=256" width="256" alt="QR" />

URL shortening

POST/api/shorten

Request body

NameTypeDefaultDescription
urlstringrequiredhttp/https URL to shorten. Scheme defaults to https.
slugstringoptionalDesired code. Letters, numbers, hyphen, underscore · 3–64 chars.
expiresInintoptionalSeconds until expiry. Omit for no expiry.

Examples

curl -X POST "https://urlu.cc/api/shorten" \
  -H 'Content-Type: application/json' \
  -d '{"url":"https://example.com/very/long/path","slug":"my-link","expiresIn":86400}'

Response (201 Created)

{
  "code": "my-link",
  "shortUrl": "https://urlu.cc/s/my-link",
  "url": "https://example.com/very/long/path",
  "createdAt": "2026-07-28T08:00:00.000Z",
  "expiresAt": "2026-07-29T08:00:00.000Z",
  "clicks": 0,
  "lastClickedAt": null,
  "qrUrl": "https://urlu.cc/api/qr?data=...",
  "statsUrl": "https://urlu.cc/api/links/my-link",
  "reused": false
}

Shortening the same URL twice without a slug returns the existing link with 200 and reused: true. This service’s QR API URLs (/api/qr?…) can be shortened. Already-short addresses (short links, stats URLs) are not recreated; an existing match is returned when found.

File drop

Temporary upload links. Create a drop, share uploadUrl with recipients, and collect downloadUrl for each file. Full spec for agents: /ai-drops.md

POST/api/drops

Request body

NameTypeDefaultDescription
webhookUrlstringoptionalOptional https URL. Receives file.uploaded and drop.full JSON POSTs.
expiresInintoptionalSeconds until drop expiry. Default 604800 (7 days). Max 2592000 (30 days).
maxFilesintoptionalMax files per drop. Default 20, max 50.
maxFileBytesintoptionalMax bytes per file. Default 25 MiB, max 50 MiB.
maxTotalBytesintoptionalMax total bytes per drop. Default 100 MiB, max 200 MiB.

Examples

curl -X POST "https://urlu.cc/api/drops" \
  -H 'Content-Type: application/json' \
  -d '{"expiresIn":604800,"maxFiles":10}'

Response (201 Created)

{
  "id": "k3nPq7xR2m",
  "uploadUrl": "https://urlu.cc/drop/k3nPq7xR2m?t=…",
  "pageUrl": "https://urlu.cc/drop/k3nPq7xR2m",
  "uploadToken": "…",
  "manageToken": "…",
  "expiresAt": "2026-08-16T08:00:00.000Z",
  "maxFiles": 10,
  "maxFileBytes": 26214400,
  "maxTotalBytes": 104857600
}
POST/api/drops/{id}/files

Multipart form with a single file field named file. Pass uploadToken via X-Drop-Upload-Token header or ?t= query. manageToken also works for uploads.

curl -X POST "https://urlu.cc/api/drops/k3nPq7xR2m/files" \
  -H "X-Drop-Upload-Token: UPLOAD_TOKEN" \
  -F "file=@./report.csv"
GET/api/files/{fileId}

Returns raw file bytes with Content-Disposition: attachment. No auth — anyone with the fileId can download until the drop expires.

GET/api/drops/{id}
PATCH/api/drops/{id}
DELETE/api/drops/{id}

GET without manageToken returns metadata and fileCount only. With manageToken (header X-Drop-Manage-Token or ?manageToken=), the response includes the files array with downloadUrl. PATCH and DELETE require manageToken in the JSON body.

Lookup & redirect

GET/s/{code}

Issues a 307 to the original URL and increments the click count. Missing or expired codes show a help page. e.g. https://urlu.cc/s/my-link

GET/api/links/{code}

Returns click and expiry info as JSON, or 404 if missing.

curl "https://urlu.cc/api/links/my-link"
GET/api/health

Returns service health.

Admin only

These two endpoints work only when ADMIN_TOKEN is set and require Authorization: Bearer <token>. Without the variable they return 404. Public delete would let anyone remove others’ links; a public list would expose every shortened URL.

DELETE/api/links/{code}
GET/api/links?limit=50
curl -X DELETE "https://urlu.cc/api/links/my-link" \
  -H "Authorization: Bearer $ADMIN_TOKEN"

Error shape

All errors share one shape. On validation failure (422), details holds per-field messages.

{
  "error": {
    "code": "validation_failed",
    "message": "Request validation failed.",
    "details": { "size": "Too big: expected number to be <=2000" }
  }
}
StatuscodeMeaning
400bad_requestBody is not JSON, or URL cannot be encoded
401unauthorizedAdmin token missing or wrong
404not_foundCode not found, or admin features disabled
409conflictRequested slug already in use
413payload_too_largeBody or encoded payload exceeds the limit
415unsupported_media_typeContent-Type is not application/json
422validation_failedParameter values fail validation
503service_unavailableCould not allocate a unique code — retry

Limits