Skip to content

Text Validation

The /validate endpoint runs Pretext’s text measurement and line-breaking logic without drawing anything to a canvas. It tells you exactly how many lines your text will occupy, and whether it overflows the available space. Validation is free, unlimited, and not metered against your plan’s render quota.

Consider a CMS where editors write article titles of unpredictable length. Without validation, a long title silently gets truncated with in the generated OG card. By validating on blur (or before saving), you can surface a warning to the editor in real time.

Similarly, if you’re programmatically generating cards from database content, validation lets you fall back to a smaller font size or truncate the description yourself before committing to a render.

Terminal window
curl -X POST https://og-engine.com/validate \
-H "Content-Type: application/json" \
-d '{
"format": "og",
"title": "Some Long Headline That Might Not Fit"
}'

The Authorization header is optional — if provided, your API key is validated but the call is never metered. The response is always JSON:

{
"fits": true,
"title": { "lines": 2, "maxLines": 3, "overflow": false },
"description": { "lines": 0, "maxLines": 4, "overflow": false },
"computeTimeMs": 0.14
}

fits is true only when both title and description are within their limits.

Terminal window
curl -X POST https://og-engine.com/validate \
-H "Content-Type: application/json" \
-d '{
"format": "og",
"title": "The Complete Guide to Building High-Performance APIs with Bun",
"description": "A comprehensive walkthrough covering routing, middleware, database access, caching, authentication, rate limiting, and deployment to production on Fly.io."
}'
{
"fits": false,
"title": { "lines": 3, "maxLines": 3, "overflow": false },
"description": { "lines": 5, "maxLines": 4, "overflow": true },
"computeTimeMs": 0.21
}

Here the title fits (3 lines, max 3), but the description overflows (5 lines, max 4). The fits top-level field is false because at least one block overflowed.

You can override the default max-lines limits to match your specific template configuration:

Terminal window
curl -X POST https://og-engine.com/validate \
-H "Content-Type: application/json" \
-d '{
"format": "og",
"title": "My Headline",
"description": "My description text.",
"maxTitleLines": 2,
"maxDescLines": 3
}'

This is useful when a template visually only has room for 2 title lines even though the format technically allows 3.

Validating with a different font or size gives you accurate results for non-default configurations. Text measurement is font-metric-dependent — the same string in Playfair Display at 52px will wrap differently than Inter at 48px.

Terminal window
curl -X POST https://og-engine.com/validate \
-H "Content-Type: application/json" \
-d '{
"format": "og",
"title": "The Pragmatic Engineer Guide to Distributed Systems",
"font": "Playfair Display",
"titleSize": 52,
"maxTitleLines": 3
}'

Always pass the same font and titleSize/descSize values you intend to use in /render. Mismatched sizes will give inaccurate overflow results.

Here’s a React example that validates on input blur before allowing the user to save:

import { useState } from 'react'
function OGTitleField() {
const [title, setTitle] = useState('')
const [warning, setWarning] = useState<string | null>(null)
async function handleBlur() {
if (!title) return
const res = await fetch('https://og-engine.com/validate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ format: 'og', title }),
})
const { fits, title: t } = await res.json()
if (!fits) {
setWarning(`Title is too long — wraps to ${t.lines} lines (max ${t.maxLines}). Try shortening it.`)
} else {
setWarning(null)
}
}
return (
<div>
<input
type="text"
value={title}
onChange={(e) => setTitle(e.target.value)}
onBlur={handleBlur}
placeholder="Article title"
/>
{warning && <p style={{ color: 'orange' }}>{warning}</p>}
</div>
)
}

If you want text to always fit, you can use /validate in a binary search over font sizes:

async function findMaxFontSize(title: string, format: string): Promise<number> {
let lo = 28, hi = 72
while (lo < hi) {
const mid = Math.floor((lo + hi + 1) / 2)
const res = await fetch('https://og-engine.com/validate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ format, title, titleSize: mid, maxTitleLines: 3 }),
})
const { fits } = await res.json()
if (fits) lo = mid
else hi = mid - 1
}
return lo
}

Because /validate is free and ultra-fast (~0.1–0.3ms), you can run 5–6 iterations without worrying about cost or latency.

0.0ms|8500x