API metadata URL: buat pratinjau tautan seperti Slack dalam satu panggilan
Ekstrak tag Open Graph, data Twitter Card, favicon, dan judul halaman dari URL mana pun dengan satu permintaan POST. Buat kartu pratinjau tautan dalam kurang dari 20 baris kode.
Seorang pengguna menempelkan URL di aplikasi obrolan Anda. Anda ingin menampilkan kartu pratinjau kaya dengan judul halaman, deskripsi, dan gambar kecil; tampilan kartu Slack, Discord, dan iMessage yang sama. Anda bisa ambil halaman, parsing HTML, dan ekstrak sendiri tag Open Graph. Atau Anda bisa mengirimkannya permintaan POSTING.
Botoi itu /v1/url-metadata titik akhir mengambil URL apa pun, membacanya
<meta> tag, dan mengembalikan JSON terstruktur: judul OG, deskripsi OG, gambar OG,
Data Kartu Twitter, favicon, URL kanonik, bahasa, dan lainnya. Satu panggilan menggantikan pengambilan, yaitu
Parser HTML, dan logika fallback.
Titik akhir
curl -X POST https://api.botoi.com/v1/url-metadata \\
-H "Content-Type: application/json" \\
-d '{ "url": "https://github.com/anthropics/claude-code" }'
Tanggapan:
{
"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
}
}
Responsnya memberi Anda semua yang Anda perlukan untuk merender kartu pratinjau tautan. Itu
og objek berisi tag Open Graph yang dibaca Slack dan Discord.
Itu twitter objek berisi tag Kartu Twitter. Ketika sebuah halaman mengatur keduanya, Anda mendapatkannya
keduanya. Ketika suatu halaman tidak menetapkan keduanya, Anda masih mendapatkan HTML title Dan
description sebagai fallback.
Buat komponen pratinjau tautan aplikasi obrolan
Komponen Preact ini mengambil URL, memanggil API, dan merender kartu dengan gambar OG, judul, deskripsi, dan nama situs. Itu kembali ke judul HTML ketika tag OG tidak ada.
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>
);
}
Itu useLinkPreview hook menangani pengambilan dan memetakan respons API ke flat
objek yang dapat digunakan oleh UI Anda. Rantai mundur (data.og?.title || data.title)
berarti Anda selalu memiliki sesuatu untuk ditampilkan, bahkan ketika halaman tidak memiliki tag OG. Komponen
merender kerangka pemuatan saat panggilan API sedang dijalankan, lalu menukarnya di kartu pratinjau.
Isi otomatis metadata SEO di CMS
Editor konten menempelkan URL referensi saat menulis artikel. Daripada membuat mereka copy-paste judul dan deskripsi secara manual, CMS Anda dapat mengambil data tersebut dari URL dan mengisinya terlebih dahulu bidang 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/..."
Saat editor menempelkan URL di kolom "referensi", CMS akan memanggil autoFillSeoFields,
mengisi judul SEO, deskripsi, dan input gambar OG, dan memungkinkan editor mengubah dari sana.
Pendekatan yang sama berlaku untuk pengelola bookmark, aplikasi baca nanti, dan alat wiki internal
buat kartu secara otomatis dari tautan yang ditempel.
Fungsi Node.js dengan batas waktu dan penanganan kesalahan
Dalam produksi, Anda menginginkan waktu tunggu sehingga halaman target yang lambat tidak memblokir permintaan Anda tanpa batas waktu.
Fungsi ini membungkus panggilan API dengan waktu 5 detik AbortController batas waktu dan
kembali null pada kegagalan bukannya melempar.
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);
}
}
Fungsi ini mengembalikan objek bersih dengan bidang yang dibutuhkan UI Anda. Penelepon tidak menyentuh yang mentah
Respons API. Jika halaman target tidak aktif atau permintaan habis, fungsi akan kembali
null dan aplikasi Anda dapat menampilkan fallback, bukannya error.
Menangani kasus tepi
Tidak semua URL dapat bekerja sama. Beberapa halaman tidak memiliki tag OG. Beberapa berada di balik rantai pengalihan. Beberapa luangkan waktu 10 detik untuk merespons. Berikut cara menangani setiap kasus.
// 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,
}));
Tidak ada DAN tag: Jatuh kembali ke title Dan description. Jika
itu juga kosong, tampilkan URL mentahnya. Tampilkan gambar placeholder kapan og.image
adalah nol.
Pengalihan: API mengikuti pengalihan dan mengembalikan metadata dari halaman akhir.
Bandingkan URL masukan dengan canonical untuk mendeteksi kapan pengalihan terjadi.
Halaman lambat: Tetapkan batas waktu di sisi Anda (5 detik berfungsi untuk sebagian besar kasus). Itu API itu sendiri memiliki batas waktu internal, tetapi Anda harus menerapkan batas waktu Anda sendiri sehingga target yang lambat tidak menghentikan pengalaman pengguna Anda.
Pengambilan batch: Menggunakan Promise.allSettled untuk mengambil pratinjau
beberapa URL secara paralel. Permintaan yang gagal dikembalikan null tanpa menghalangi sisanya.
Poin-poin penting
-
POST /v1/url-metadatamengembalikan tag OG, tag Kartu Twitter, favicon, kanonik URL, bahasa, dan kata kunci dalam satu respons JSON. - Responsnya mencerminkan data yang digunakan Slack, Discord, dan iMessage untuk merender pratinjau tautan. kamu dapatkan bidang yang sama tanpa menulis parser HTML.
- Akses anonim berfungsi pada 5 permintaan per menit tanpa kunci API. Cukup untuk pengembangan dan aplikasi dengan lalu lintas rendah.
-
Jatuh kembali ke
titleDandescriptionketika tag OG hilang. API selalu mengembalikan ini dari HTML<head>ketika mereka ada. -
Untuk penggunaan produksi, tambahkan batas waktu, cache hasil berdasarkan URL, dan tangani
nulltanggapan dengan anggun. Periksa dokumen API untuk referensi parameter lengkap.
FAQ
- Apa itu API metadata URL?
- API metadata URL mengambil halaman web dan mengekstrak data terstruktur dari HTML-nya: judul halaman, deskripsi meta, tag Open Graph (og:title, og:image, og:description), tag Twitter Card, URL favicon, URL kanonik, dan bahasa. Anda mengirim URL, dan API mengembalikan semua ini sebagai JSON. Ini menyelamatkan Anda dari mengambil halaman sendiri dan menguraikan HTML mentah.
- Bagaimana cara Slack, Discord, dan iMessage menghasilkan pratinjau tautan?
- Saat pengguna menempelkan URL, aplikasi ini mengambil halaman di latar belakang dan membaca tag meta Open Graph-nya (og:title, og:description, og:image). Mereka merender kartu pratinjau dari nilai-nilai tersebut. Jika tag OG tidak ada, tag tersebut akan kembali ke judul HTML dan deskripsi meta. Titik akhir botoi /v1/url-metadata mengembalikan data yang sama yang dibaca oleh aplikasi ini, sehingga Anda dapat membuat kartu pratinjau yang identik di aplikasi Anda sendiri.
- Apa yang terjadi jika halaman tidak memiliki tag Open Graph?
- API masih mengembalikan judul HTML, deskripsi meta, favicon, URL kanonik, dan bahasa. Bidang og dalam respons akan bernilai nol. Frontend Anda akan kembali ke judul dan deskripsi ketika og.title dan og.image tidak ada.
- Apakah API mengikuti pengalihan?
- Ya. API mengikuti pengalihan HTTP 301/302/307/308 dan mengembalikan metadata dari URL tujuan akhir. Responsnya mencakup URL yang diselesaikan dan kode status HTTP-nya, sehingga Anda dapat mendeteksi rantai pengalihan.
- Apakah API metadata URL gratis?
- Akses anonim tidak memerlukan kunci API dan memungkinkan 5 permintaan per menit ditambah 100 per hari. Itu mencakup kasus pengembangan dan penggunaan lalu lintas rendah. Paket berbayar mulai dari $9/bulan untuk batas tarif yang lebih tinggi.
Mulai membangun dengan botoi
150+ endpoint API untuk pencarian, pemrosesan teks, pembuatan gambar, dan utilitas developer. Paket gratis, tanpa kartu kredit.