Book a call

How-to

How to turn a blog post into LinkedIn and Instagram carousels in 2 hours

A LinkedIn carousel is a PDF and an Instagram carousel is a folder of JPEGs, so both come out of one Pillow script instead of a design tool. Here is the two hour build, the slide contract that makes every later post a fill-in-the-blanks job, and the platform limits that catch people.

By 7 min read
Contents
  1. What a carousel actually is on each platform
  2. The arithmetic before you build
  3. The slide contract
  4. The build, about 2 hours
  5. Post by hand or post through the API
  6. What breaks
  7. Limitations
  8. My take

A LinkedIn carousel is a PDF. An Instagram carousel is a folder of JPEGs. Neither one needs a design tool, and both can come out of the same eight lines of text you already wrote for the blog post.

That is the part most carousel advice skips. It treats the carousel as a design problem. It is a file format problem with eight sentences attached.

I run a content engine that publishes 40+ posts a month across 14 platforms, so I care a lot about which part of that is typing and which part is rendering. The rendering part is a script you write once.

LinkedIn has no carousel object. It has a document post: you upload a file and the feed paginates it. LinkedIn's help page lists PPT, PPTX, DOC, DOCX and PDF, caps the upload at 100MB and 300 pages, and recommends converting to PDF for the highest quality upload. It also says you can edit the description after posting but not the document itself. A typo on slide one means delete and repost.

Instagram is the other shape. A carousel is a set of separate images. Posting by hand in the app, you can share up to 20 photos and videos as a single feed post.

So: one script, two outputs. A single PDF for LinkedIn, numbered JPEGs for Instagram, both fed by the same text.

The arithmetic before you build

Use your own numbers, not mine. Take M, the minutes a carousel currently costs you in a design tool, and W, the posts you publish a week. You are spending M x W x 52 minutes a year on layout.

The script does not touch the writing. You still have to decide what goes on eight slides. It removes the layout only. If layout is most of M for you, two hours pays back. If you were never making carousels at all, the honest read is that the script is the cheap part and the habit is the expensive part, and you should skip this post until you are publishing weekly.

The slide contract

Decide the shape first and never change it. One file, slides.json:

[
  {"kind": "hook", "title": "Your carousel is a PDF"},
  {"kind": "body", "title": "LinkedIn", "text": "No carousel object. A document post."},
  {"kind": "cta",  "title": "Full write-up", "text": "tarunsai.com/blog"}
]

Eight entries. Slide one is the hook, three to eight words, no body. Slides two to seven carry one idea each. Slide eight is the call to action. One idea per slide is the only design rule that matters, because the reader is swiping on a phone and will never go back to re-read slide three.

Once the contract is fixed, every later post is: write eight lines, run one command.

The build, about 2 hours

These are my estimates from the documented steps, not stopwatch readings.

Write the first eight slides by hand (about 25 minutes)

Do this once, manually, before you automate anything. Open your blog post, pull the eight strongest single sentences, and cut each one until it survives on its own. If you cannot fill eight slides from the post, the post was thin and the carousel will not save it.

Set the canvas and the fonts (about 15 minutes)

Install Pillow. Pick two TrueType files you have a licence for, one heavy for titles and one regular for body, and load them with ImageFont.truetype at a fixed size per role. The commonly recommended Instagram carousel frame is 1080 by 1350 pixels, portrait. Use the same canvas for both platforms so there is only one renderer.

Write the renderer (about 35 minutes)

This is the only real work. Per the Pillow ImageDraw reference, you draw with text and multiline_text, and you measure with textbbox and multiline_textbbox. The align argument takes left, center, right or justify, and the anchor argument (which decides where your xy coordinate sits relative to the text) is ignored for anything that is not a TrueType font.

The auto-fit loop is the whole trick. Wrap the string with textwrap at a guessed character width, measure the wrapped block with multiline_textbbox, and if the box is taller than the text area you reserved, drop the font size by two points and measure again. Set a floor. If it still does not fit at the floor, raise an error instead of drawing. A slide that overflows silently is worse than a build that fails.

Export the LinkedIn PDF (about 10 minutes)

Render all eight slides as RGB images, then save the first one with save_all=True and append_images set to the other seven. Pillow's PDF writer puts each frame on its own page, and the resolution or dpi argument, together with the pixel count, decides the physical page size. That file is your document post.

