Public URLs
GET /i/{keyId}/{sig} (see Signing)
needs your app to compute an HMAC over the request. Some sites have no
server or build step that can do that — a Hugo or Jekyll site built on
GitHub Pages, a Ghost theme, a Webflow page. For those, mint a
public key in the dashboard instead: it accepts
unsigned requests, but only for the templates you explicitly allow.
The URL shape
GET https://ogmake.com/p/{keyId}/{template}?title=...&description=... {keyId} is the public key's id (starts with
k_), {template} is one of the
key's allow-listed templates (e.g. blog), and every other query
param is that template's own field — same params as the signed route, just no
sig. A request for a template outside the key's allow-list, or
against a key that isn't marked public, is refused — like every other refusal on this route
(see below), that's a 200 fallback image, never an error status.
Create a public key
Sign in at /login and open the dashboard. Click New key,
choose Public key, select which templates the key may render, then
Create key. The key id (not the bearer secret) is what goes in the URL — it
is safe to commit into a public site's source. If the key uses auto mode (/p/YOUR_KEY/auto), set
Allowed sites in the key's Settings to protect your renders.
Hugo
In a page or partial template, e.g. layouts/partials/opengraph.html:
<meta property="og:image" content="https://ogmake.com/p/{your-public-key}/basic?title={{ .Title | urlquery }}&description={{ .Summary | urlquery }}" /> Jekyll (GitHub Pages)
In _layouts/default.html:
<meta property="og:image" content="https://ogmake.com/p/{your-public-key}/basic?title={{ page.title | url_encode }}&description={{ page.excerpt | strip_html | url_encode }}" /> One tag for every page: /auto
Don't want to pass a title and description per page? Point the key at the page itself:
GET /p/{keyId}/auto?url=<page url>. ogmake reads the page's title,
description and site name and renders your key's default template (its first allowed
template, or one you pick with &template=). Put one tag in your
layout and every page gets its own card:
<meta property="og:image" content="https://ogmake.com/p/YOUR_KEY/auto?url={{page.url}}"> Replace {{page.url}} with your generator's absolute, URL-encoded page address:
<!-- Hugo -->
<meta property="og:image" content="https://ogmake.com/p/{your-public-key}/auto?url={{ .Permalink | urlquery }}">
<!-- Jekyll -->
<meta property="og:image" content="https://ogmake.com/p/{your-public-key}/auto?url={{ page.url | absolute_url | url_encode }}">
<!-- Ghost (default.hbs) -->
<meta property="og:image" content="https://ogmake.com/p/{your-public-key}/auto?url={{url absolute="true"}}"> Webflow: in a page's Open Graph settings, paste
https://ogmake.com/p/{your-public-key}/auto?url=https://yoursite.com/that-page
as the image URL.
Set your site under the key's allowed domains in the dashboard: an auto URL only works for pages on those domains, so nobody else can spend your renders, and social crawlers (which send no Referer) can fetch the image. A key with no allowed domains only answers requests whose Referer is the page itself. Pages with no title get a card from your site name; a repeat request for the same page is served from cache and costs nothing.
Behaves like a normal render, minus the signature
- Renders on first request, served from cache after that — same as the signed route.
- Counts against your account's monthly quota, same caps — but unlike the signed route, a public URL never gets the over-quota watermark degrade. Once your account is over its monthly cap, public URLs serve only an existing cached image or the fallback image; they never render, watermarked or not.
- Rate-limited per key and per requester IP.
- Bounded by a separate per-account public-render budget, on top of your monthly quota: up to
one fifth of your monthly sub-cap in
cache-missrenders a day and up to your whole monthly cap in cache-miss renders a month, whichever is reached first — Free (100/mo) gets 20/day and 100/mo, Starter (2,000/mo) gets 400/day and 2,000/mo, Scale (10,000/mo) gets 2,000/day and 10,000/mo, Agency (30,000/mo) gets 6,000/day and 30,000/mo, Pro (100,000/mo) gets 20,000/day and 100,000/mo, Business (300,000/mo) gets 60,000/day and 300,000/mo. A cache hit is always free and never counts against either budget. The budget is per account, not per key — minting more public keys doesn't multiply it. - The response is only ever the image (or a fallback image on a render failure) — it never reveals your account, or the key's bearer-secret half.
- An ordinary (non-public) key's behaviour is completely unchanged by this feature.
Anyone who copies this URL can spend your quota — it's unsigned on purpose. A per-account daily render budget (one fifth of your monthly cap) limits how fast a copied or replayed URL can run up renders, but a public URL can still use your whole monthly cap over time; revoke the key's public access from the dashboard at any time without touching its signed use.
Debugging a public URL that isn't working
Every refusal on this route is still a 200 image — a wrong template, a
mistyped key id, a missing required field, or a render your account can't currently afford all look
identical in the browser or the crawler's preview. Check the response headers instead
(e.g. curl -I the URL): a failed request carries an
x-ogmake-error header set to one of:
forbidden— the key id doesn't exist, is revoked, isn't marked public, or the requested template isn't in that key's allow-list.allowed_sites_required— the page's host isn't allowed (auto mode and URL templates only). With Allowed sites set, the page host must be on the list; without any, only requests whose Referer host matches the page are allowed. Add the site in the key's Settings > Allowed sites.invalid_request— the request itself is malformed (a bad or missing param). Also carries anx-ogmake-error-detailheader naming the field and why.rate_limited— too many requests right now, from this key or this requester's IP; retry shortly.unavailable— the account is over its monthly cap, over its public-URL daily/monthly render budget, or a render attempt failed transiently. These are deliberately indistinguishable from each other in the response, since this route is unauthenticated and anyone holding the URL can ask — see your account's real budget/quota state on the dashboard instead.
A successful render never carries x-ogmake-error at all.