Documentation

API Reference

Everything you need to integrate SnapForge into your application.

Authentication

Authenticate by passing your API key as a Bearer token in the Authorization header, or as a key query parameter.

bash
# Via header (recommended)
curl https://api.snapforgehq.com/v1/og?template=blog-post&title=Hello \
  -H "Authorization: Bearer sf_live_your_key_here"

# Via query parameter (useful for meta tags)
<meta property="og:image"
  content="https://api.snapforgehq.com/v1/og?template=blog-post&title=Hello&key=sf_live_your_key_here" />

Endpoints

Base URL: https://api.snapforgehq.com

GET/v1/og

Generate an OG image via query parameters. Ideal for use directly in HTML meta tags.

Parameters

NameTypeRequiredDescription
templatestringYesTemplate ID: blog-post, social, minimal, product-hunt, or github-card
titlestringYesMain title text
subtitlestringNoSubtitle text (blog-post template)
authorstringNoAuthor name (blog-post template)
descriptionstringNoDescription text (social template)
brandstringNoBrand name (social template)
colorstringNoBackground color hex (minimal template)
text_colorstringNoText color hex (minimal template)
taglinestringNoProduct tagline (product-hunt template)
categorystringNoCategory label (product-hunt template)
upvotesstringNoUpvote count display (product-hunt template)
namestringNoRepo name (github-card template)
starsstringNoStar count (github-card template)
languagestringNoProgramming language (github-card template)
language_colorstringNoLanguage dot color hex (github-card template)
widthnumberNoImage width in pixels (default: 1200)
heightnumberNoImage height in pixels (default: 630)
formatstringNoOutput format: png (default), jpeg, webp
jsonbooleanNoSet to true to get JSON with a link to the image instead of the image itself
keystringYesYour API key (alternative to Authorization header)
POST/v1/og

Generate an OG image via JSON body.

Request Body

json
{
  "template": "blog-post",
  "params": {
    "title": "My Blog Post",
    "subtitle": "A deep dive into...",
    "author": "Jane Doe"
  },
  "width": 1200,
  "height": 630,
  "format": "png"
}
POST/v1/screenshot

Capture a screenshot of a public URL or raw HTML as PNG, JPEG or WebP. Requires Starter plan or higher. Each screenshot counts as one generation.

Request Body

json
{
  "url": "https://example.com",
  "width": 1280,
  "height": 720,
  "full_page": false,
  "selector": "#main",
  "format": "png",
  "delay": 0
}
GET/v1/usage

Get your current usage stats for the billing period.

Response

json
{
  "used": 450,
  "limit": 5000,
  "month": "2026-03",
  "planId": "starter"
}
GET/v1/templates

List the built-in templates and your custom templates with their parameters. Custom templates come 100 at a time: pass ?offset= with the returned next_offset to get the next page (null on the last page).

Code Examples

JavaScript (fetch)

javascript
const response = await fetch(
  "https://api.snapforgehq.com/v1/og",
  {
    method: "POST",
    headers: {
      "Authorization": "Bearer sf_live_your_key_here",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      template: "blog-post",
      params: {
        title: "My Blog Post",
        subtitle: "Written with love",
        author: "Jane Doe",
      },
    }),
  }
);

const imageBlob = await response.blob();
// Use the blob as needed (save, display, upload, etc.)

Python

python
import requests

response = requests.post(
    "https://api.snapforgehq.com/v1/og",
    headers={"Authorization": "Bearer sf_live_your_key_here"},
    json={
        "template": "blog-post",
        "params": {
            "title": "My Blog Post",
            "subtitle": "Written with love",
            "author": "Jane Doe",
        },
    },
)

with open("og-image.png", "wb") as f:
    f.write(response.content)

cURL

bash
curl -X POST "https://api.snapforgehq.com/v1/og" \
  -H "Authorization: Bearer sf_live_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{"template":"blog-post","params":{"title":"Hello World"}}' \
  -o og-image.png

Custom templates

Pro and Business plans can author their own OG templates from the dashboard. A saved template gets an id of the form tpl_<id>, which is passed as the template parameter to /v1/og exactly like a built-in template slug.

Placeholder syntax

Write placeholders as {{name}} anywhere in your template HTML. A valid placeholder name matches ^[a-z][a-z0-9_]{0,49}$: lowercase letters, digits and underscores, starting with a letter, 1-50 characters. Anything else (uppercase, hyphens, leading digits, or internal whitespace like {{ title }}) is left as literal text instead of being treated as a placeholder.

At render time each placeholder is replaced in this order: the request's params, then the template's saved default_params, then an empty string if neither supplies a value. Substituted values are HTML-escaped, so a value can never inject markup or break out of an attribute.

Allowed image sources

Every image source — an <img src> or an inline-style url(...) — must be a data: URI or a public https:// URL. Anything else is rejected at save time and again at render time.

Limits

FieldLimit
name1-60 characters
html20,000 bytes or less (UTF-8)
default paramsup to 20, keys up to 50 characters, values up to 500 characters
templates per accountPro: 10 · Business: unlimited

Example

bash
curl "https://api.snapforgehq.com/v1/og?template=tpl_3f9a1c7e5b2d4a8f9e0c1b2a3d4e5f60&product=Acme&title=New+release&version=v2.4.0&date=Sep+27%2C+2026" \
  -H "Authorization: Bearer sf_live_your_key_here" \
  -o og-image.png

Error Codes

StatusErrorDescription
400bad_requestInvalid parameters. Check the error message for details.
401unauthorizedMissing or invalid API key.
400blocked_urlScreenshots of private or internal addresses are not allowed.
403plan_requiredThe Screenshot API needs the Starter plan or higher.
422capture_failedThe page could not be captured: it did not load, or the selector was not found.
429too_many_requestsMore than 10 screenshots in a minute. Retry after the Retry-After header.
429rate_limit_exceededMonthly generation limit reached. Upgrade your plan.
500generation_failedImage generation failed. Contact support if this persists.
503busyScreenshot capacity is in use. Retry after the Retry-After header.
404not_foundThe custom template does not exist, isn't yours, or the id is malformed.
403template_limit_reachedYour plan's custom template limit is reached. Delete one or upgrade to Business.
400invalid_templateThe template HTML could not be parsed or rendered.
400blocked_image_sourceAn image source is not a data: URI or a public https:// URL.

Rate Limits

Rate limits are based on your plan's monthly generation quota. Cache hits do not count against your quota. Check the X-RateLimit-Remaining and X-RateLimit-Limit response headers.

PlanMonthly LimitAt the limit
Free50Stops (429)
Starter500Stops until reset (429), no overage charges
Pro5,000Stops until reset (429), no overage charges
Business25,000Stops until reset (429), no overage charges

Limits reset on the first of each month (UTC). Cached images never count. If a payment fails, your paid limits stay in place for 7 days while the charge is retried. After that the account uses Free limits until the payment goes through.