API метаданных URL: создавайте предварительный просмотр ссылок, как в Slack, за один вызов
Извлекайте теги Open Graph, данные Twitter Card, значки и заголовки страниц из любого URL-адреса с помощью одного запроса POST. Создавайте карточки предварительного просмотра ссылок, используя менее 20 строк кода.
Пользователь вставляет URL-адрес в ваше приложение чата. Вы хотите показать расширенную карточку предварительного просмотра с заголовком страницы, описание и миниатюрное изображение; та же карта отображается в Slack, Discord и iMessage. Ты мог бы получите страницу, проанализируйте HTML и извлеките теги Open Graph самостоятельно. Или вы можете отправить один POST-запрос.
Ботой /v1/url-metadata конечная точка извлекает любой URL-адрес, читает его
<meta> теги и возвращает структурированный JSON: заголовок OG, описание OG, изображение OG,
Данные Twitter Card, значок, канонический URL-адрес, язык и многое другое. Один вызов заменяет выборку,
HTML-парсер и резервная логика.
Конечная точка
curl -X POST https://api.botoi.com/v1/url-metadata \\
-H "Content-Type: application/json" \\
-d '{ "url": "https://github.com/anthropics/claude-code" }'
Ответ:
{
"success": true,
"data": {
"url": "https://github.com/anthropics/claude-code",
"status": 200,
"content_type": "text/html",
"title": "anthropics/claude-code: Claude Code is an agentic coding tool",
"description": "Claude Code is an agentic coding tool that lives in your terminal",
"og": {
"title": "anthropics/claude-code",
"description": "Claude Code is an agentic coding tool that lives in your terminal",
"image": "https://opengraph.githubassets.com/1/anthropics/claude-code",
"type": "object",
"url": "https://github.com/anthropics/claude-code",
"site_name": "GitHub"
},
"twitter": {
"card": "summary_large_image",
"title": "anthropics/claude-code",
"description": "Claude Code is an agentic coding tool that lives in your terminal",
"image": null
},
"favicon": "https://github.com/favicon.ico",
"canonical": "https://github.com/anthropics/claude-code",
"language": "en",
"author": null,
"keywords": [],
"theme_color": null
}
}
Ответ дает вам все необходимое для отображения карточки предварительного просмотра ссылки.
og Объект содержит теги Open Graph, которые считывают Slack и Discord.
twitter объект содержит теги Twitter Card. Когда на странице заданы оба параметра, вы получаете
оба. Если на странице не установлено ни того, ни другого, вы все равно получите HTML-код. title и
description в качестве запасного варианта.
Создайте компонент предварительного просмотра ссылки на приложение чата.
Этот компонент Preact принимает URL-адрес, вызывает API и отображает карточку с изображением OG, заголовком и описание и название сайта. Он возвращается к заголовку HTML, когда теги OG отсутствуют.
import { useState, useEffect } from "preact/hooks";
interface LinkPreview {
title: string | null;
description: string | null;
image: string | null;
favicon: string | null;
url: string;
siteName: string | null;
}
function useLinkPreview(url: string) {
const [preview, setPreview] = useState<LinkPreview | null>(null);
const [loading, setLoading] = useState(false);
useEffect(() => {
if (!url) return;
setLoading(true);
fetch("https://api.botoi.com/v1/url-metadata", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ url }),
})
.then((res) => res.json())
.then(({ data }) => {
setPreview({
title: data.og?.title || data.title,
description: data.og?.description || data.description,
image: data.og?.image || null,
favicon: data.favicon,
url: data.canonical || url,
siteName: data.og?.site_name || null,
});
})
.catch(() => setPreview(null))
.finally(() => setLoading(false));
}, [url]);
return { preview, loading };
}
function LinkPreviewCard({ url }: { url: string }) {
const { preview, loading } = useLinkPreview(url);
if (loading) {
return (
<div class="rounded-lg border border-gray-200 p-4 animate-pulse">
<div class="h-4 bg-gray-100 rounded w-3/4 mb-2"></div>
<div class="h-3 bg-gray-100 rounded w-full"></div>
</div>
);
}
if (!preview) return null;
return (
<a
href={preview.url}
target="_blank"
rel="noopener noreferrer"
class="block rounded-lg border border-gray-200 overflow-hidden
hover:border-gray-400 transition-colors no-underline"
>
{preview.image && (
<img
src={preview.image}
alt=""
class="w-full h-40 object-cover"
/>
)}
<div class="p-4">
<div class="flex items-center gap-2 mb-2">
{preview.favicon && (
<img src={preview.favicon} alt="" class="w-4 h-4" />
)}
<span class="text-xs text-gray-500">
{preview.siteName || new URL(preview.url).hostname}
</span>
</div>
<p class="font-semibold text-sm text-gray-900 mb-1">
{preview.title}
</p>
<p class="text-xs text-gray-600 line-clamp-2">
{preview.description}
</p>
</div>
</a>
);
}
The useLinkPreview Хук обрабатывает выборку и отображает ответ API в плоский
объект, который может использовать ваш пользовательский интерфейс. Резервная цепочка (data.og?.title || data.title)
означает, что вам всегда есть что отобразить, даже если на странице нет тегов OG. Компонент
отображает скелет загрузки во время вызова API, а затем заменяет карту предварительного просмотра.
Автоматическое заполнение метаданных SEO в CMS
Редакторы контента вставляют ссылочные URL-адреса при написании статей. Вместо того, чтобы копипастить их заголовок и описание вручную, ваша CMS может извлечь эти данные из URL-адреса и предварительно заполнить SEO-поля.
async function autoFillSeoFields(referenceUrl: string) {
const res = await fetch("https://api.botoi.com/v1/url-metadata", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: \`Bearer \${process.env.BOTOI_API_KEY}\`,
},
body: JSON.stringify({ url: referenceUrl }),
});
const { data } = await res.json();
return {
seoTitle: data.og?.title || data.title || "",
seoDescription: data.og?.description || data.description || "",
ogImage: data.og?.image || "",
canonical: data.canonical || referenceUrl,
favicon: data.favicon || "",
};
}
// Usage in a CMS admin panel
const fields = await autoFillSeoFields("https://stripe.com/docs/payments");
// fields.seoTitle → "Payments | Stripe Documentation"
// fields.seoDescription → "Accept payments online..."
// fields.ogImage → "https://images.stripe.com/..."
Когда редактор вставляет URL-адрес в поле «Ссылка», CMS вызывает autoFillSeoFields,
заполняет входные данные SEO-заголовка, описания и изображения OG и позволяет редактору настраивать их оттуда.
Тот же подход работает для менеджеров закладок, приложений для последующего чтения и внутренних вики-инструментов, которые
автоматически генерировать карточки из вставленных ссылок.
Функция Node.js с таймаутом и обработкой ошибок
В рабочей среде вам нужен тайм-аут, чтобы медленная целевая страница не блокировала ваш запрос на неопределенный срок.
Эта функция оборачивает вызов API 5-секундным интервалом. AbortController тайм-аут и
возвращает null при неудаче вместо броска.
async function getLinkPreview(url: string) {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 5000);
try {
const res = await fetch("https://api.botoi.com/v1/url-metadata", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: \`Bearer \${process.env.BOTOI_API_KEY}\`,
},
body: JSON.stringify({ url }),
signal: controller.signal,
});
if (!res.ok) {
throw new Error(\`API returned \${res.status}\`);
}
const { success, data } = await res.json();
if (!success) {
return null;
}
return {
title: data.og?.title || data.title,
description: data.og?.description || data.description,
image: data.og?.image,
favicon: data.favicon,
siteName: data.og?.site_name,
canonical: data.canonical,
twitterCard: data.twitter?.card,
};
} catch (err) {
console.error(\`Failed to fetch preview for \${url}:\`, err);
return null;
} finally {
clearTimeout(timeout);
}
}
Функция возвращает чистый объект с полями, необходимыми вашему пользовательскому интерфейсу. Вызывающие абоненты не прикасаются к сырому
Ответ API. Если целевая страница не работает или время запроса истекло, функция возвращает
null и ваше приложение может отображать резервный вариант вместо сбоя.
Обработка пограничных случаев
Не каждый URL-адрес сотрудничает. На некоторых страницах нет тегов OG. Некоторые из них находятся за цепочками перенаправления. Некоторые ответьте 10 секунд. Вот как действовать в каждом случае.
// 1. Pages with no OG tags: fall back to title + description
const preview = await getLinkPreview(url);
const displayTitle = preview?.title || "Untitled page";
const displayDesc = preview?.description || url;
const displayImage = preview?.image || "/fallback-thumbnail.png";
// 2. Detect redirects by comparing input URL to canonical
const inputUrl = "https://bit.ly/3xYzAbc";
const result = await getLinkPreview(inputUrl);
if (result?.canonical !== inputUrl) {
console.log(\`Redirected to: \${result?.canonical}\`);
}
// 3. Batch multiple URLs with Promise.allSettled
const urls = [
"https://github.com",
"https://stripe.com",
"https://vercel.com",
];
const previews = await Promise.allSettled(
urls.map((u) => getLinkPreview(u))
);
const results = previews.map((p, i) => ({
url: urls[i],
preview: p.status === "fulfilled" ? p.value : null,
}));
Нет тегов AND: Вернуться к title и description. Если
они также пусты, отобразите необработанный URL-адрес. Показывать изображение-заполнитель, когда og.image
является нулевым.
Перенаправления: API следует за перенаправлениями и возвращает метаданные с конечной страницы.
Сравните входной URL с canonical чтобы определить, когда произошло перенаправление.
Медленные страницы: Установите тайм-аут на своей стороне (в большинстве случаев подойдет 5 секунд). Сам API имеет внутренний тайм-аут, но вам следует использовать свой собственный, чтобы медленная цель не остановить ваш пользовательский опыт.
Пакетная загрузка: Использовать Promise.allSettled чтобы получить превью для
несколько URL-адресов параллельно. Неудачные запросы возвращаются null не блокируя остальных.
Ключевые моменты
-
POST /v1/url-metadataвозвращает теги OG, теги Twitter Card, favicon, canonical URL-адрес, язык и ключевые слова в одном ответе JSON. - Ответ отражает данные, которые Slack, Discord и iMessage используют для отображения предварительного просмотра ссылок. ты получить те же поля без написания парсера HTML.
- Анонимный доступ работает со скоростью 5 запросов в минуту без API-ключа. Достаточно для развития и приложения с низким трафиком.
-
Вернуться к
titleиdescriptionкогда теги OG отсутствуют. API всегда возвращает их из HTML.<head>когда они существуют. -
Для производственного использования добавьте тайм-аут, кэшируйте результаты по URL-адресу и обработайте
nullотвечает изящно. Проверьте Документация по API для полной справки по параметрам.
FAQ
- Что такое API метаданных URL-адреса?
- API метаданных URL-адреса извлекает веб-страницу и извлекает структурированные данные из ее HTML: заголовок страницы, метаописание, теги Open Graph (og:title, og:image, og:description), теги Twitter Card, URL-адрес значка, канонический URL-адрес и язык. Вы отправляете URL-адрес, и API возвращает все это в формате JSON. Это избавляет вас от необходимости самостоятельно получать страницу и анализировать необработанный HTML.
- Как Slack, Discord и iMessage создают предварительный просмотр ссылок?
- Когда пользователь вставляет URL-адрес, эти приложения получают страницу в фоновом режиме и считывают ее метатеги Open Graph (og:title, og:description, og:image). Они отображают карту предварительного просмотра на основе этих значений. Если теги OG отсутствуют, они возвращаются к заголовку HTML и мета-описанию. Конечная точка botoi /v1/url-metadata возвращает те же данные, которые считывают эти приложения, поэтому вы можете создавать идентичные карточки предварительного просмотра в своем собственном приложении.
- Что произойдет, если на странице нет тегов Open Graph?
- API по-прежнему возвращает заголовок HTML, метаописание, значок, канонический URL-адрес и язык. Поля og в ответе будут иметь значение null. Ваш интерфейс должен вернуться к заголовку и описанию, когда og.title и og.image отсутствуют.
- Соблюдает ли API перенаправления?
- Да. API следует перенаправлению HTTP 301/302/307/308 и возвращает метаданные из конечного целевого URL-адреса. Ответ включает разрешенный URL-адрес и его код состояния HTTP, поэтому вы можете обнаружить цепочки перенаправлений.
- Является ли API метаданных URL бесплатным?
- Анонимный доступ не требует ключа API и допускает 5 запросов в минуту плюс 100 в день. Это охватывает случаи разработки и использования с низким трафиком. Платные планы начинаются с 9 долларов в месяц для более высоких лимитов.
Начните разработку с botoi
150+ API-эндпоинтов для поиска, обработки текста, генерации изображений и утилит для разработчиков. Бесплатный тариф, без банковской карты.