Skip to content

LLM Integration

How LLMs and AI agents can use the linked.is API

The linked.is API is designed to be easily consumed by Large Language Models (LLMs) and AI agents. This guide explains how to integrate our API with AI-powered tools.

Quick Start for LLMs

We provide a machine-readable documentation file at:

https://linked.is/llms.txt

This file contains all API endpoints, request/response formats, and examples in a structured format optimized for LLM consumption.

Tip: LLMs can fetch and parse llms.txt to understand the full API capabilities before making requests.

Common LLM Use Cases

1. URL Shortening

LLMs can shorten URLs on behalf of users:

<script setup lang="ts">
const shortenUrl = async () => {
  // Shorten a URL
  const response = await fetch('https://linked.is/api/v1/links', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${API_TOKEN}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      url: 'https://example.com/very/long/article/path'
    })
  })

  const { data } = await response.json()
  // Returns: { shortUrl: "https://linked.is/abc123", ... }
  console.log(data);
  return data;
};
</script>

<template>
  <div>
    <button @click="shortenUrl">Shorten URL</button>
  </div>
</template>

2. Analytics Retrieval

Query link performance data:

<script setup lang="ts">
const getAnalytics = async () => {
  // Get analytics overview
  const response = await fetch('https://linked.is/api/v1/analytics/overview', {
    headers: { 'Authorization': `Bearer ${API_TOKEN}` }
  })

  const { data } = await response.json()
  // Returns: { totalClicks: 1500, totalLinks: 25, topLinks: [...] }
  console.log(data);
  return data;
};
</script>

<template>
  <div>
    <button @click="getAnalytics">Get Analytics</button>
  </div>
</template>

3. Profile Management

Create and manage link-in-bio profiles:

<script setup lang="ts">
const createProfile = async () => {
  // Create a new bio profile
  const response = await fetch('https://linked.is/api/v1/profiles', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${API_TOKEN}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      displayName: 'AI Assistant',
      slug: 'ai-assistant',
      bio: 'Links curated by AI'
    })
  })

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

<template>
  <div>
    <button @click="createProfile">Create AI Profile</button>
  </div>
</template>

API Response Handling

All API responses follow a consistent format:

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

const handleApiResponse = (result: any) => {
  if (result.success) {
    // Process data
    const links = result.data.items;
    console.log("Links:", links);
  } else {
    // Handle error
    const errorCode = result.error.code;
    const errorMessage = result.error.message;
    console.error("Error:", errorCode, errorMessage);
  }
};
</script>

<template>
  <div>
    <h3>API Response:</h3>
    <pre>{{ JSON.stringify(apiResponse, null, 2) }}</pre>
    <button @click="handleApiResponse(apiResponse)">Process Response</button>
  </div>
</template>

Error Handling

Common error codes LLMs should handle:

CodeMeaningSuggested Action
INVALID_URLURL format is invalidAsk user to verify the URL
SLUG_TAKENCustom slug already existsGenerate a different slug
LINK_NOT_FOUNDLink doesn't existInform user the link was not found
PROFILE_NOT_FOUNDProfile doesn't existSuggest creating a new profile

Best Practices for AI Agents

1. Rate Limiting

Respect rate limits to avoid being blocked:

<script setup lang="ts">
const makeRequestWithRetry = async (url: string, headers: Record<string, string>, maxRetries = 3) => {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const response = await fetch(url, { headers });
    
    if (response.status === 429) {  // Rate limited
      const retryAfter = parseInt(response.headers.get('Retry-After') || '60', 10);
      await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
      continue;
    }
    
    return response.json();
  }
  
  throw new Error('Max retries exceeded');
};

const handleRetryRequest = async () => {
  try {
    const result = await makeRequestWithRetry(
      "https://linked.is/api/v1/links",
      {
        "Authorization": "Bearer YOUR_API_TOKEN"
      }
    );
    console.log("Result:", result);
  } catch (error) {
    console.error("Max retries exceeded:", error);
  }
};
</script>

<template>
  <div>
    <button @click="handleRetryRequest">Retry Request</button>
  </div>
</template>

2. Batch Operations

When processing multiple items, use pagination efficiently:

<script setup lang="ts">
const getAllLinks = async (apiToken: string) => {
  const allLinks = [];
  let page = 1;
  
  while (true) {
    const response = await fetch(
      `https://linked.is/api/v1/links?page=${page}&limit=100`,
      {
        headers: {
          "Authorization": `Bearer ${apiToken}`
        }
      }
    );
    
    const result = await response.json();
    const data = result.data;
    allLinks.push(...data.items);
    
    if (page >= data.pagination.totalPages) {
      break;
    }
    page += 1;
  }
  
  return allLinks;
};

const fetchAllLinks = async () => {
  try {
    const links = await getAllLinks("YOUR_API_TOKEN");
    console.log("All links:", links);
  } catch (error) {
    console.error("Error fetching links:", error);
  }
};
</script>

<template>
  <div>
    <button @click="fetchAllLinks">Fetch All Links</button>
  </div>
</template>

3. Idempotent Operations

Use custom slugs for idempotent link creation:

<script setup lang="ts">
const createOrGetLink = async (url: string, slug: string, apiToken: string) => {
  // Try to create with specific slug
  const response = await fetch(
    'https://linked.is/api/v1/links',
    {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${apiToken}`, 
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({url, slug})
    }
  );
  
  const result = await response.json();
  
  if (result.error?.code === 'SLUG_TAKEN') {
    // Link already exists, fetch it
    console.log("Slug already taken, fetching existing link...");
    // Implement logic to fetch existing link
  }
  
  return result.data;
};

const handleLinkCreation = async () => {
  try {
    const link = await createOrGetLink(
      "https://example.com/article",
      "my-slug",
      "YOUR_API_TOKEN"
    );
    console.log("Link created or retrieved:", link);
  } catch (error) {
    console.error("Error:", error);
  }
};
</script>

<template>
  <div>
    <button @click="handleLinkCreation">Create/Get Link</button>
  </div>
</template>

Function Calling Schema

For LLMs that support function calling, here's the schema:

<script setup lang="ts">
const createShortLinkSchema = {
  name: "create_short_link",
  description: "Create a shortened URL using linked.is",
  parameters: {
    type: "object",
    properties: {
      url: {
        type: "string",
        description: "The long URL to shorten"
      },
      slug: {
        type: "string",
        description: "Optional custom slug for the short URL"
      }
    },
    required: ["url"]
  }
};

const getLinkAnalyticsSchema = {
  name: "get_link_analytics",
  description: "Get click analytics for a short link",
  parameters: {
    type: "object",
    properties: {
      link_id: {
        type: "integer",
        description: "The ID of the link"
      }
    },
    required: ["link_id"]
  }
};
</script>

<template>
  <div>
    <h3>Create Short Link Schema:</h3>
    <pre>{{ JSON.stringify(createShortLinkSchema, null, 2) }}</pre>
    <h3>Get Link Analytics Schema:</h3>
    <pre>{{ JSON.stringify(getLinkAnalyticsSchema, null, 2) }}</pre>
  </div>
</template>

MCP Integration

For AI assistants using Model Context Protocol (MCP), linked.is can be integrated as a tool:

<script setup lang="ts">
const mcpSchema = {
  tools: [
    {
      name: "linkedis_shorten",
      description: "Shorten a URL using linked.is",
      inputSchema: {
        type: "object",
        properties: {
          url: { type: "string" }
        },
        required: ["url"]
      }
    }
  ]
};
</script>

<template>
  <div>
    <h3>MCP Integration Schema:</h3>
    <pre>{{ JSON.stringify(mcpSchema, null, 2) }}</pre>
  </div>
</template>

Security Considerations

Important for AI Developers:
  • Store API tokens securely, never in code
  • Use environment variables for token storage
  • Implement token rotation policies
  • Log API usage for audit purposes

Here's a complete example of an AI agent managing links:

<script setup lang="ts">
interface LinkData {
  id: string;
  url: string;
  slug: string;
  shortUrl: string;
  clicks: number;
  createdAt: string;
}

interface StatsData {
  totalClicks: number;
  uniqueVisitors: number;
  clicksOverTime: Array<{ date: string; clicks: number }>;
}

interface LinkedisAgent {
  apiToken: string;
  baseUrl: string;
  headers: Record<string, string>;
}

const createAgent = (apiToken: string): LinkedisAgent => {
  return {
    apiToken,
    baseUrl: 'https://linked.is/api/v1',
    headers: {
      'Authorization': `Bearer ${apiToken}`,
      'Content-Type': 'application/json'
    }
  };
};

const shorten = async (agent: LinkedisAgent, url: string, slug?: string): Promise<LinkData> => {
  """Create a short link"""
  const payload = { url };
  if (slug) {
    (payload as any)['slug'] = slug;
  }
  
  const response = await fetch(
    `${agent.baseUrl}/links`,
    {
      method: 'POST',
      headers: agent.headers,
      body: JSON.stringify(payload)
    }
  );
  
  const result = await response.json();
  return result.data;
};

const getStats = async (agent: LinkedisAgent, linkId: string): Promise<StatsData> => {
  """Get link statistics"""
  const response = await fetch(
    `${agent.baseUrl}/links/${linkId}/stats`,
    {
      headers: agent.headers
    }
  );
  
  const result = await response.json();
  return result.data;
};

const searchLinks = async (agent: LinkedisAgent, query: string): Promise<LinkData[]> => {
  """Search links by slug"""
  const response = await fetch(
    `${agent.baseUrl}/links?search=${encodeURIComponent(query)}`,
    {
      headers: agent.headers
    }
  );
  
  const result = await response.json();
  return result.data.items;
};

// Usage example
const handleAgentDemo = async () => {
  const agent = createAgent("YOUR_API_TOKEN");
  
  try {
    const shortLink = await shorten(agent, 'https://example.com/article');
    console.log(`Created: ${shortLink.shortUrl}`);
    
    const stats = await getStats(agent, shortLink.id);
    console.log("Link stats:", stats);
    
    const searchResults = await searchLinks(agent, "article");
    console.log("Search results:", searchResults);
  } catch (error) {
    console.error("Error:", error);
  }
};
</script>

<template>
  <div>
    <button @click="handleAgentDemo">Run AI Agent Demo</button>
  </div>
</template>

Resources