ScreenshotNeo

BlogHow-to

How to Generate an Open Graph Image from HTML with HTMLCSStoImage

Generate Open Graph images with HTMLCSStoImage, add crawler-ready metadata, and fix stale social previews with a direct API or automatic config.

By the ScreenshotNeo team4 October 202610 min read

To generate an Open Graph image from HTML with HTMLCSStoImage, send your markup and optional CSS to POST https://hcti.io/v1/image, authenticated with HTTP Basic using your API ID and API key. Use the returned image URL in server-rendered og:image and twitter:image tags. If every page on your site needs its own card, an HTML/CSS to Image OG Image Config can map your public site paths to generated image URLs automatically.

These are two separate jobs: rendering an image and making social crawlers discover the right public image URL. A successful render alone does not guarantee that a social preview updates; the source metadata, rendered image, and destination platform can each be cached.

1. Choose direct rendering or an OG Image Config

Workflow Use it when Input and result
Direct image API An application or build process has specific HTML, CSS, or a public page to capture. POST HTML and optional CSS, or a public URL, to the image endpoint. The response includes an image URL and ID.
OG Image Config A website, CMS, store, or static site needs a share card for many public routes. Associate your public site origin with a path-based HCTI image URL. HCTI maps each image path to the corresponding source page and can use its metadata or a configured template.

For a one-off or application-generated card, direct rendering gives you control over the markup at request time. For route-based cards, the config avoids constructing and storing a separate image request for every page. The official [HTML/CSS to Image API guide](https://docs.htmlcsstoimage.com/getting-started/) documents the direct API, while the [OG Image Config guide](https://docs.htmlcsstoimage.com/use-cases/og-image/) covers path-based generation.

2. Render HTML and CSS with the API

Send either an html value or a fully qualified public url. They are alternatives; if url is provided, it takes precedence. The optional css field styles HTML input. Authenticate with HTTP Basic: API ID is the username and API key is the password. Keep both credentials in trusted server-side code.

Minimal HTML request with cURL

curl -X POST "https://hcti.io/v1/image" \
  -u "YOUR_API_ID:YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"html":"<div class=\"card\"><h1>Hello world</h1></div>","css":"body { margin: 0; } .card { width: 1200px; height: 630px; display: grid; place-items: center; background: #f4f7fb; color: #14213d; font: 700 64px Arial, sans-serif; }"}'

