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.ccQR code generation
/api/qr/api/qrBoth 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
| Name | Type | Default | Description |
|---|---|---|---|
| type | string | url | url · text · email · tel · sms · wifi · vcard · geo |
| format | string | png | png or svg. Other formats are not supported. |
| size | int | 512 | 64–2000. Output edge length in pixels (including margin). |
| margin | int | 2 | 0–20. Quiet zone in modules. |
| ecc | string | M | L·M·Q·H. Error correction. Prefer H for print. |
| dark | string | #000000 | Foreground. #rgb · #rrggbb · #rrggbbaa. |
| light | string | #ffffff | Background. Use #ffffff00 for transparent PNG. |
| response | string | binary | Set to json to receive a JSON body with dataUrl. |
| download | bool | false | When true, adds Content-Disposition: attachment. |
Content parameters by type
Flat (not nested) so query strings and JSON bodies look the same.
| type | required | optional |
|---|---|---|
| url | data | — |
| text | data | — |
| to | subject, body | |
| tel | phone | — |
| sms | phone | message |
| wifi | ssid | password, encryption(WPA·WEP·nopass), hidden |
| vcard | At least one of name, phone, or email | firstName, lastName, organization, title, phone, email, website, address, note |
| geo | latitude, 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.pngExamples
# 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
/api/shortenRequest body
| Name | Type | Default | Description |
|---|---|---|---|
| url | string | required | http/https URL to shorten. Scheme defaults to https. |
| slug | string | optional | Desired code. Letters, numbers, hyphen, underscore · 3–64 chars. |
| expiresIn | int | optional | Seconds 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
/api/dropsRequest body
| Name | Type | Default | Description |
|---|---|---|---|
| webhookUrl | string | optional | Optional https URL. Receives file.uploaded and drop.full JSON POSTs. |
| expiresIn | int | optional | Seconds until drop expiry. Default 604800 (7 days). Max 2592000 (30 days). |
| maxFiles | int | optional | Max files per drop. Default 20, max 50. |
| maxFileBytes | int | optional | Max bytes per file. Default 25 MiB, max 50 MiB. |
| maxTotalBytes | int | optional | Max 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
}/api/drops/{id}/filesMultipart 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"/api/files/{fileId}Returns raw file bytes with Content-Disposition: attachment. No auth — anyone with the fileId can download until the drop expires.
/api/drops/{id}/api/drops/{id}/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
/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
/api/links/{code}Returns click and expiry info as JSON, or 404 if missing.
curl "https://urlu.cc/api/links/my-link"/api/healthReturns 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.
/api/links/{code}/api/links?limit=50curl -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" }
}
}| Status | code | Meaning |
|---|---|---|
| 400 | bad_request | Body is not JSON, or URL cannot be encoded |
| 401 | unauthorized | Admin token missing or wrong |
| 404 | not_found | Code not found, or admin features disabled |
| 409 | conflict | Requested slug already in use |
| 413 | payload_too_large | Body or encoded payload exceeds the limit |
| 415 | unsupported_media_type | Content-Type is not application/json |
| 422 | validation_failed | Parameter values fail validation |
| 503 | service_unavailable | Could not allocate a unique code — retry |
Limits
- • QR payload max is 2,953 bytes (version 40, ECC L). Higher ECC lowers capacity.
- • Shorten targets max 2,048 characters; only http/https are allowed.
- • You cannot create a new short link that targets this service’s short links or pages. Exception: QR API URLs (/api/qr?…) can be shortened.
- • Private, loopback, and link-local hosts (localhost, 10.x, 192.168.x, 169.254.x, etc.) cannot be shortened.
- • There is no rate limit today. Add per-IP limits before exposing this on the public internet.
- • File drop defaults: 7-day lifetime, 20 files, 25 MiB per file, 100 MiB total. Download URLs (/api/files/{id}) are public — use short expiry for sensitive files.