Rich link previews
Send URL messages with iMessage-style rich previews, override the image and title, and batch-send multiple links in one request
When a message's text is exactly a URL, Blooio delivers it as an iMessage URL balloon — the rounded bubble with a hero image, bold title, and link preview, the same shape that appears when a user pastes a URL into Messages.app.
Previews are generated automatically from the page's Open Graph metadata. Pass an optional link_preview object to override the image, the title, or both.
Auto-generated preview
Send a single URL and Blooio handles the rest — it fetches the page's OG tags and builds the preview.
curl -X POST 'https://backend.blooio.com/v2/api/chats/%2B15551234567/messages' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"text": "https://example.com/launch"
}'const chatId = encodeURIComponent('+15551234567')
await fetch(`https://backend.blooio.com/v2/api/chats/${chatId}/messages`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.BLOOIO_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ text: 'https://example.com/launch' }),
})import os, requests
from urllib.parse import quote
chat_id = quote('+15551234567', safe='')
requests.post(
f'https://backend.blooio.com/v2/api/chats/{chat_id}/messages',
headers={
'Authorization': f"Bearer {os.environ['BLOOIO_API_KEY']}",
'Content-Type': 'application/json',
},
json={'text': 'https://example.com/launch'},
)Override the image and title
Pass a link_preview object with image_url, title, or both to replace what the auto-fetched preview would show.
curl -X POST 'https://backend.blooio.com/v2/api/chats/%2B15551234567/messages' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"text": "https://example.com/launch",
"link_preview": {
"image_url": "https://cdn.example.com/previews/launch-hero.png",
"title": "Our new product is here"
}
}'await fetch(url, {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
text: 'https://example.com/launch',
link_preview: {
image_url: 'https://cdn.example.com/previews/launch-hero.png',
title: 'Our new product is here',
},
}),
})Both fields inside link_preview are optional. If only title is provided, the image comes from auto-fetched OG metadata. If only image_url is provided, the title is auto-fetched.
Fields
| Field | Type | Description |
|---|---|---|
image_url | string (HTTPS URL) | Points to an image (png, jpg, webp, or gif). Blooio downloads it server-side and attaches it as the bubble's hero. Maximum 16 MB. |
title | string | Bold title line shown in the bubble. Maximum 200 characters. |
Batch — multiple URL messages in one request
Use the parts array and attach a link_preview to each part. Every part becomes its own URL-balloon message; messages are sent sequentially in the order of the array. The response contains message_ids and count instead of message_id.
curl -X POST 'https://backend.blooio.com/v2/api/chats/%2B15551234567/messages' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"parts": [
{
"text": "https://example.com/product",
"link_preview": {
"title": "Our product",
"image_url": "https://cdn.example.com/previews/product.png"
}
},
{
"text": "https://example.com/pricing",
"link_preview": { "title": "Pricing plans" }
},
{
"text": "https://example.com/contact",
"link_preview": {}
}
]
}'await fetch(url, {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
parts: [
{
text: 'https://example.com/product',
link_preview: {
title: 'Our product',
image_url: 'https://cdn.example.com/previews/product.png',
},
},
{
text: 'https://example.com/pricing',
link_preview: { title: 'Pricing plans' },
},
{
text: 'https://example.com/contact',
link_preview: {},
},
],
}),
})Example response:
{
"message_ids": ["msg_a1b2c3", "msg_d4e5f6", "msg_g7h8i9"],
"status": "queued",
"count": 3
}In batch mode (any part with a link_preview), every part must be text-only
and its text must be a single http(s) URL. Attachment parts and non-URL text
are rejected with a 400.
Validation rules
link_previewrequires the messagetext(or each per-parttext) to be exactly a single http(s) URL — no surrounding words or whitespace. Mixed text plus URL returns 400.- Cannot be combined with the
attachmentsfield. - The single-message form doesn't accept an array of
textvalues; usepartsfor batch mode.
Failure handling
Preview downloads are best-effort. If image_url can't be resolved (DNS error, 404, non-image content type, oversized file, or network timeout), the message still sends — Blooio silently falls back to the auto-fetched Open Graph preview. A broken preview URL never fails the request or returns 5xx.
What the recipient sees
The iMessage URL bubble on iPhone and iPad renders three elements:
- Large hero image
- Bold title line
- URL host derived from the link
Any field you don't provide comes from the page's OG tags. Fields the iOS bubble doesn't render (site name, long description) aren't exposed here — only what the recipient actually sees.
Animated previews
If the target URL's Open Graph metadata points at an animated webp or mp4 (e.g. a video preview), Blooio delivers it as an animated bubble automatically — the same behavior you get pasting the URL into Messages.app's compose view. No extra parameters needed.
Related
- Sending messages — base concepts
- Send a message — full API reference