The 1280 720 Lesson: Why a Zip Isn't Shippable Until It Has a Cover"**
The 1280×720 Lesson: Why a Zip Isn't Shippable Until It Has a Cover For three weeks I had been building a sticker pack. A hundred and twenty drawings, each one traced, cleaned, and exported with the same four commands.
The 1280×720 Lesson: Why a Zip Isn't Shippable Until It Has a Cover
For three weeks I had been building a sticker pack. A hundred and twenty drawings, each one traced, cleaned, and exported with the same four commands. On Friday night I ran pack_product.py, watched a 34 MB archive appear in the packs/ folder, and uploaded the whole batch to a marketplace. Six hours later the review queue came back: every listing rejected. The reason was one line of boilerplate about images needing to be presentable at a glance. There was no image. I had shipped a zip full of my best work with nothing but a filename attached to it.
That was the moment I understood something I had been avoiding for weeks. The art was never the bottleneck. The last five percent was. And the last five percent is always the cover.
The rule is real, and it is specific
Stock and marketplace platforms don't just frown on a bare zip. Vecteezy, Adobe Stock, and Dreamstime each require a preview asset with every upload, and the sizing matters. A 1280×720 cover is the safe default, wide enough for a listing card and small enough to render quickly. A 600×600 thumbnail is what buyers actually see in search results, so if the square crop is unreadable, the listing is invisible no matter how good the vector underneath is.
The fix in the kit is a single command:
python pack_product.py my-product "My Product" "A short subtitle" 9
That writes my-product.zip, my-product-cover.jpg (1280×720), and my-product-thumb.jpg (600×600) into the _system folder next to where you run it. Pillow is the only dependency, and it is one of just two third party packages in the whole toolset. The other is reportlab, used by md2pdf.py. Everything else runs on the standard library.
The lesson generalizes. A pack without a cover is not a product. It is a folder. The cover is the product's first sentence, and buyers decide in under a second whether to read the second one.
Packaging is where solo sellers actually stall
When I broke down where my own time went, it was not drawing. It was four repetitive tasks, and all four were rejection magnets:
Metadata. All three channels reject a wrong CSV header instantly, before a human ever sees the file. Adobe wants five columns, Vecteezy wants four, Dreamstime wants fifteen, and the header has to match exactly. make_metadata.py writes the header each platform demands and nothing else:
python make_metadata.py ./my-assets --platform adobe --out metadata.csv --ext .jpg
python make_metadata.py ./my-assets --platform vecteezy --out vecteezy.csv --ext .jpg
python make_metadata.py ./my-assets --platform dreamstime --out dreamstime.csv --ext .jpg
Without --titles, the filename gets title-cased and used as the title. That is fine for a first pass and never fine for a submission. Pass a real titles file before you publish.
Double-posting. The fastest way to get an account flagged is running your batch twice. upload_tracker.py makes that impossible by remembering what is already done:
python upload_tracker.py pending items.txt
python upload_tracker.py done "asset-001.jpg"
python upload_tracker.py status
Turning a doc into a deliverable. Buyers expect a PDF, not a markdown file. md2pdf.py content.md product.pdf "My Product Title" handles headings, tables, lists, and links in one command.
None of this is glamorous. All of it is the difference between a listing that goes live and one that sits in a rejection queue.
What it took to get 39 products out
This pipeline packaged 39 products, 856 MB of finished output, from a parent project that carried roughly 19,000 lines of pipeline code and 300 plus pages of research behind the rules the scripts enforce. None of that ran on special hardware. It ran on 1 vCPU, 2 GB of RAM, and no GPU, with a few habits that made it possible:
- One process at a time. Never fan out image jobs on a single core.
-
gc.collect()after each asset to free memory between items. - Measure peak RSS with
resource.getrusage(RUSAGE_SELF).ru_maxrss, not average memory. The peak is the number that kills the process. - Process one file per subprocess for heavy render steps, so memory goes back to the OS.
- Render previews at 60 dpi. If it looks fine at 60, it looks fine.
The stack above the four scripts cost nothing. An orchestrator schedules jobs and retries failures. A local proxy handles model access. The packaging layer is the part that touches the money, and it is the part most people skip.
What I built from this
The 1280×720 rejection was not a setback. It was the specification. After that night, every product in the catalogue went through the same channel: build the file, generate the metadata CSV for each platform, check the tracker, render the cover and the thumbnail, then package. The cover stopped being an afterthought and became the gate.
If you are a solo maker selling stock vectors, stickers, or templates, this is the workflow that actually gets you to the upload button without a rejection queue. I wrote it all down, including the exact scripts, the folder layout that scales, and the platform rules that keep your listings live, as the ai sticker factory playbook.
You do not need a bigger machine. You need to stop shipping bare zips.
Get ai sticker factory playbook
Building digital products? Free guides & kits for digital product sellers.
Originally published by Dev.to AI. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.