REST API v1

Build anything on top of your links

A predictable REST API over links, QR codes, analytics and bio pages. Standard verbs, JSON everywhere, and error codes that mean what they say.

API access is included on Pro and Business.

# POST /api/v1/workspaces/{workspace}/links — shorten a URL
curl -X POST {{APP_URL}}/api/v1/workspaces/1/links \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "original_url": "https://example.com/very/long/url",
    "custom_alias": "my-campaign",
    "expires_at": "2026-12-31 23:59:59",
    "tags": ["marketing", "q4"]
  }'

# 201 Created
{
  "data": {
    "id": 4821,
    "short_url": "{{APP_URL}}/my-campaign",
    "clicks": 0,
    "created_at": "2026-04-20T10:32:00Z"
  }
}
const res = await fetch(`{{APP_URL}}/api/v1/workspaces/${workspaceId}/links`, {
  method:  'POST',
  headers: {
    'Authorization': `Bearer ${process.env.SHORTLINK_API_TOKEN}`,
    'Content-Type': 'application/json',
    'Accept':       'application/json',
  },
  body: JSON.stringify({
    original_url: 'https://example.com/very/long/url',
    custom_alias: 'my-campaign',
    expires_at:   '2026-12-31 23:59:59',
    tags:         ['marketing', 'q4'],
  }),
})

const { data } = await res.json()
console.log(data.short_url) // {{APP_URL}}/my-campaign
import os, requests

res = requests.post(
    f"{{APP_URL}}/api/v1/workspaces/{workspace_id}/links",
    headers={
        "Authorization": f"Bearer {os.environ['SHORTLINK_API_TOKEN']}",
        "Accept":        "application/json",
    },
    json={
        "original_url": "https://example.com/very/long/url",
        "custom_alias": "my-campaign",
        "expires_at":   "2026-12-31 23:59:59",
        "tags":         ["marketing", "q4"],
    },
)

print(res.json()["data"]["short_url"])  # {{APP_URL}}/my-campaign
use Illuminate\Support\Facades\Http;

$response = Http::withToken(env('SHORTLINK_API_TOKEN'))
    ->acceptJson()
    ->post("{{APP_URL}}/api/v1/workspaces/{$workspaceId}/links", [
        'original_url' => 'https://example.com/very/long/url',
        'custom_alias' => 'my-campaign',
        'expires_at'   => '2026-12-31 23:59:59',
        'tags'         => ['marketing', 'q4'],
    ]);

echo $response->json('data.short_url'); // {{APP_URL}}/my-campaign
1
CAPABILITIES

Everything you need in an API

Consistent JSON responses, standard HTTP verbs, and errors that tell you what to fix.

  • RESTful design

    GET, POST, PATCH and DELETE against resource-oriented URLs, with JSON in and JSON out. Nothing to learn beyond HTTP.

  • Scoped API tokens

    Named tokens with read, write or admin scope. Revoke one without disturbing the others, and see when each was last used.

  • Rate limiting

    Limits by plan, with X-RateLimit headers on every response so you can back off before you are told to.

  • Signed webhooks

    HTTP POST on clicks, expiry and limit events, each payload signed with HMAC-SHA256 so you can verify it came from us.

  • Full resource coverage

    Links, QR codes, bio pages, tags, domains, workspaces and analytics — if the dashboard can do it, the API can too.

  • OpenAPI 3.1 spec

    Import into Postman or Insomnia, or generate a typed client in TypeScript, Python, Go or anything else that reads the spec.

2
ENDPOINTS

Core endpoints

Paths below are relative to /api/v1 and authenticated with a bearer token. Resources are scoped to a workspace, so {ws} is the id you get back from the first call in the list.

  • GET /workspaces
  • POST /workspaces/{ws}/links
  • GET /workspaces/{ws}/links
  • GET /workspaces/{ws}/links/{id}
  • PUT /workspaces/{ws}/links/{id}
  • DELETE /workspaces/{ws}/links/{id}
  • POST /workspaces/{ws}/links/bulk
  • GET /workspaces/{ws}/analytics/overview
  • GET /workspaces/{ws}/analytics/links/{id}
  • GET /workspaces/{ws}/analytics/export
  • POST /workspaces/{ws}/qr
3
RATE LIMITS

Rate limits by plan

Every response carries its own limit headers, so you never have to guess where you stand or discover it from a 429.

API rate limits for each plan
Plan Requests / min Requests / day Webhooks
Free — — Not included
Pro 1,000 100,000 5
Business 10,000 Unlimited Unlimited
4
FAQ

Questions about the API

Anything not covered here? Ask us directly.

How do I authenticate?

A bearer token in the Authorization header on every request. Tokens are created in the dashboard, scoped to read, write or admin, and can be revoked individually.

Which plans include API access?

Pro and Business. The free plan can create links in the dashboard but not through the API — the redirect endpoints themselves are open to everyone regardless of plan.

What happens when I hit a rate limit?

You get a 429 with a Retry-After header. Because every earlier response also carried X-RateLimit-Remaining, a well-behaved client can slow down before reaching that point.

Can I generate QR codes through the API?

Yes. POST to /api/v1/workspaces/{ws}/qr with a link id and the styling you want, then download the result as PNG, SVG or PDF from the download endpoint.

How do webhooks work?

You register a URL and we POST to it on clicks, expiry and limit events. Each payload is signed with HMAC-SHA256 using your webhook secret, so you can reject anything that does not verify.

Can I use it for marketing automation?

That is the common case. Create a tracked link per recipient or per campaign at send time, then read the analytics endpoint to attribute the response — no manual tagging in between.

How do I integrate it with a CRM?

Create the link when the record is created and store the returned id on the record. Polling the analytics endpoint for those ids, or receiving a webhook on click, then keeps engagement data next to the contact.

Is the analytics data real time?

Clicks are queued at redirect time and processed within about a second, so the API returns what the dashboard shows. It is not a nightly export.

Start building today

Create a token from the dashboard and make your first request. API access is included on Pro and Business.