Last March I sat on a Sunday night with 40 finished vector packs and a rejection email. Adobe Stock had bounced the CSV. The reason: my header row had five columns in the wrong order. Not the art. Not the keywords. A header row I had hand-typed in a spreadsheet at midnight. Every file in that batch was done, reviewed, and ready. None of it could be submitted until I fixed a text file.
That night changed how I think about this work. The art is maybe 30% of the job. The other 70% is packaging, and almost nobody builds for it.
The metadata layer is unforgiving
Every stock platform has an upload CSV with an exact column count and order. Adobe wants 5 columns. Vecteezy wants 4. Dreamstime wants 15. Get the header wrong and the platform rejects the file before a human ever looks at it. There is no partial credit.
The fix is boring: one script that writes the header each platform demands and nothing else. Mine reads a folder of files, title-cases the filenames as a first pass, and emits three CSVs if I ask for three. When I have real titles and keywords, I pass them in from a JSON file. I have not hand-typed a header row since.
If you do this by hand, budget the time honestly. Fifteen columns, one per asset, times 40 assets, is an afternoon of error-prone spreadsheet work. Automating it took one evening once and then zero time forever.
Double-posting gets you flagged, not just embarrassed
Here is the failure mode nobody warns you about. You run a batch upload. Something hiccups halfway. You fix it, re-run the batch, and now half your assets exist twice on the platform. On some marketplaces, duplicate submissions are one of the fastest ways to get an account reviewed or flagged.
My solution is embarrassingly simple: a ledger script. upload_tracker.py done "asset-001.jpg" marks a file as posted. pending shows what is left. status gives the count. A re-run can never double-post because the tracker remembers. It uses only the Python standard library, no install, no database. A text file and some discipline encoded in code.
A document is not a product until it looks like one
I had a 60-page Markdown guide that was genuinely useful. As a raw .md file it sold nothing. Same content, zero packaging.
Two small tools fixed this. One converts Markdown to a styled PDF with real headings, tables, lists, and links, built on reportlab. One command, done. The other bundles a product: it zips the files and renders a cover at 1280x720 plus a thumbnail at 600x600 using Pillow. Those two image sizes matter because they are what storefronts actually ask for, and generating them automatically means I never ship a product with a placeholder cover again.
The difference in perceived value between "a zip of files" and "a zip with a cover, a thumbnail, and a clean PDF" is not subtle. Same content. Completely different product.
The whole thing ran on 1 vCPU and 2 GB of RAM
I want to kill the idea that you need cloud infrastructure for this. My entire pipeline, the one that packaged 39 products and 856 MB of output, ran on a single-core box with 2 GB of RAM and no GPU. The habits that made it work:
- One image job at a time. Never fan out in parallel on one core.
- Call
gc.collect()after each asset to free memory between items. - Measure peak RSS, not average.
resource.getrusage(RUSAGE_SELF).ru_maxrsstells you the real ceiling. Average memory lies to you. - Run heavy render steps one file per subprocess so memory returns to the OS.
- Render at the resolution you need. Inspecting a preview at 60 dpi instead of 300 saves most of the memory.
The other cost people forget is AI image generation itself. Every generation costs money or credits, and re-generating because a downstream step failed is pure waste. Fixing the packaging layer first means the expensive part of the pipeline only runs once per asset.
The folder structure is part of the system
A catalogue without a fixed layout becomes a junk drawer. Mine is five folders: assets for raw output, output for ready-to-submit files plus metadata, packs for finished zips and covers, research for notes and sources, scripts for the tools.
One rule I learned the hard way: keep research/ forever. I deleted my scrape logs once to "clean up" and later wanted the exact notes behind a product decision. They were gone. Your research folder is the raw material for the next product, not clutter.
What I actually built
After the 39 products, roughly 19,000 lines of pipeline code, and over 300 pages of research behind the rules these tools enforce, I pulled out the four scripts that do the packaging layer and made them standalone. No framework, no cloud account, Python 3.9 and up. Two third-party packages total: Pillow and reportlab. The metadata builder and the upload tracker need nothing but the standard library.
They are not glamorous. They are the boring 70% of the job, done once, so the fun 30% is the only part you think about. That is the whole pitch.
Building digital products? Free guides & kits for digital product sellers.
Top comments (0)