June 08, 2026
my blog posts itself to instagram now
a build log, and a small confession
so, real talk: writing the blog post was never the hard part. the hard part was everything after. taking a screenshot, opening instagram, thinking of a caption that doesn't sound like a press release, finding a picture that isn't just the same grey stock photo everyone else uses, picking hashtags, posting it at a time that isn't 2am. that whole tax is why i'd write five posts and share none of them.
so i built the tax away. now i type one line — ig post: <url> — and a few seconds later there's a finished instagram post sitting on my profile. caption in my voice, a custom-drawn square image, hashtags, the works. i don't touch a single part of it. this is the log of how, and honestly it fought me at every step.
the shape of it is four steps glued together. blog url goes in. a caption comes out of one end, an image comes out of another, the two get married and shoved onto instagram. simple to say. each of those four steps had a small knife waiting for me.
step one, the caption. it reads the whole post and writes a caption in the same voice the post is written in — casual, a person talking, not a brand, capped at 150 words. easy part. the gotcha is the link. instagram doesn't make links in captions tappable, at all, which i did not know and which makes the entire platform feel slightly hostile to anyone who writes anything. so every caption ends with the same little dance: "read the full thing → link in bio", then the short typeable url under it as a backup, then the hashtags. you work around the platform, you don't fight it.
step two is the image, and this is where it gets stupid. the images are made with openai's gpt-image-2. but here's the part i'm weirdly proud of — it doesn't draw from a blank page. every single post on my instagram is locked to ONE house style. a collage/scrapbook maximalism thing — big rounded display type, periwinkle-blue and dusty pink, cut-out objects, stickers, washi tape, halftone textures. brain-candy. i have one reference image, ref3.jpg, that defines that look forever.
so the trick is i don't ask it to "generate" an image. i hit the image edits endpoint instead of the generations one, and pass ref3.jpg in as an actual file (image[], multipart form upload, the whole thing). the instruction is: copy this reference's composition, palette and type treatment, but absolutely do not copy its subject — build the collage out of objects pulled from this specific blog post. so the look stays identical across every post and the contents are always brand new. the picture comes back as a base64 blob, which i decode and write out as a png. nothing about that is one function call. it's a curl with a dozen form fields and a pipe through base64.
and the model is sneaky. it drifts. on a sad or heavy post it gets ideas, ignores my actual headline, and hallucinates some generic "it's okay to not be okay / some days are hard" mental-health collage out of nowhere. so i had to learn to pin it down hard: state the exact headline verbatim, max 25 characters, and explicitly forbid it from inventing any other text — name the cliché phrases it loves and tell it NO, this headline is the only words allowed in the image. you're not prompting it so much as negotiating with something that keeps trying to wander off.
there are smaller rules baked in too. the mood shifts with the post — same style, same palette, but cheerful and sparkly for a fun post, desaturated and torn-paper-sad for a heavy one. if the post is about a real product, its logo gets drawn in its true bright brand colours so it pops as the one high-contrast hero, and everything else stays calm around it. and the collage props have to be fresh every time — if last post used sharks and unicorns, this one can't. one idea per image or it turns to mush.
step three, hosting, is the part where i cheated. i don't pay for an image host. the png just gets curled straight up to neocities — the same site you're reading this on — under /ig/<slug>.png. multipart upload, check the json came back success, and the script prints one thing: the public https url. my personal site is quietly moonlighting as a cdn for its own instagram and i think that's funny.
step four, actually posting. this goes through zernio, which is a remote server my claude talks to over the internet (an mcp server, if that means anything to you — it's just a standardised way for the ai to call out to a real service). i ask it for my instagram account id once, then fire posts_publish_now with the caption, the platform, and the neocities image url. the nice surprise: that endpoint takes a public url directly. no browser, no "upload your media first" handshake, no temp tokens. you hand it a link and it goes. it comes back with a post id and the thing is live. no preview, no "are you sure", it just exists now.
now the boring grown-up part, because someone always asks: what does this cost.
gpt-image-2 doesn't bill per image, it bills per output token, and that number scales hard with quality. "high" quality is roughly four times the tokens of "medium". so the script is pinned to medium on purpose, which keeps the headline text and the brand logos crisp while costing a fraction — call it a cent or two of image per post instead of four times that. across a real posting habit that gap is the whole budget.
and the actual discipline isn't the quality knob, it's this: generate once. the prompt is the only real lever, so the rule is get it right the first time instead of regenerating to nudge it — every regen is a fresh full charge, the model doesn't remember the last attempt, it just bills you again. no re-running "just to preview". the first hosted url is the deliverable, full stop. the reference image is tiny (about 275px) so it barely adds anything to the input cost, which is the only reason passing a file in is even affordable.
so what do i actually get for stacking up a base64 pipe, an edits endpoint, a stubborn image model, a moonlighting website and a remote posting server.
i get to not care. that's the whole prize and it's bigger than it sounds. the instagram was never the thing i wanted to do — it was the chore stapled to the thing i wanted to do, and the chore was winning. now the writing is the only part left that's mine. everything downstream of hitting publish just happens, off to the side, the way it should. posting went from a task to a side effect.
it's a few files and a shell script. but it took the most annoying part of having a blog and quietly deleted it, and that turned out to matter more than it has any right to.