I still have the screenshot. It's an Adobe Stock rejection screen from last year, and the reason field says the file could not be processed. Not "rejected for quality". Not "keyword spam". Just a silent fail on a batch of 20 vectors I'd spent two days generating. The problem wasn't the art. It was my CSV header. Adobe's uploader expects a specific 5-column metadata header, and mine had the columns in a slightly different order. Every file in the batch bounced.
That moment changed how I think about this work. The art was never the bottleneck. Here are the four things that actually were, and what closed each one.
Metadata headers are a hard gate, not a suggestion
Each stock platform has its own exact CSV format, and the upload checks it before it checks anything else. Adobe wants 5 columns. Vecteezy wants 4. Dreamstime wants 15. Get one column wrong or put them in the wrong order and the whole batch fails instantly, no partial credit.
Once I knew the headers, I stopped hand-building spreadsheets. make_metadata.py walks a folder of files and writes the exact header each platform demands, nothing more. Run it three times with --platform adobe, --platform vecteezy, --platform dreamstime and you get three correct CSVs from the same folder. It uses only the Python standard library, so there is nothing to install. Without a titles file it title-cases the filename, which is fine for a first pass, but replace those before you submit, because generic titles are the second most common reason for soft rejections.
Double-posting is the fastest way to get flagged
This one almost ended my Vecteezy account. I re-ran a batch script after fixing one file and forgot it would re-submit everything. Same assets, posted twice, days apart. Reviewers notice that pattern and it looks like spam behavior.
The fix is dumb-simple and I should have built it on day one: a ledger. upload_tracker.py records every asset you mark done, and pending shows you only what has not been posted yet. A re-run now skips anything already in the ledger. It is also standard library only, no dependencies, runs anywhere Python runs. If you are batching 50 or 500 assets, this script is the difference between a clean account and a flagged one.
Content is not a product until it looks like one
I had a 40-page guide sitting in Markdown for months because turning it into a PDF that did not look like a printout felt like a weekend of fiddling with Word. Meanwhile the content was finished. The gap between "I made this" and "I can sell this" was purely cosmetic.
md2pdf.py closes it with one command. It uses reportlab to render headings, tables, lists, and links into a clean styled PDF. Pass it the Markdown file, an output name, and a title. That is the whole interaction.
Packaging has the same problem. A bare zip sells poorly. A zip with a real cover at 1280×720 and a square thumbnail at 600×600 looks like something a marketplace listing expects. pack_product.py builds both with Pillow, writes the zip, and drops everything in a _system folder you can relocate with --dir or the PACK_ROOT environment variable. No script editing.
The pipeline has to run on nothing
I do not own a GPU rig. The whole thing ran on a 1 vCPU, 2 GB RAM server, no GPU. That constraint forced habits that turned out to be load-bearing:
- One image job at a time. Fanning out parallel renders on one core just thrashes.
-
gc.collect()after each asset, or memory quietly climbs until the process dies at asset 40 of 60. - Measure peak RSS with
resource.getrusage(RUSAGE_SELF).ru_maxrss, not average usage. The peak is what kills you. - One file per subprocess for heavy renders so memory actually returns to the OS.
- Inspect previews at 60 dpi instead of rendering at 300. Same decisions, a fraction of the memory.
None of this is clever. It is just what the machine allows.
What the numbers look like now
Since building this pipeline I have made about 1,700 assets, packaged 39 products (856 MB of output), and gotten 58 files live on Adobe Stock. My first sale was $0.99. That is not a fortune, and I am not going to pretend it is. But it proves the loop works end to end: generate, package, submit, sell. The boring parts, headers, ledgers, covers, PDFs, are now scripts that run in seconds instead of hours of spreadsheet work.
The parent project is around 19,000 lines of pipeline code backed by 300+ pages of research into platform rules. The four scripts I described are the packaging layer of that system, the part that kept breaking and is now boring.
I put them together as the Digital Asset Factory, the full system built from this work, including the pipeline starter kit with those exact scripts. If you are stuck on the same four failure points, it is the honest version of what worked for me, not a promise of passive income.
Building digital products? Free guides & kits for digital product sellers.
Top comments (0)