Someone pastes a link into your comment box and you want to render the card Slack renders: title, description, favicon, preview image. Building that yourself means fetching the page, following redirects, parsing <meta> tags, resolving relative image paths against the final URL, and handling the third of the web that ships no OG tags. You end up maintaining a scraper.
SiteIntel does that fetch-and-parse in one GET. Here's the whole thing.
The call
curl -s "https://siteintel.p.rapidapi.com/v1/analyze?url=https://stripe.com" \
-H "X-RapidAPI-Key: $RAPIDAPI_KEY" \
-H "X-RapidAPI-Host: siteintel.p.rapidapi.com"
The url param takes a full URL with the scheme, not a bare domain. URL-encode it if the target has its own query string, or you'll lose everything after the first &.
You get back:
{
"query": "https://stripe.com",
"final_url": "https://stripe.com/",
"status": 200,
"fetched_at": "2026-08-14T15:02:11.418Z",
"title": "Stripe | Financial Infrastructure to Grow Your Revenue",
"description": "Stripe powers online and in-person payment processing...",
"canonical": "https://stripe.com/",
"lang": "en",
"favicon": "https://stripe.com/favicon.ico",
"open_graph": {
"title": "Stripe | Financial Infrastructure to Grow Your Revenue",
"image": "https://images.stripecdn.com/...og.png",
"site_name": "Stripe",
"type": "website"
},
"detected_tech": ["Cloudflare", "React"],
"social_links": ["https://twitter.com/stripe"],
"emails": [],
"server": "nginx"
}
In Node
Node 18+ has fetch globally, so there's no dependency to install.
const HOST = 'siteintel.p.rapidapi.com';
async function analyze(url) {
const endpoint = `https://${HOST}/v1/analyze?url=${encodeURIComponent(url)}`;
const res = await fetch(endpoint, {
headers: {
'X-RapidAPI-Key': process.env.RAPIDAPI_KEY,
'X-RapidAPI-Host': HOST,
},
});
if (!res.ok) throw new Error(`SiteIntel returned ${res.status}`);
return res.json();
}
function toCard(data) {
const og = data.open_graph || {};
return {
url: data.final_url,
title: og.title || data.title || data.final_url,
description: data.description || '',
image: og.image || null,
icon: data.favicon,
site: og.site_name || new URL(data.final_url).hostname,
};
}
const data = await analyze('https://stripe.com');
console.log(toCard(data));
The fallback chain in toCard is the part that matters. Plenty of pages set <title> but no og:title, or the reverse. Any of these fields can come back null when the page never declared them, so read them defensively rather than assuming a full house.
Reading the response
query is what you sent; final_url is where you landed after redirects. Key your cache on final_url, not query, or you'll store four copies of the same article behind four different tracking URLs. status is the HTTP status of the target page, so a 200 from the API with a status of 404 means the fetch worked and the page is gone. Don't render a card for that.
favicon and open_graph.image come back as absolute URLs, already resolved against the final URL. That's the piece that eats an afternoon when you write the parser yourself.
Two fields are flat arrays of strings, not objects: detected_tech is ["Cloudflare", "React"], and social_links is a list of URLs. If you're mapping over them, you're mapping over strings.
What I'd build with it
A preview cache. When a user submits a link, call /v1/analyze once, run toCard, and store the result as a row next to the comment with the fetched_at timestamp. Render from your own database from then on. The user's page load never waits on a third-party fetch, and a slow or dead target site degrades to a plain link instead of hanging your request.
Two things fall out of that design. Refresh a row when fetched_at gets older than your TTL, seven days works fine for article metadata. And when open_graph.image comes back null, fall back to GET /v1/screenshot?url=... for that URL and store the screenshot as the card image. Sites without OG tags still get a picture.
There's also GET /v1/seo-audit?url=... on the same base URL if you want an on-page report rather than a preview.
Working examples and the full response fields: github.com/clause-netizen/siteintel-api. It's on RapidAPI if you want a managed key.
Top comments (0)