A share image is the picture that Slack, LinkedIn, Facebook, Discord and X show when someone posts your link. You will make it from a screenshot of the page. You do not store any files: the image URL in your HTML asks RenderScreenshot to render the page the first time a crawler fetches it.
The URL is signed: it carries a public key ID and a signature, never your API key. Nobody can change its options or use it to take other screenshots.
Create a signing key pair
Open API Keys and click Create Key. Enter a name for the site, for example Website og:image, and select Signing key pair.
Click Create Key. The dashboard shows two values one time only. Copy both.
Save the keys on your server
Add the two values to your server's environment, next to your API key:
RS_API_KEY=rs_live_... # server requests RS_PUBLIC_KEY_ID=rs_pub_... # public: goes into the image URL RS_SIGNING_KEY=rs_secret_... # secret: signs the image URL
Never send
RS_SIGNING_KEYorRS_API_KEYto the browser. Only the finished, signed URL goes into your HTML.
Sign the image URL in your template
The examples below sign a URL for the page itself with the og_card preset (1200×630). They also turn on caching for 30 days, so repeat crawler fetches return the first render for 0 credits.
The expiry changes once a week, not on every request. A URL that changes on every request looks like a new image to the social networks, so they would fetch it again and again.
Node.js
import { Client, TakeOptions } from 'renderscreenshot'; const DAY = 24 * 60 * 60 * 1000; const client = new Client(process.env.RS_API_KEY, { signingKey: process.env.RS_SIGNING_KEY, publicKeyId: process.env.RS_PUBLIC_KEY_ID, }); // The same expiry for 7 days, always 21 to 28 days ahead. function windowedExpiry(now = Date.now()) { const start = Math.floor(now / (7 * DAY)) * 7 * DAY; return new Date(start + 28 * DAY); } export function ogImageUrl(pageUrl) { return client.generateUrl( TakeOptions.url(pageUrl).preset('og_card').cacheAvailable().cacheTtl(2592000), windowedExpiry(), ); }
Python
import os import time from renderscreenshot import Client, TakeOptions DAY = 24 * 3600 client = Client( os.environ['RS_API_KEY'], signing_key=os.environ['RS_SIGNING_KEY'], public_key_id=os.environ['RS_PUBLIC_KEY_ID'], ) def windowed_expiry(now=None): """The same expiry for 7 days, always 21 to 28 days ahead.""" now = time.time() if now is None else now start = int(now // (7 * DAY)) * 7 * DAY return start + 28 * DAY def og_image_url(page_url): options = TakeOptions.url(page_url).preset('og_card').cache_available().cache_ttl(2592000) return client.generate_url(options, windowed_expiry())
Ruby (Rails helper)
# app/helpers/og_image_helper.rb module OgImageHelper DAY = 24 * 3600 OG_CLIENT = RenderScreenshot::Client.new( ENV.fetch('RS_API_KEY'), signing_key: ENV.fetch('RS_SIGNING_KEY'), public_key_id: ENV.fetch('RS_PUBLIC_KEY_ID') ) # The same expiry for 7 days, always 21 to 28 days ahead. def self.windowed_expiry(now = Time.now) start = (now.to_i / (7 * DAY)) * 7 * DAY Time.at(start + (28 * DAY)) end def og_image_url(page_url) options = RenderScreenshot::TakeOptions.url(page_url).preset('og_card').cache_available.cache_ttl(2_592_000) OG_CLIENT.generate_url(options, expires_at: OgImageHelper.windowed_expiry) end end
PHP, Go and Java versions, and a version for static sites, are in signed URLs in og:image tags.
Add the meta tags
Put the signed URL in the page's <head>. Your template engine escapes each & as &, which is correct.
<meta property="og:image" content="https://api.renderscreenshot.com/v1/screenshot?cache_available=true&cache_ttl=2592000&expires=...&key_id=rs_pub_...&preset=og_card&url=...&signature=..."> <meta property="og:image:width" content="1200"> <meta property="og:image:height" content="630"> <meta property="og:image:alt" content="Launch week: five new features"> <meta name="twitter:card" content="summary_large_image">
In Rails, with the helper from step 3:
<%= tag.meta property: 'og:image', content: og_image_url(request.original_url) %> <%= tag.meta property: 'og:image:width', content: 1200 %> <%= tag.meta property: 'og:image:height', content: 630 %>
Check the result
Copy the signed URL from your page source and open it once. That first fetch renders the image, so it uses one credit:
curl -sS -o card.png -w '%{http_code} %{content_type}\n' "$SIGNED_URL" # 200 image/png
Then paste your page URL into a link preview checker, or post it in a private Slack or Discord channel. The card shows the image, the title and the description from your other meta tags.
| You see | Cause | Fix |
|---|---|---|
| No image | The URL returns 401 | The signature or expiry is wrong. Sign with the same options you put in the URL. |
| An old image | The platform cached the first fetch | Use the platform's refresh tool, or change the page URL. |
| The page looks wrong | Cookie banner, lazy images | Add the fixes from remove cookie banners to the signed options. |
Make a designed card instead
The page itself is the simplest card. For a card with a big title and your logo, make a small route on your site, for example /og/launch-week, that lays out the title at 1200×630, and sign that URL instead. Generate OG images from HTML walks through a full card template.
Next steps
- Show link previews for links that your users paste.
- Reuse screenshots for 0 credits.
- Read the social card checklist before launch.