LLM Integration
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.
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:
| Code | Meaning | Suggested Action |
|---|---|---|
INVALID_URL | URL format is invalid | Ask user to verify the URL |
SLUG_TAKEN | Custom slug already exists | Generate a different slug |
LINK_NOT_FOUND | Link doesn't exist | Inform user the link was not found |
PROFILE_NOT_FOUND | Profile doesn't exist | Suggest 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
- Store API tokens securely, never in code
- Use environment variables for token storage
- Implement token rotation policies
- Log API usage for audit purposes
Example: AI-Powered Link Manager
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
- Full API Reference
- Authentication Guide
- Rate Limits
- Machine-readable docs:
https://linked.is/llms.txt