API de metadados de URL: crie visualizações de links como o Slack em uma chamada
Extraia tags Open Graph, dados de cartões do Twitter, favicons e títulos de páginas de qualquer URL com uma solicitação POST. Crie cartões de visualização de links em menos de 20 linhas de código.
Um usuário cola um URL no seu aplicativo de chat. Você deseja mostrar um cartão de visualização avançado com o título da página, descrição e imagem em miniatura; o mesmo cartão Slack, Discord e iMessage exibem. Você poderia busque a página, analise o HTML e extraia você mesmo as tags Open Graph. Ou você pode enviar um Solicitação POST.
O botoi /v1/url-metadata endpoint busca qualquer URL, lê seu
<meta> tags e retorna JSON estruturado: título OG, descrição OG, imagem OG,
Dados do cartão do Twitter, favicon, URL canônico, idioma e muito mais. Uma chamada substitui a busca, o
Analisador HTML e lógica de fallback.
O ponto final
curl -X POST https://api.botoi.com/v1/url-metadata \\
-H "Content-Type: application/json" \\
-d '{ "url": "https://github.com/anthropics/claude-code" }'
Resposta:
{
"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
}
}
A resposta fornece tudo que você precisa para renderizar um cartão de visualização de link. O
og O objeto contém as tags Open Graph que o Slack e o Discord leem.
O twitter O objeto contém as tags do cartão do Twitter. Quando uma página define ambos, você obtém
ambos. Quando uma página não define nenhum dos dois, você ainda obtém o HTML title e
description como substitutos.
Crie um componente de visualização de link do aplicativo de bate-papo
Este componente Preact pega um URL, chama a API e renderiza um cartão com a imagem OG, título, descrição e nome do site. Ele volta ao título HTML quando as tags OG estão faltando.
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>
);
}
O useLinkPreview hook lida com a busca e mapeia a resposta da API para um plano
objeto que sua IU pode consumir. A cadeia de fallback (data.og?.title || data.title)
significa que você sempre tem algo para exibir, mesmo quando uma página não tem tags OG. O componente
renderiza um esqueleto de carregamento enquanto a chamada da API está em andamento e, em seguida, troca o cartão de visualização.
Preencher automaticamente metadados de SEO em um CMS
Os editores de conteúdo colam URLs de referência ao escrever artigos. Em vez de fazê-los copiar e colar o título e a descrição manualmente, seu CMS pode extrair esses dados do URL e preencher previamente o Campos de 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/..."
Quando um editor cola uma URL no campo “referência”, o CMS chama autoFillSeoFields,
preenche o título SEO, a descrição e as entradas da imagem OG e permite que o editor ajuste a partir daí.
A mesma abordagem funciona para gerenciadores de favoritos, aplicativos de leitura posterior e ferramentas wiki internas que
gerar cartões automaticamente a partir de links colados.
Função Node.js com tempo limite e tratamento de erros
Na produção, você deseja um tempo limite para que uma página de destino lenta não bloqueie sua solicitação indefinidamente.
Esta função envolve a chamada da API com um intervalo de 5 segundos AbortController tempo limite e
retorna null no fracasso em vez de lançar.
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);
}
}
A função retorna um objeto limpo com os campos que sua IU precisa. Os chamadores não tocam no cru
Resposta da API. Se a página de destino estiver inativa ou a solicitação expirar, a função retornará
null e seu aplicativo pode mostrar um substituto em vez de travar.
Lidando com casos extremos
Nem todo URL coopera. Algumas páginas não possuem tags OG. Alguns estão por trás de cadeias de redirecionamento. Alguns leve 10 segundos para responder. Veja como lidar com cada caso.
// 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,
}));
Sem tags AND: Volte para title e description. Se
estes também estão vazios, exiba o URL bruto. Mostrar uma imagem de espaço reservado quando og.image
é nulo.
Redirecionamentos: A API segue redirecionamentos e retorna metadados da página final.
Compare o URL de entrada com canonical para detectar quando um redirecionamento aconteceu.
Páginas lentas: Defina um tempo limite para você (5 segundos funcionam para a maioria dos casos). O A própria API tem um tempo limite interno, mas você deve impor o seu próprio para que um alvo lento não paralisar a experiência do usuário.
Busca em lote: Usar Promise.allSettled para buscar visualizações para
vários URLs em paralelo. Retorno de solicitações com falha null sem bloquear o resto.
Pontos-chave
-
POST /v1/url-metadataretorna tags OG, tags de cartão do Twitter, favicon, canônico URL, idioma e palavras-chave em uma resposta JSON. - A resposta reflete os dados que Slack, Discord e iMessage usam para renderizar visualizações de links. Você obtenha os mesmos campos sem escrever um analisador HTML.
- O acesso anônimo funciona a 5 solicitações por minuto sem chave de API. O suficiente para o desenvolvimento e aplicativos de baixo tráfego.
-
Volte para
titleedescriptionquando as tags OG estão faltando. A API sempre os retorna do HTML<head>quando eles existem. -
Para uso em produção, adicione um tempo limite, armazene em cache os resultados por URL e manipule
nullrespostas graciosamente. Verifique o Documentos da API para obter a referência completa do parâmetro.
FAQ
- O que é uma API de metadados de URL?
- Uma API de metadados de URL busca uma página da web e extrai dados estruturados de seu HTML: título da página, meta descrição, tags Open Graph (og:title, og:image, og:description), tags de cartão do Twitter, URL de favicon, URL canônico e idioma. Você envia uma URL e a API retorna tudo isso como JSON. Isso evita que você busque a página sozinho e analise o HTML bruto.
- Como o Slack, o Discord e o iMessage geram visualizações de links?
- Quando um usuário cola um URL, esses aplicativos buscam a página em segundo plano e leem suas metatags Open Graph (og:title, og:description, og:image). Eles renderizam um cartão de visualização desses valores. Se as tags OG estiverem faltando, elas retornarão ao título HTML e à meta descrição. O endpoint botoi /v1/url-metadata retorna os mesmos dados que esses aplicativos leem, para que você possa criar cartões de visualização idênticos em seu próprio aplicativo.
- O que acontece se uma página não tiver tags Open Graph?
- A API ainda retorna o título HTML, meta descrição, favicon, URL canônico e idioma. Os campos og na resposta serão nulos. Seu frontend deve voltar ao título e à descrição quando og.title e og.image estiverem faltando.
- A API segue redirecionamentos?
- Sim. A API segue redirecionamentos HTTP 301/302/307/308 e retorna metadados do URL de destino final. A resposta inclui o URL resolvido e seu código de status HTTP, para que você possa detectar cadeias de redirecionamento.
- A API de metadados de URL é gratuita?
- O acesso anônimo não requer chave de API e permite 5 solicitações por minuto mais 100 por dia. Isso cobre casos de desenvolvimento e uso de baixo tráfego. Os planos pagos começam em US$ 9/mês para limites de taxas mais altos.
Comece a construir com botoi
150+ endpoints de API para consultas, processamento de texto, geração de imagens e utilitários para desenvolvedores. Plano gratuito, sem cartão de crédito.