Unfurl¶
Unfurl resolves one URL into the link preview a message would show for it. The API forwards the URL to the TEBHOS unfurl service, which picks a site-specific resolver or falls back to reading the page's Open Graph and meta tags. The same resolver chain is used for link previews attached to messages.
Unfurl embed object¶
The response of Resolve URL embed. This is a flat preview object, not a list of message embeds.
Structure¶
| Field | Type | Description |
|---|---|---|
| url | string | The URL that was resolved |
| title | ?string | The page or media title |
| description | ?string | The page description |
| image | ?string | A preview image URL |
| site_name | ?string | The site or provider name |
| author | ?string | The author name, when the page exposes one |
| type | ?string | The preview type, for example link, video, article, social or gifv |
| video | ?unfurl video object | Playable video details, when the resolver found a video |
| color | ?string | A theme colour hint, for example #1DA1F2 |
| favicon | ?string | The site favicon URL |
Text fields (title, description, site_name, author) are sanitised before they are returned.
Unfurl video object¶
| Field | Type | Description |
|---|---|---|
| url | string | The video URL |
| width | ?integer | The video width |
| height | ?integer | The video height |
| embed_url | ?string | An embeddable player URL (for example a YouTube iframe URL) |
Example¶
{
"url": "https://example.com/article",
"title": "Example article",
"description": "A short description taken from the page metadata.",
"image": "https://example.com/cover.png",
"site_name": "Example",
"author": null,
"type": "article",
"video": null,
"color": null,
"favicon": "https://example.com/favicon.ico"
}
Site resolvers¶
| Site | Matched URL | Notes |
|---|---|---|
| YouTube | youtube.com, www.youtube.com, m.youtube.com, music.youtube.com, youtube-nocookie.com, www.youtube-nocookie.com, youtu.be, when a video ID can be parsed |
Uses the YouTube Data API. The instance needs a YouTube API key; without one no preview is produced. Non-video YouTube pages use the generic resolver |
| Klipy | klipy.com and www.klipy.com with a path under /gif, /gifs, /clip or /clips |
Uses the Klipy API and returns a gifv preview. The instance needs a Klipy API key |
| Wikipedia | Any URL containing .wikipedia.org/wiki/ |
Uses the Wikipedia REST summary; falls back to the generic resolver on failure |
| Twitter / X | Any URL containing twitter.com or x.com |
Generic scrape with the site name set to Twitter or X, type social, and the author derived from the path; falls back to the generic resolver on failure |
| Generic | Every other URL | Reads og:*, twitter:* and standard meta tags from the first 500 KB of an HTML page. A non-HTML response produces a bare {url, type: "link"} preview |
Network policy¶
The API rejects a URL before resolution when it cannot be parsed, its scheme is not http or https, or its host is a local or private address (localhost, loopback, link-local, private IPv4 ranges, cloud metadata hosts).
The unfurl service applies a second check to every URL: it must be at most 8192 characters with no control characters, have no user information, not use port 0, have a valid public hostname, and resolve only to public IP addresses. A page fetch times out after 5 seconds and follows at most 3 redirects. The whole resolution is abandoned after 20 seconds.
Cache behaviour¶
Successful results are cached for one hour per exact URL. A repeated request within that time is served from the cache without contacting the site.
Resolve URL embed¶
GET /embed
Resolves the supplied URL and returns one unfurl embed object. No authentication is required and no permission applies.
Query parameters¶
| Field | Type | Description |
|---|---|---|
| url | string | Absolute http or https URL to resolve |
Response¶
| Status | Body | Condition |
|---|---|---|
| 200 | unfurl embed object | The URL was resolved |
| 400 | error response | url is missing, malformed, blocked by the network policy, cannot be fetched, or is a YouTube/Klipy URL on an instance without the matching API key (BAD_REQUEST) |
| 500 | error response | The unfurl service is unavailable or failed internally (INTERNAL_SERVER_ERROR) |
Side effects¶
Resolution can fetch the URL and the provider APIs listed above. It creates no message.
Rate limit¶
This route has no route-specific bucket. Authenticated requests count toward the global per-client limit described in Rate limits; unauthenticated GET requests are not counted.