Skip to content

API Reference

Complete API reference for linked.is REST API

The linked.is API allows you to programmatically manage your short links, bio profiles and profile blocks, and access analytics data.

Base URL

All API requests should be made to:

https://linked.is/api/v1

Paths in this reference such as POST /v1/links are relative to https://linked.is/api, so POST /v1/links means POST https://linked.is/api/v1/links.

Authentication

All API requests require authentication using an API token. You can generate an API token from your account settings. See Authentication for details.

Include your API token in the Authorization header:

curl -X GET "https://linked.is/api/v1/links" \
  -H "Authorization: Bearer YOUR_API_TOKEN"
<script setup lang="ts">
const authenticatedApiCall = async () => {
  const response = await fetch("https://linked.is/api/v1/links", {
    method: "GET",
    headers: {
      Authorization: "Bearer YOUR_API_TOKEN",
    },
  });

  const data = await response.json();
  console.log(data);
  return data;
};
</script>

<template>
  <div>
    <button @click="authenticatedApiCall">Call Authenticated API</button>
  </div>
</template>
Call the API from your server. The API doesn't send CORS headers, so browsers block requests made from other websites. Keep your token on your server and call the API from there.

Request Format

Send request bodies as JSON with the Content-Type: application/json header. IDs of links, profiles, blocks and teams are integers. Dates in responses are ISO 8601 strings in UTC, for example 2024-01-15T10:30:00.000Z.

Response Format

Successful responses are returned in JSON format with success and data:

<script setup lang="ts">
const successResponse = {
  success: true,
  data: { /* ... */ }
};
</script>

<template>
  <div>
    <h3>Success Response:</h3>
    <pre>{{ JSON.stringify(successResponse, null, 2) }}</pre>
  </div>
</template>

The only exception is Get QR Code, which returns the image itself for the png and svg formats.

Errors

Errors use the standard error body of the server. When the API knows what went wrong, the error code and a readable message are in data.error:

<script setup lang="ts">
const errorResponse = {
  error: true,
  url: "https://linked.is/api/v1/links/42",
  statusCode: 404,
  statusMessage: "Server Error",
  message: "",
  data: {
    success: false,
    error: {
      code: "LINK_NOT_FOUND",
      message: "The specified link does not exist"
    }
  }
};
</script>

<template>
  <div>
    <h3>Error Response:</h3>
    <pre>{{ JSON.stringify(errorResponse, null, 2) }}</pre>
  </div>
</template>

Some errors have no data.error, only a top-level message:

  • Authentication errors (401), for example "message": "Invalid API token". See Authentication Errors.
  • Validation errors (400) when the request body doesn't match the documented fields. statusMessage is "Validation Error" and message is a JSON array of the problems, each with the field path and a message.
  • Team access errors (403) with "message": "Not a member of this team". See Team Workspaces.
  • Rate limit errors (429). See Rate Limits.

To read any error, use body.data?.error?.code and fall back to body.message.

Error Codes

CodeStatusDescription
INVALID_REQUEST400The ID in the path is missing or not a number
SLUG_TAKEN400The slug is already used by a short link or a bio profile
SLUG_RESERVED400The slug is a reserved path (for example dashboard or api)
BLOCKED_URL400The destination URL is flagged as malicious
INVALID_BLOCKS400A reorder request didn't list every block of the profile exactly once
LINK_NOT_FOUND404The link doesn't exist or you don't have access to it
PROFILE_NOT_FOUND404The profile doesn't exist or you don't have access to it
BLOCK_NOT_FOUND404The block doesn't exist on this profile
CREATE_FAILED500The item couldn't be created
UPDATE_FAILED500The item couldn't be updated

HTTP Status Codes

CodeDescription
200Success (also when an item is created or deleted)
400Bad Request
401Unauthorized
403Forbidden
404Not Found
429Rate Limit Exceeded
500Internal Server Error

Rate Limits

Creating links (POST /v1/links) is limited to 100 requests per minute for each API token. The limit is the same on every plan. See Rate Limits.

Rate limit information is included in the response headers of POST /v1/links:

<script setup lang="ts">
const rateLimitHeaders = {
  "X-RateLimit-Limit": 100,
  "X-RateLimit-Remaining": 99,
  "X-RateLimit-Reset": 1640000000
};
</script>

<template>
  <div>
    <h3>Rate Limit Headers:</h3>
    <pre>{{ JSON.stringify(rateLimitHeaders, null, 2) }}</pre>
  </div>
</template>

Team Workspaces

By default the API works in your personal workspace. To work in a team you belong to, add the teamId query parameter to these endpoints:

  • GET /v1/links and POST /v1/links
  • GET /v1/profiles and POST /v1/profiles
  • GET /v1/analytics/overview
curl -X GET "https://linked.is/api/v1/links?teamId=7" \
  -H "Authorization: Bearer YOUR_API_TOKEN"

If you are not a member of the team, the request fails with 403. A teamId that isn't a positive number is ignored and your personal workspace is used.

Endpoints that take a link or profile ID (for example GET /v1/links/:id) don't need teamId. They work for your own personal items and for the items of every team you are a member of.

List Teams

List the teams you are a member of, with your role in each.

GET /v1/teams
curl -X GET "https://linked.is/api/v1/teams" \
  -H "Authorization: Bearer YOUR_API_TOKEN"
<script setup lang="ts">
const teamsResponse = {
  success: true,
  data: {
    items: [
      {
        id: 7,
        name: "Marketing",
        slug: "marketing",
        avatar: null,
        role: "owner"
      }
    ]
  }
};
</script>

<template>
  <pre>{{ JSON.stringify(teamsResponse, null, 2) }}</pre>
</template>

role is owner, admin or member.

Endpoints

Bio Profiles

Analytics

Teams

LLM Integration