A moving card stops the scroll better than a still one: a countdown that ticks, a number that counts up, a headline that types itself. With an HTML-template image API you do not need a video editor for that — the same template that renders your 1200×630 PNG can render a short animated GIF or MP4, from the same fields, in one request.
This guide covers where animated images actually play (fewer places than you would hope, and og:image is mostly not one of them), how to render one with Shareglint, how to write a template that works as a still and as a clip, and what it costs.
Where animation plays, and where it does not
Start with the uncomfortable part. The og:image tag is defined by the Open Graph protocol as "an image URL which should represent your object" — a picture, and crawlers treat it like one. X's card documentation states that for GIFs only the first frame is used. Other networks do not promise animation in link previews either. So an animated GIF in og:image usually shows as its first frame — which, for most animations, is the emptiest moment of the clip.
Where animation does work is when the file itself is the content:
- Social posts. Upload the MP4 (or GIF) to X, LinkedIn, Instagram or Facebook as the post's media. That is a video post, not a link preview, and it plays in the feed.
- Chat. Slack and Discord play GIFs posted or uploaded into a channel. Discord's link preview documentation also describes video embeds: point
og:videoat an.mp4, andog:image"becomes the preview frame". - Email. GIF is the only animation that works broadly in email. Can I email lists the exceptions: Outlook for Windows does not animate GIFs (with Microsoft 365 accounts it plays three times and stops). The HTML
<video>element is far worse — Gmail strips it entirely. - Your own pages. An MP4 in
<video autoplay muted loop playsinline>is the lightest way to show the clip on a landing page or in a blog post.
So the practical pattern is: a still PNG or JPEG in og:image, the GIF or MP4 for posts, chat and email — all three from one template.
Render a GIF or MP4 with one request
Pick a template and set format to gif or mp4. Two optional parameters shape the clip: duration in seconds (0.5 to 5, default 3) and fps (10 to 30; default 15 for GIF, 30 for MP4).
curl -X POST "https://shareglint.com/v1/images?wait=true" \
-H "Authorization: Bearer sk_live_…" \
-H "Content-Type: application/json" \
-d '{
"template": "animated-stat-counter",
"format": "mp4",
"duration": 3,
"modifications": {
"value": "1,240,000",
"label": "images rendered last month",
"site": "northwind.dev"
}
}'
The response is an ordinary render with one extra field:
{
"id": "img_01m08…",
"status": "completed",
"format": "mp4",
"animation": { "duration": 3, "fps": 30, "frames": 90 },
"url": "…/img_01m08….mp4"
}
The long edge is capped at 1080 px with even dimensions, so the 1200×630 og size comes out as a 1080×566 file and a 1080×1920 story as 608×1080 — plenty for a feed, and it keeps files small. quality and scale do not apply. size and sizes work as for stills, so one request can give you the landscape clip and the vertical story version together.
The GIF loops forever, with a palette computed for the clip; the MP4 is H.264 with faststart and no audio track, which every platform and browser plays. A 3-second GIF at 15 fps is roughly 0.1 to 3 MB, depending on how much of the frame moves; the same clip as MP4 is a fraction of that.
Without a server, use a signed URL: the extension picks the format, and duration and fps are signed like every other parameter, so nobody can change them on your quota.
<img src="https://shareglint.com/i/acc_…/animated-countdown.gif?number=12&unit=days%20to%20go&duration=4&s=…" alt="12 days to go">
An animated render takes a few seconds rather than one, so ?wait=true and the first request to a signed URL wait longer than for a still; later requests redirect to the cached file at once.
Built-in animated templates
The template library has ready-made animated designs — countdown, stat counter, typing headline, product reveal, quote fade-in, sale pulse and more — with slugs starting with animated-. Like every built-in template they lay themselves out in all 6 sizes (og 1200×630, square 1080×1080, portrait 1080×1350, story 1080×1920, pinterest 1000×1500, youtube 1280×720) and take accent and theme. Rendered as PNG they show the finished frame, so the same slug serves your og:image and your post.
Animating your own template
Animation is recorded, not filmed: the renderer pauses every CSS animation and transition and steps it to the exact time of each frame. The same fields therefore always give the same file — nothing depends on how busy the browser was — and anything CSS can animate works: transform, opacity, colours, clip-path, filters, keyframes with delays.
Two hooks make one template work as a still and as a clip. During a GIF or MP4 render the <html> element gets the class motion and the CSS variable --clip with the clip length (3s by default). A still render of the same template has no motion class. Put the animation under .motion and time it with var(--clip):
.title { opacity: 1; } /* the still: the finished design */
.motion .title { animation: rise var(--clip) ease-out both; }
@keyframes rise { from { opacity: 0; transform: translateY(40px); } }
.motion .badge { animation: pop calc(var(--clip) * 0.3) calc(var(--clip) * 0.5) both; }
@keyframes pop { from { transform: scale(0); } }
Timing everything as a fraction of --clip means a 2-second and a 5-second render both play the whole animation. Make the last frame the finished design, because a GIF loops back to the start and email clients that do not animate show only the first frame — so it is worth making the first frame readable too: fade the decoration in, not the headline.
Your templates cannot run scripts (they are removed when you save), so animate with CSS. Counting numbers and typed text in the built-in templates use a hook of our own that is not available to custom HTML.
Check the still without spending quota in the template preview in the dashboard, which renders the PNG version. For the motion, one GIF at a low fps is enough to judge the timing before you render the final MP4.
What it costs
An animated render uses 5 renders of your monthly quota instead of one, per size — capturing and encoding dozens of frames is that much more work. Everything else works as for stills: the same template with the same fields and the same duration and fps again returns the existing file for free, and a render that fails gives all 5 back. The Free plan (100 renders a month) includes animated formats, with the same watermark as stills. Bulk batches render still formats only; for a series of clips, call the API once per clip.
Checklist
- Keep a still PNG or JPEG in
og:image; use the GIF or MP4 where the file itself is shown. - MP4 for social posts, Discord and web pages; GIF for email and image-only places.
- Scope animations under
.motion, time them withvar(--clip), end on the finished design. - Stay short: 2–4 seconds loops well and keeps GIFs small.
- Use
size: "story"orsizesfor the vertical version from the same request.