Export the Instagram JPEGs (about 10 minutes)

Same images, written out as 01.jpg through 08.jpg. Zero-pad the numbers so the phone upload picker keeps the order.

Wire the contract into the post pipeline (about 15 minutes)

One command that takes a post slug, reads slides.json next to the markdown, and writes both outputs into a folder named after the slug. No arguments to remember.

Post both once by hand and look at them on a phone (about 10 minutes)

Not on your monitor. On a phone, at arm's length, at the size the reader sees.

Total: about 2 hours.

Post by hand or post through the API

By hand Through Meta's API
Slides per Instagram carousel up to 20 10
Image format your choice JPEG only
Where files live your phone a public URL Meta can fetch
Cropping you choose per post every slide cropped to match slide one
Setup none professional account, a Meta app, app review
Worth it when you publish a few times a week you are running an unattended queue

Every row on the right comes from Meta's content publishing docs. My opinion: at one post a week, uploading by hand is faster than getting an app approved, and you can stop reading at the JPEG export step.

What breaks

  • Long unbroken words overflow the text box. Measure with textbbox before you draw, always.
  • On the API, slide one sets the crop for every other slide, and the default is 1:1. A portrait slide two will get cut.
  • The API accepts JPEG only. Pillow will happily hand you a PNG and Meta will reject it.
  • A media container expires if it is not published within 24 hours.
  • The same Meta page states two different rate limits: the carousel section says 50 published posts in a 24 hour period, the requirements section says 100 API-published posts in a 24 hour moving window. Query the content_publishing_limit endpoint rather than trusting either.
  • A font file only draws the glyphs it contains. Keep slide text to plain characters unless you have checked your font.
  • Your LinkedIn document cannot be edited after posting. Only the description can.
  • Some Pillow builds ship without a JPEG encoder (Homebrew's Python 3.14 build on a Mac is one). Then save_all for PDF fails with a KeyError: 'JPEG'. Either install a Pillow wheel with libjpeg, or write the PDF yourself: one page per image, raw RGB pixels, zlib-compressed, about 40 lines. The renderer behind this site does the second.

Limitations

I have written this from the platform documentation and the Pillow reference, not from a measured run, so treat the step times as estimates rather than benchmarks. I have not tested the Meta publishing flow end to end, and app review timelines are outside anything I can promise.

The bigger limitation is not technical. A script gives you a consistent, ugly-in-a-good-way carousel. It does not give you a designed one. If your brand depends on illustration or photography, this replaces the layout step and nothing else.

And it does not make the post better. Eight weak sentences rendered at 1080 pixels wide are still eight weak sentences.

My take

The two hours are worth it for one reason: it removes the decision. When a carousel costs a design session, you skip it on a busy week. When it costs eight lines in a JSON file, you do not. Consistency is the only thing carousels reliably buy you, and consistency is a cost problem before it is a taste problem.

If you want the same reasoning applied to other pipelines I have written up, the rest are at tarunsai.com/blog.

Next step: open your last post, write the eight sentences in a plain text file, and see whether you can fill all eight. That takes about 25 minutes and tells you whether the other 95 minutes are worth spending.

Questions people ask

Can I just export slides from Canva or Google Slides as a PDF instead?

Yes, and for a first carousel you should. LinkedIn accepts PPT, PPTX, DOC, DOCX and PDF up to 100MB and 300 pages, so a slide deck exported to PDF works as a document post with no code at all. The script earns its keep on the fifth carousel, not the first, because it removes the layout session rather than the writing.

Why do my Instagram slides look cropped when I schedule them but fine when I post by hand?

Because the publishing API crops every slide in a carousel to match the first image, defaulting to 1:1, per Meta's content publishing documentation. Posting by hand in the app lets you set the crop yourself. If you schedule through a tool that uses the API, render every slide at the same aspect ratio and make slide one the ratio you actually want.

How many slides should a carousel be?

Eight is a working default, not a rule from any platform. LinkedIn allows up to 300 pages and the Instagram app allows up to 20 images, so the cap is never the constraint. Eight fits one idea per slide with a hook and a call to action, and it is short enough that you will actually write it every week.

Have a process like this leaking time or leads?Thirty minutes, no deck, and you leave with a plan.

Book a call