I had 39 products packaged and ready to ship. I uploaded the first one to Gumroad, looked at the listing, and closed the tab.
The problem wasn't the content. It was that the listing showed a bare zip file. No cover image. Nothing to look at. It looked like a download link someone lost, not a product. I had spent weeks generating 1,700 assets and writing ~19,000 lines of pipeline code, and the thing that almost killed the whole project was a missing 1280×720 JPEG.
Here is what I learned about the last mile of a digital product pipeline, the part nobody warns you about.
A zip is not a product
A zip file is a container. A product is a container plus a promise of what's inside. Marketplaces and storefronts both assume this. On Adobe Stock, your submission rides on the preview image and the metadata. On Gumroad, the cover is the only thing a buyer sees before deciding to spend $0.99 (which is, honestly, what my first sale was, and I was still happy about it).
My pack_product.py script now does three things every time it bundles a product:
- Zips the files
- Renders a cover at exactly 1280×720
- Renders a thumbnail at 600×600
Those two numbers are not arbitrary. The wide 16:9 cover fits marketplace listing cards, and the square 600×600 thumbnail fits the places that want a square. Rendering both at pack time means I never ship a product with a missing image again, because the pack step fails loudly if it can't produce them.
The other thing that got me rejected: CSV headers
Before the cover problem, I burned time on metadata. All three stock channels I submit to (Adobe, Vecteezy, Dreamstime) reject a wrong CSV header instantly. Not "reviews slowly and gives feedback". Rejects on upload.
Adobe wants a 5-column header. Vecteezy wants 4 columns. Dreamstime wants 15. If you hand-write these, you will get one wrong eventually, and a re-run of your batch will produce inconsistent files. So make_metadata.py writes the exact header each platform demands and nothing else. It reads a folder of assets, title-cases the filenames as a first pass, and writes the CSV. If I pass a titles.json with real titles and keywords, it uses those instead.
The honest warning: filename-derived titles are fine for a first pass, but replace them before you submit. Reviewers can tell.
Double-posting is the fastest way to get flagged
This one scared me straight. If you run your upload batch twice, because you lost track of what you'd already submitted, you can get your account flagged for duplicate submissions. With 58 assets live on Adobe out of 1,700 made, I am running batches constantly, and losing track is easy.
So I built the dumbest possible ledger: upload_tracker.py. It's a text-file-backed tracker with three commands:
python upload_tracker.py pending items.txt
python upload_tracker.py done "asset-001.jpg"
python upload_tracker.py status
A re-run never double-posts because the ledger remembers. No database, no cloud, standard library only. Two of my four scripts need zero pip installs, which matters when your server has 2 GB of RAM and you'd rather not install anything you don't need.
Packaging on a 1 vCPU, 2 GB box
The whole pipeline runs on a single-core server with no GPU. Cover rendering with Pillow is cheap, but the habits that keep the box alive apply to any render step:
- One process at a time. Never fan out image jobs in parallel on 1 core.
- Call
gc.collect()after each asset. - Measure peak RSS with
resource.getrusage(RUSAGE_SELF).ru_maxrss, not average usage. Peak is what kills you. - Run heavy render steps one file per subprocess so memory goes back to the OS.
- Inspect previews at 60 dpi, not 300. Render at the resolution you actually need.
The result: 39 finished products, 856 MB of output, packaged on hardware that costs pennies a month.
The folder layout that saved my catalogue
One more thing, because it's the mistake I can't un-see. Without a fixed structure, a catalogue becomes a junk drawer. Mine is:
project/
assets/ raw output
output/ ready-to-submit files + metadata.csv
packs/ finished zips + covers
research/ notes and sources
scripts/ the tools
And keep research/ forever. I deleted mine once to "clean up" and lost raw material for future products. Won't repeat that.
Where this came from
Everything above comes from a real project: 1,700 assets made, 58 live on Adobe, first sale $0.99, ~19,000 lines of pipeline code, 300+ pages of research behind the rules. I pulled the four scripts that did the packaging work into a small kit: make_metadata.py, upload_tracker.py, md2pdf.py, and pack_product.py. Two third-party packages total (Pillow and reportlab), the other two scripts are standard library only. No framework, no cloud account, Python 3.9+.
If you're building your own pipeline and the making-the-art part is going fine but the last mile keeps stalling you, this is the tool I built from that work. It's not a magic system, it's four scripts and the folder layout they assume.
Building digital products? Free guides & kits for digital product sellers.
Top comments (0)