Developer API
Unshorten links, trace redirect chains, and run security checks programmatically. All API features require a Pro key.
Authentication
Pass your Pro key in the Authorization header
of every request.
Authorization: Bearer pro_your_key_here
Your Pro key is delivered by email after purchase and shown once on the success page. Keep it safe — it grants access to your daily quota.
Rate limits
Rate limits are enforced per Pro key using a sliding 24-hour window. Each URL counts as one check, including individual items in a bulk request.
| Plan | Limit | Window |
|---|---|---|
| Free (UI only) | 30 checks | 24 hours |
| Pro | 500 checks | 24 hours |
Rate limit headers are included in every response: X-RateLimit-Remaining and X-RateLimit-Reset (Unix ms timestamp).
Endpoints
/api/v1/unshorten Unshorten a single URL. Traces the redirect chain, runs security checks, and optionally strips tracking parameters. Returns a shareable result link.
Request body
| Field | Type | Default | Description |
|---|---|---|---|
url * | string | — | URL to unshorten (must start with http:// or https://) |
clean | boolean | false | Strip tracking parameters (utm_*, fbclid, gclid, etc.) |
maxHops | number | 15 | Maximum redirects to follow (1–15) |
requestTimeout | number | 5000 | Per-hop timeout in milliseconds |
Response
{
"inputUrl": "https://bit.ly/example",
"finalUrl": "https://example.com/article?id=123",
"finalDomain": "example.com",
"chain": [
{ "url": "https://bit.ly/example", "status": 301, "location": "https://example.com/article?id=123", "ms": 87 },
{ "url": "https://example.com/article?id=123", "status": 200, "ms": 210 }
],
"flags": [
{ "code": "KNOWN_SHORTENER", "message": "Input is a known shortener domain" }
],
"shareUrl": "https://unshorten.app/share?d=..."
}
Examples
curl
curl -X POST https://unshorten.app/api/v1/unshorten \
-H "Authorization: Bearer pro_your_key_here" \
-H "Content-Type: application/json" \
-d '{"url":"https://bit.ly/example","clean":true}'
JavaScript
const res = await fetch('https://unshorten.app/api/v1/unshorten', {
method: 'POST',
headers: {
'Authorization': 'Bearer pro_your_key_here',
'Content-Type': 'application/json',
},
body: JSON.stringify({ url: 'https://bit.ly/example', clean: true }),
});
const data = await res.json();
console.log(data.finalUrl);
Python
import requests
res = requests.post(
'https://unshorten.app/api/v1/unshorten',
headers={'Authorization': 'Bearer pro_your_key_here'},
json={'url': 'https://bit.ly/example', 'clean': True},
)
data = res.json()
print(data['finalUrl'])
/api/v1/unshorten/bulk Unshorten up to 200 URLs in a single request. Each URL is processed concurrently and counts as one check against your daily limit. Results are returned in the same order as the input.
Request body
| Field | Type | Default | Description |
|---|---|---|---|
urls * | string[] | — | Array of URLs to unshorten (max 200) |
clean | boolean | false | Strip tracking parameters from all results |
maxHops | number | 15 | Maximum redirects per URL (1–15) |
requestTimeout | number | 5000 | Per-hop timeout in milliseconds |
Response
{
"results": [
{
"inputUrl": "https://bit.ly/example1",
"finalUrl": "https://example.com/page",
"finalDomain": "example.com",
"chain": [...],
"flags": []
},
{
"inputUrl": "https://invalid-url",
"error": "Provide http(s) URL."
}
],
"processed": 2,
"failed": 1
}
Examples
curl
curl -X POST https://unshorten.app/api/v1/unshorten/bulk \
-H "Authorization: Bearer pro_your_key_here" \
-H "Content-Type: application/json" \
-d '{"urls":["https://bit.ly/a","https://t.co/b"],"clean":true}'
JavaScript
const res = await fetch('https://unshorten.app/api/v1/unshorten/bulk', {
method: 'POST',
headers: {
'Authorization': 'Bearer pro_your_key_here',
'Content-Type': 'application/json',
},
body: JSON.stringify({
urls: ['https://bit.ly/a', 'https://t.co/b'],
clean: true,
}),
});
const { results } = await res.json();
results.forEach(r => console.log(r.inputUrl, '->', r.finalUrl ?? r.error));
Python
import requests
res = requests.post(
'https://unshorten.app/api/v1/unshorten/bulk',
headers={'Authorization': 'Bearer pro_your_key_here'},
json={
'urls': ['https://bit.ly/a', 'https://t.co/b'],
'clean': True,
},
)
for r in res.json()['results']:
print(r['inputUrl'], '->', r.get('finalUrl', r.get('error')))
Response fields
inputUrl The URL you submitted, normalised.
finalUrl The resolved destination after all redirects.
finalDomain
Hostname of the final URL (e.g. example.com).
chain
Array of hops. Each hop has url, status, optional location, and ms (latency).
flags
Security warnings. Each flag has a code and
message.
shareUrl Shareable link to view these results on unshorten.app. Only present in the single-URL endpoint.
Security flags
Flags are heuristic signals, not verdicts. A flagged URL may be legitimate; an unflagged URL may still be dangerous. Use them as a first-pass filter.
PUNYCODE_DOMAINCREDENTIALS_IN_URLAT_SYMBOLIP_DOMAINPRIVATE_IP_DOMAINSUSPICIOUS_PORTVERY_LONG_URLVERY_LONG_QUERYMANY_QUERY_PARAMSMIXED_SCRIPT_DOMAINLOOKS_LIKE_HOMOGRAPHKNOWN_SHORTENERSUSPICIOUS_TLDNON_HTTPS_FINALDOWNLOAD_FILETOO_MANY_REDIRECTSMAX_HOPS_REACHED Errors
| HTTP status | error code | Meaning |
|---|---|---|
| 400 | BAD_JSON | Request body is not valid JSON. |
| 400 | BAD_REQUEST | Invalid URL, forbidden host, or other input error. Check
message.
|
| 401 | UNAUTHORIZED | Missing or invalid Pro key. |
| 429 | RATE_LIMITED | Daily limit reached. Check X-RateLimit-Reset for when the window resets.
|