shorten.dev/docs
shorten.dev/docs
Getting StartedAuthentication
OverviewLinksAnalyticsErrors & Rate Limits
API Reference

Errors & Rate Limits

Error codes, response format, rate limits, and retry strategies.

Error format

All API errors return a consistent JSON body:

{
  "error": "error_code",
  "message": "Human-readable description",
  "status": 400
}

Some errors include additional fields:

FieldTypeWhen present
error_idstring (UUID)Server errors (500+) — useful for support tickets
detailsobjectValidation errors (400) — field-level error descriptions

Example with details (validation error)

{
  "error": "bad_request",
  "message": "Validation failed",
  "status": 400,
  "details": {
    "destination_url": "Invalid URL",
    "custom_slug": "Slug must be 3–50 characters"
  }
}

Example with error_id (server error)

{
  "error": "internal_error",
  "message": "An unexpected error occurred",
  "status": 500,
  "error_id": "a1b2c3d4-5678-90ab-cdef-1234567890ab"
}

Status codes

Client errors

StatusCodeDescription
400bad_requestMissing required field, malformed JSON, invalid URL
401unauthorizedMissing or invalid API key
403forbiddenAPI key lacks required scope
404not_foundInvalid slug or deleted resource
409conflictDuplicate custom slug
429rate_limitedToo many requests — see rate limits below

Server errors

StatusCodeDescription
500internal_errorUnexpected server error
503service_unavailableTemporary maintenance

Rate limits

Link creation is rate-limited to 300 creations per hour per user. This limit applies across all of your API keys and dashboard combined — not per key. Both single (POST /api/v1/links) and bulk (POST /api/v1/links/bulk) endpoints share the same counter.

All other operations (read, revoke) are not rate-limited.

Bulk creation limits

The bulk endpoint accepts up to 500 links per request. Each link in the batch counts as one creation toward your hourly limit. If the batch would exceed the remaining quota, the entire request is rejected with a 429 and no links are created.

Rate limit headers

Link creation responses include rate limit headers:

HeaderDescription
X-RateLimit-LimitMaximum requests per hour
X-RateLimit-UsedRequests used in the current window
X-RateLimit-ResetUnix timestamp when the window resets
Retry-AfterSeconds until window resets (only on 429 responses)
X-RateLimit-Limit: 300
X-RateLimit-Used: 53
X-RateLimit-Reset: 1708099200

Throttle your requests when X-RateLimit-Used approaches the limit.

Need a higher rate limit? Submit a support ticket describing your use case and we'll bump it up for you — no charge.


Retry strategy

For 429 and 5xx errors, use exponential backoff with jitter. When you receive a 429, the response includes a Retry-After header indicating how many seconds to wait before retrying.

fetchWithRetry.ts
async function fetchWithRetry(url: string, options: RequestInit, maxRetries = 3) {
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    const res = await fetch(url, options);

    if (res.ok) return res;

    if ((res.status === 429 || res.status >= 500) && attempt < maxRetries) {
      const retryAfter = res.headers.get("Retry-After");
      const baseDelay = retryAfter ? parseInt(retryAfter, 10) * 1000 : 2 ** attempt * 1000;
      const jitter = Math.random() * 500;
      await new Promise((r) => setTimeout(r, baseDelay + jitter));
      continue;
    }

    throw new Error(`Request failed: ${res.status}`);
  }

  throw new Error("Max retries exceeded");
}

Analytics

Retrieve click analytics and traffic data for your links.

Overview

Official TypeScript SDK for the Shorten.dev API.

On this page

Error formatStatus codesRate limitsRetry strategy