API Reference

The Vertex API is REST-based, JSON-encoded, and versioned from /v1/. All endpoints require an API key unless otherwise noted.

Base URL: https://api.vrx.li/v1

Authentication

Authenticate using an API key in the Authorization header. API keys are prefixed with vrx_.

http
Authorization: Bearer vrx_your_api_key_here

Obtain an API key from your dashboard. Keys never expire but can be rotated at any time.

Shorten a URL

POST/v1/shorten

Creates a new short link. Returns a vrx.li short URL immediately. Short codes are base58-encoded, crypto-random, and 6–8 characters.

curl
curl -X POST https://api.vrx.li/v1/shorten \
  -H "Authorization: Bearer vrx_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://yoursite.com/very/long/path",
    "mask": false,
    "custom_code": "launch"
  }'

Request body

urlstringrequired

The destination URL to shorten. Must use http:// or https://. Private IPs, javascript:, and data: URIs are rejected.

maskboolean

If true, hides the destination URL from the recipient. Defaults to false.

custom_codestring

Optional custom short code (3–32 chars, alphanumeric and hyphens). Must be unique. Omit to auto-generate.

Response

codestring

The short code. 6–8 characters, base58-encoded.

short_urlstring

The full short URL, e.g. https://vrx.li/aX9kpQ.

created_atstring

ISO 8601 timestamp of creation.

Redirect

GET/:code

Redirects to the destination URL. Click events are logged asynchronously — the redirect never waits for analytics writes. Target latency: p50 <10ms.

Response

302 Found

Returns a 302 redirect. Use 301 for permanent/cacheable links (configurable per link). The Location header contains the destination URL.

Link stats

GET/v1/stats/:code

Returns aggregate click statistics for a single link. No auth required for public links.

json
{
  "code": "aX9kpQ",
  "clicks": 1402,
  "unique_clicks": 891,
  "created_at": "2024-01-15T10:32:00Z",
  "last_clicked": "2024-02-14T08:17:00Z"
}

Full analytics

GET/v1/analytics/:code

Full analytics breakdown including geo, device, UTM, referrer, and time-of-day heatmap. Requires authentication. Use the range query parameter to filter.

Query parameters

rangestring

Time range: 7d, 30d, or 90d. Defaults to 30d.

tzstring

IANA timezone for grouping (e.g. America/New_York). Defaults to UTC.

bash
curl https://api.vrx.li/v1/analytics/aX9kpQ?range=30d \
  -H "Authorization: Bearer vrx_your_api_key"

Analytics export

GET/v1/analytics/:code/export

Exports raw click events as CSV. Each row is one click event with all captured signals. Useful for loading into data warehouses, BI tools, or ad platforms.

Query parameters

formatstringrequired

Must be "csv". JSON support planned.

fromstring

Start date, ISO 8601 (e.g. 2024-01-01).

tostring

End date, ISO 8601 (e.g. 2024-01-31).

CSV columns: timestamp, country, city, device, browser, os, referrer, utm_source, utm_medium, utm_campaign, is_unique, is_bot

Health

GET/v1/health

Returns service health. No auth required. Used by the status page and uptime monitors.

json
{
  "status": "ok",
  "services": {
    "db": "ok",
    "cache": "ok"
  },
  "uptime_seconds": 2847392
}

Rate limits

Rate limiting is enforced per IP using a Redis-backed token bucket. Limits reset on a rolling window. Exceeded requests receive a 429 Too Many Requests response.

POST /v1/shorten60 req / minute
GET /v1/stats/:code300 req / minute
GET /v1/analytics/:code120 req / minute
GET /:code (redirect)No limit (cached)

Rate limit headers: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset

Error codes

All errors return JSON with a typed error field.

400invalid_urlURL failed validation (protocol, private IP, etc.)
401unauthorizedMissing or invalid API key.
404not_foundShort code does not exist.
409code_conflictCustom code is already in use.
422validation_errorRequest body malformed or missing required fields.
429rate_limitedToo many requests. Retry after X-RateLimit-Reset.
500internal_errorServer error. Retry with exponential backoff.
json
{
  "error": {
    "type": "invalid_url",
    "message": "URL uses disallowed protocol: javascript:",
    "code": 400
  }
}