yeetpostyeetpost

Generate on-brand image cards for every post from HTML

October 29, 2026

A post with an image gets looked at. A post with a different, hand-made image every time takes twenty minutes you do not have. The usual compromise is no image, or the same stock screenshot.

There is a third way: write the card as HTML once, fill in the words per post, and turn it into an image through an API call. This post shows how that works in yeetpost, with the limits stated, and how to make the cards follow your brand without anyone opening a design tool.

#One call: HTML in, image out

POST /images/render turns an HTML document into a jpeg or png and stores it as an image, the same as an upload. You get a mediaId back to attach to posts.

Start with the card as a file. Draw with HTML, CSS and inline SVG:

<body style="margin:0;background:#0f172a;color:#fff;font-family:'Inter',sans-serif">
  <div style="padding:80px;height:1040px;display:flex;flex-direction:column;justify-content:space-between">
    <p style="font-size:34px;color:#fb923c;margin:0">v2.4</p>
    <p style="font-size:96px;font-weight:800;line-height:1.05;margin:0">The scheduler is twice as fast.</p>
    <p style="font-size:32px;color:#94a3b8;margin:0">example.com/changelog</p>
  </div>
</body>

Send it. The jq call builds the JSON so you do not have to escape the HTML by hand:

jq -n --rawfile html card.html \
  '{html: $html, preset: "square", altText: "v2.4: the scheduler is twice as fast"}' \
| curl -X POST https://api.yeetpost.com/api/v2/images/render \
  -H "x-api-key: $YEETPOST_API_KEY" \
  -H "content-type: application/json" \
  -d @-

The answer:

{
  "mediaId": "9b1c2d3e-...",
  "contentType": "image/jpeg",
  "sizeBytes": 48213,
  "width": 1200,
  "height": 1200,
  "postId": null,
  "rendersUsed": 1,
  "rendersLimit": 300
}

Use the mediaId in a post:

curl -X POST https://api.yeetpost.com/api/v2/posts \
  -H "x-api-key: $YEETPOST_API_KEY" \
  -H "content-type: application/json" \
  -d '{
    "text": "v2.4 is live. the scheduler is twice as fast.",
    "connectionSlugs": ["linkedin", "bluesky"],
    "mediaIds": ["9b1c2d3e-..."]
  }'

If the post is already scheduled and waiting, skip that step: send postId in the render call and the image is appended to the post's current images, five at most.

#Sizes and format

Pick a size with preset:

PresetSize
square1200 x 1200
landscape1200 x 627
portrait1080 x 1350

Or pass width and height, 200 to 2000 each. Sizes are CSS pixels and the image is drawn at twice that density, so a 1200 wide card is a 2400 pixel image. The format is jpeg by default, or png. Mind the platform limits on the way out: Bluesky takes 1 MB per image, X 5 MB, and Telegram takes png and jpeg only. A photo-heavy png can be over Bluesky's limit, where the jpeg is not.

#What a card can and cannot do

The renderer is deliberately boxed in:

Charts and numbers are a good use. A big number, a bar drawn in SVG, a quote with a name under it all render well. A page that needs a script to lay itself out does not.

#Limits and what counts

Each account gets 300 renders per calendar month (UTC), and the response tells you how many are used. Past it, the call is 429 render_allowance_reached with the date it comes back. A render that fails on the renderer's side does not count. A timeout or an over-size result does, because it used the renderer's time. The call itself is limited to 20 a minute per key.

Errors worth handling:

CodeMeaningWhat to do
502 render_failedRenderer failureSafe to retry
503 renderer_busyTry again shortlyRetry in a few seconds
429 render_rate_limitedOver 30 renders a minute across keysSlow down
400 invalid_mediaA yeetpost-media: id is not yoursCheck the id

#Keeping cards on brand

A template you fill in by hand still drifts. The part that scales is storing the brand with the connection, so the agent or script that makes the card reads it instead of guessing.

yeetpost calls this the connection's look: a description in plain words, up to 5 example cards (HTML documents, the same kind POST /images/render takes) and an optional logo. A connection without its own look falls back to the account's.

curl -X PATCH https://api.yeetpost.com/api/v2/connections/$CONNECTION_ID \
  -H "x-api-key: $YEETPOST_API_KEY" \
  -H "content-type: application/json" \
  -d '{"look": {"description": "Dark navy, one orange accent, large type, logo bottom left.", "exampleCards": ["<html>...</html>"], "logoMediaId": "9b1c2d3e-..."}}'

The description is up to 4,000 characters. Each example card is 1 to 30,000 characters. The logo is an ordinary uploaded image, so a card shows it as yeetpost-media:<logoMediaId>.

If you do not have a look yet, POST /look/draft-from-url drafts one from your website: a description, two example cards, the logo and the facts it read. It saves nothing unless you pass apply, so you can read the draft first.

#Letting a post draw its own card

Two more routes, so you do not write the HTML at all:

A prompt that uses all of it:

Write a LinkedIn post about the v2.4 release in our voice, make an image card in our look, attach it and save the post as a draft.

The agent calls get_brand, writes the text, renders the card, creates the draft with the mediaId, and you review one draft instead of a text file and an image folder.

#A card for every post, automatically

If you want this without a person in the loop, a source can watch an RSS feed, a sitemap or a page and post what is new, in queue or draft mode. When the connection has a look with example cards and makeCard is not false, each new item also gets a square card in that look, which counts as one of your monthly renders. A card that cannot be drawn is left out and the text still posts. Use draft mode first and look at what it produces before you put it on a queue.

#Checklist

  1. Write one card as HTML and render it at the preset your platforms use.
  2. Store the brand as a look, with 2 to 5 example cards and a logo.
  3. Add alt text on every render. It is sent to the platforms that take it.
  4. Watch rendersUsed against the 300 monthly limit.
  5. Keep file sizes under the strictest platform you post to.

The docs have the full image reference, and the platform pages, such as LinkedIn and Bluesky, list each platform's image limits.