Every post on a blog needs the same small set of images: the OG card for link previews, a square for social feeds, a story format for the vertical surfaces. The usual ways to make them are a design tool and a folder of exports that drift out of date, or an editor-based service where the template lives in someone else's dashboard and the rendering is metered per image, per format, and per scale.
SpecCard is the API we built for the pipeline-shaped version of that job. The templates are named, versioned code. The request is a JSON spec: template name, text fields, palette or color overrides. The response is finished images, and the same spec always renders to the same bytes.
What it does
- A curated catalog of 10 templates in four families: editorial (headline OG cards, a changelog card, an event card), stat cards, quote cards in serif and sans, and product cards
- Single renders: one template at one size, returned as the image bytes, JPEG by default and PNG on request
- Content packs: one spec rendered at the template's full channel size set (2 to 3 sizes) in a single call, and the pack counts as one request
- Batch: up to 10 single renders in one call, each item independent, so one bad spec returns its own error and never fails the batch
- Named palettes (3 per template, from 9 canonical colorways) with per-slot color overrides, so brand colors travel in the request instead of living in a dashboard
- A self-describing catalog: the API publishes every template's full field schema, limits, palettes, sizes, and a complete working example spec
Sizes are named and fixed: og at 1200x630, square at 1080x1080, story at 1080x1920, and post at 1200x1200. Every template declares 2 to 3 of them, and the pack is always the full set.
A taste
One spec, rendered at every size its template ships:
curl -X POST "https://speccard.p.rapidapi.com/v1/render/pack" \ -H "X-RapidAPI-Key: YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{ "template": "headline-og", "data": { "brand": "Northstar Ops", "category": "Operations", "title": "The Hidden Cost of Almost-Right Dates", "subtitle": "Why deadline software fails at the exact moment a customer needs certainty.", "author": "Maya Chen", "date": "Nov 18, 2026", "read_time": "7 min read", "domain": "northstarops.com" } }' The pack endpoint answers with one envelope, one entry per size, each carrying the base64 image plus its byte count and SHA-256:
{ "catalog_version": "catalog.1", "template": "headline-og", "template_version": 1, "format": "jpeg", "kind": "pack", "images": [ {"size": "og", "width": 1200, "height": 630, "format": "jpeg", "bytes": 71234, "sha256": "<hex>", "data": "<base64>"}, {"size": "square", "width": 1080, "height": 1080, "format": "jpeg", "bytes": 84510, "sha256": "<hex>", "data": "<base64>"} ] } (That example is abridged to two entries; a headline-og pack always carries all three of its sizes, and the byte counts shown are illustrative.) The hash matters more than it might look: because a pack image is byte-identical to the same spec rendered on its own, a build pipeline can hash a card in CI, diff it in review, and trust a backfill of ten thousand archive posts to produce exactly the cards the templates describe.
How it is built
FastAPI on the front, Pillow doing the rendering, and nothing else in the request path. There is no database, no stored state, and no network call at runtime: the fonts (the Noto families, bundled under the SIL Open Font License 1.1 and loaded by explicit file path, never by system fallback) travel with the service, so the deploy artifact renders identically anywhere.
Determinism is a contract, not a hope. The render path reads no clock, no randomness, and no environment. Each template draws at 2x supersampling and downsamples with Lanczos, and the encoder settings are pinned constants. A shipped template version is never edited in place for a change that alters output bytes; a visual change ships as the next version, the old renderer stays selectable by pinning template_version, and pack, batch, and error responses carry a catalog_version stamp naming the catalog that produced them. The prototype that proved this design out rendered the same spec twice and produced identical SHA-256 hashes, and ran a 100-render batch in one process peaking at 78.7 MB of memory, which is why batch-shaped rendering is a normal case here instead of a top-tier feature.
Text fitting follows a published policy instead of vibes. Text wraps, an unbroken word wider than its box is hard-split, the size steps down until the text fits its line budget, and only then is a final line ellipsized with three ASCII dots. Text never overflows its box or collides with an anchored element, and a field over its published length limit is rejected with a structured error naming the field and the limit, never silently truncated.
The error vocabulary is a closed set of codes in one shape ({detail, code, catalog_version}), and registry errors name the offending value and the valid options. An unknown field inside data is rejected rather than ignored: on an API whose contract is byte-exact rendering, a misspelled field silently falling back to a default would be a quiet lie in image form.
What v1 deliberately leaves out
SpecCard v1 is narrow on purpose, and the narrowness is the product. There is no visual editor and no caller-defined layout; the catalog is 10 curated templates with defined fields. There are no caller-supplied photos, logos, or fonts, and no AI imagery; avatars render as initials, and product templates use their own flat illustration. If your workflow starts in a design tool, editor-based services are built for you and good at it. SpecCard is for the workflow that starts in a repository.
Text is Latin-script at launch. The supported repertoire covers the Latin blocks including Latin Extended and combining marks (Vietnamese renders fine), general punctuation, currency symbols, and arrows. Anything outside it, Cyrillic, CJK, Arabic, and emoji included, is rejected with a 422 unsupported_script error naming the field and the character. Rejection is the honest option: the alternatives were rendering placeholder boxes under a 200 status, or silently stripping characters out of the caller's words.
Dates, prices, and metrics in a spec are free text, printed exactly as given. The API does no date parsing, currency formatting, or number localization, and the listing does not pretend otherwise.
Pricing
Every endpoint is available on every plan, including the free tier. The catalog, single render, pack, and batch endpoints are all there from the start, with no template held back to force an upgrade. Upgrading simply gets you more requests per month: start free with 100 requests a month on a tier that renews monthly instead of expiring like a trial, move up to Pro at $9/month for 2,000, Ultra at $19/month for 5,000, or Mega at $49/month for 15,000. Paid plans are soft limits with small overages, so a backfill that runs long does not hard-stop halfway through an archive. And remember what a request can carry: a pack renders one spec at every channel size its template ships, for one request. You pay for volume, not for access, and not per format.
The listing is here: https://rapidapi.com/darksundarker/api/speccard
Feedback very welcome, especially the "my pipeline needs a template shape you do not have" kind. The catalog is curated and versioned, so the templates it grows will come from exactly that.