The JSON response includes a generated image URL and ID. Save the URL or use it directly in page metadata. The endpoint supports PNG, JPG, WebP, and PDF output options; consult the current [API documentation](https://docs.htmlcsstoimage.com/getting-started/) for the exact request options and response fields for the format you need.

Python example

import os
import requests

api_id = os.environ["HCTI_API_ID"]
api_key = os.environ["HCTI_API_KEY"]

response = requests.post(
    "https://hcti.io/v1/image",
    auth=(api_id, api_key),
    json={
        "html": "<div class='card'><h1>Hello from HTML</h1></div>",
        "css": "body { margin: 0; } .card { width: 1200px; height: 630px; display: grid; place-items: center; background: #f4f7fb; color: #14213d; font: 700 56px Arial, sans-serif; }",
    },
    timeout=90,
)
response.raise_for_status()
result = response.json()
print(result["url"])
print(result["id"])

Node.js example

const apiId = process.env.HCTI_API_ID;
const apiKey = process.env.HCTI_API_KEY;
if (!apiId || !apiKey) throw new Error('Set HCTI_API_ID and HCTI_API_KEY');

const credentials = Buffer.from(`${apiId}:${apiKey}`).toString('base64');
const response = await fetch('https://hcti.io/v1/image', {
  method: 'POST',
  headers: {
    Authorization: `Basic ${credentials}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    html: "<div class='card'><h1>Hello from HTML</h1></div>",
    css: 'body { margin: 0; } .card { width: 1200px; height: 630px; display: grid; place-items: center; background: #f4f7fb; color: #14213d; font: 700 56px Arial, sans-serif; }',
  }),
});
if (!response.ok) throw new Error(`HTMLCSStoImage returned ${response.status}: ${await response.text()}`);
const result = await response.json();
console.log(result.url, result.id);

Choose HTML or URL input deliberately

  • Use html plus optional css for a custom card layout generated from application data. Escape user-controlled values before inserting them into HTML, and do not place secrets in rendered markup.
  • Use url to capture an existing public webpage. The page must be reachable by the service. Do not send both expecting them to combine; a provided URL overrides HTML.
  • Plan for public delivery. Social crawlers need an absolute, publicly reachable image URL. An image URL that requires your private session or local filesystem will not work for a public preview.

3. Add crawler-readable Open Graph metadata

After rendering, place the absolute image URL in the page’s server-rendered <head>. For example, if the API returned https://hcti.io/v1/image/EXAMPLE_ID, publish metadata like this:

<head>
  <meta property="og:image" content="https://hcti.io/v1/image/EXAMPLE_ID">
  <meta name="twitter:card" content="summary_large_image">
  <meta name="twitter:image" content="https://hcti.io/v1/image/EXAMPLE_ID">
</head>

Use the actual image URL returned by the API. The metadata must be present in the HTML source served to crawlers. Do not rely on client-side JavaScript to insert it after page load. If a theme or SEO plugin already emits image tags, update its value or use its supported setting or filter; duplicate, competing og:image values can lead crawlers to choose a different image than you expect.

For a site-wide config, the official setup guide uses a path-based URL. Replace DOMAIN_ID with the ID assigned to your config and keep the path aligned with the page route:

<meta property="og:image" content="https://hcti.io/v1/og/DOMAIN_ID/articles/my-post">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:image" content="https://hcti.io/v1/og/DOMAIN_ID/articles/my-post">

The config is associated with the exact public HTTPS origin you administer. HCTI uses the image URL path to request the corresponding page, read metadata, and screenshot the page or render a configured template. Page metadata such as title, description, and Open Graph values can map to template variables. Query strings are ignored for this path mapping, so give distinct pages distinct paths.

4. Set dimensions and platform behavior

A practical general-purpose card is 1200 × 630 pixels. HTML/CSS to Image’s social-card guide lists recommendations of 1200 × 600 for Twitter, 1200 × 630 for Facebook and Slack, and 1200 × 627 for LinkedIn. Platform behavior can change, so treat these as the service’s documented recommendations rather than immutable platform standards. See the [social card dimensions guide](https://htmlcsstoimage.com/use-cases/social-media-card/).

For an OG Image Config, the documented approaches are to use one image everywhere, render once and adapt it to platform bounds, or create a separate render for each recognized viewport. One image is simplest; adaptation can preserve the image without stretching but may leave unused space; separate renders provide more control for responsive layouts and use multiple renders.

For direct HTML snippets, the output is automatically cropped to the outermost HTML element, and HTML/CSS images render at 2× by default. Thus, a 400-pixel CSS width can produce an 800-pixel output. Distinguish layout dimensions in CSS from the final returned pixel dimensions when diagnosing an unexpectedly large image. The [dimensions documentation](https://docs.htmlcsstoimage.com/guides/dimensions/) describes this behavior.

5. Keep credentials and signed URLs safe

The standard POST API should be called from a server or trusted build environment because HTTP Basic credentials must remain secret. The vendor documentation says, “Treat your API Key like a password.” Never ship the API key in frontend JavaScript, a public mobile app bundle, or HTML source.

If a browser needs a URL it can load directly, HTML/CSS to Image documents a signed URL flow: generate an HMAC SHA256 signature on your server using the secret API key, then provide the signed URL. The signature can be public, but the signing key cannot. Anyone holding a signed URL can request the image it authorizes, so only sign public-safe inputs and permissions. See the [signed URL guide](https://docs.htmlcsstoimage.com/guides/signed-urls/).

An OG Image Config provides public image URLs without exposing an API key or generating a signature per page. Choose it when the origin/path mapping fits your site; use direct API rendering or signed URLs when an application needs arbitrary values at request time.

6. Verify a generated card before publishing

  1. Publish the page and inspect View Source. Confirm the final server-rendered head has the intended single og:image, plus the expected Twitter card tags.
  2. Open the image URL directly and confirm it returns the expected image.
  3. Use the HTML/CSS to Image Social Card Previewer to inspect metadata and platform shapes.
  4. If the generated image is current but a social app shows an old card, check that platform’s preview cache and use its inspection or rescrape tool where available.

These are documentation-based verification steps; they do not imply that a live request or crawler was tested for this article.

7. Fix stale previews and common errors

Symptom Likely cause What to do
401 or 403 response Wrong API ID/key, malformed Basic credentials, or credentials omitted. Check the server-side environment values and Basic auth format. Rotate a credential if it was exposed.
Request succeeds but no usable image Input HTML/URL is invalid, inaccessible, or the response was treated as an image when it is JSON. Check the response body and returned URL. For URL input, ensure the page is public; for HTML input, validate the markup and CSS.
Image looks clipped or has unexpected size The outermost element controls crop, and output pixels can be 2× CSS dimensions. Set explicit dimensions on the outer element and account for the 2× output behavior.
Social crawler sees no image Metadata is missing from server-rendered HTML, uses a relative URL, or points to a private image. Serve an absolute public URL in the initial head response and check the published source.
Wrong image chosen An SEO plugin or theme emits another image tag. Update the existing metadata source and remove conflicting values rather than appending another tag.
Social app still shows the old card HCTI source metadata, HCTI rendered image, and the social platform each have a cache. Inspect source cache headers, refresh the config when eligible, then ask the platform to rescrape or inspect the URL.
Changing a query parameter has no effect OG Image Config maps paths and ignores query strings. Use a unique page path. For changed content at the same path, follow the documented hcti:content_version approach.

For config caching, the documentation gives a 24-hour default refresh interval, with plan-dependent minimums. Source Cache-Control freshness headers or Expires can extend the next metadata check. The first request after metadata becomes stale may return the existing image while a background refresh runs; a later request can reflect the changed input. Increment hcti:content_version as a monotonically increasing integer when pixels change but extracted metadata does not. Adding a random query parameter does not bust this cache. See the [caching guide](https://docs.htmlcsstoimage.com/guides/caching/).

8. Performance, reliability, and cost considerations

  • Render during the right part of your pipeline. If card data is known at build or publish time, generate then and persist the returned URL. For request-time personalization, render server-side and avoid exposing credentials.
  • Reuse stable outputs. Repeatedly rendering unchanged HTML wastes work in your own pipeline. Keep a mapping from content version to returned image URL or use the config’s documented refresh behavior.
  • Make freshness explicit. Plan for metadata and social platform caches. A successful image refresh does not force every platform to discard an old preview immediately.
  • Check dimensions before delivery. A 2× output can affect file size and downstream display assumptions; optimize the layout and choose the required output format based on the consumers.
  • Budget from current plan terms. The cited research confirms the API workflows and cache behavior but does not establish current HTML/CSS to Image pricing, quotas, or render latency. Check the provider’s current account and pricing pages for those values rather than assuming a rate.

Or skip the browser setup

If your task is capturing an existing page rather than designing a custom HTML card, [ScreenshotNeo](https://screenshotneo.com) can return a PNG, JPEG, WebP, or PDF with one GET request. Its API can capture a page URL, while HTML/CSS to Image remains the direct fit above when you need to submit custom card markup.

cURL:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

Python:

import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)

Node.js:

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

See the [ScreenshotNeo API docs](https://screenshotneo.com/docs/) for request options. ScreenshotNeo removes cookie banners, popups, and chat widgets before the shot; bot checks, blank pages, and failed loads are never billed; its MCP server lets AI agents take screenshots; and 1,000 screenshots a month are free with no card, with paid plans starting at $5 for 3,000. [Create a free ScreenshotNeo account](https://screenshotneo.com/account/sign-up/).

FAQ

Can I use HTMLCSStoImage to capture an existing webpage?

Yes. Send a public fully qualified URL in the API’s url field. For a custom-designed card, send HTML and optional CSS instead.

Should I use one image URL for Open Graph and Twitter?

The documented guide shows the same generated URL in og:image and twitter:image, with twitter:card set to summary_large_image. Use separate renders if your design needs different platform dimensions.

Will refreshing the HCTI image immediately update every social preview?

No. The social platform can retain its own preview cache after the source and generated image have changed. Inspect or rescrape the page through the platform’s available tool.

Can a query string version my OG Image Config URL?

The config guide says query strings are ignored. Use distinct paths for distinct pages; for changed content at one path, use the documented integer content version metadata.