First sale on Adobe paid $0.99. That part is real and I keep it framed on my monitor. What I do not frame is the rejection two weeks before it: I uploaded a batch of 140 files and every one came back rejected in under a minute. Not because the art was bad. Because the first line of my CSV read filename,title,keywords and Adobe wanted a header with five exact columns. The whole batch died on a comma.
I had spent the weekend drawing. I lost it in four seconds to a header row.
Wrong header, instant reject
All three channels check the CSV before they look at your art.
Adobe Stock wants 5 columns. The order matters, the names matter. Vecteezy wants 4. Dreamstime wants 15 and it will not forgive a missing one.
I built make_metadata.py for exactly this. One command, one platform, the header that platform demands:
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
It reads the folder, lists each file, writes the row, writes the header. Nothing clever. The point is that it never guesses. The used it on 1,700 assets. It uses the standard library alone, no install.
One honest caveat: without a titles file it title-cases the filename. That is fine for a first pass, and useless for submission. Pass --titles titles.json with real titles and keywords before you upload, or you will get rejected for weak metadata next.
A re-run should not double-post
The fastest way to get an account flagged is running the same batch twice. I did that once on a smaller channel. Uploaded 60 files, forgot, uploaded them again an hour later. Duplicate notices on my account for a week.
upload_tracker.py is a plain ledger. It remembers what you already posted.
python upload_tracker.py pending items.txt # shows what is NOT done yet
python upload_tracker.py done "asset-001.jpg"
python upload_tracker.py status
No database. A text file. On the tiny server that ran this whole pipeline, that was the right call. You ask for pending, you get the list you have not touched. No memory, no second run.
The server was 1 vCPU and 2 GB of RAM
No GPU. That is where this ran, all 39 products.
A few habits made it work and none of them are clever:
- One process at a time. Never fan out image jobs on one core.
-
gc.collect()after each asset. - Measure peak RSS, not average, with
resource.getrusage(RUSAGE_SELF).ru_maxrss. - One file per subprocess for heavy render steps, so memory returns to the OS.
- Render at the resolution you need. Inspecting a preview at 60 dpi beats rendering it at 300.
I learned the last one after a render job hit swap and killed the box. It was rendering previews nobody looked at.
Covers and packs sell, bare files do not
A zip with a real cover sells. A bare folder does not. pack_product.py zips the files and renders a 1280Ã720 cover plus a 600Ã600 thumbnail in one command:
python pack_product.py my-product "My Product" "A short subtitle" 9
Two third-party packages in the whole kit: Pillow for the covers, reportlab for PDF output. make_metadata.py and upload_tracker.py need no install at all.
I also keep a fixed folder layout, because a catalogue turns into a junk drawer without one:
assets/ raw output
output/ ready-to-submit files + metadata.csv
packs/ finished zips + covers
research/ notes and sources
scripts/ the tools
Keep research/ forever. I deleted a scrape log once to clean up. It was the raw material for three later products. I rebuilt it by hand.
What came out of this
The pipeline packaged 39 products, 856 MB of output. Behind it sit about 19,000 lines of pipeline code and 300+ pages of research on the rules the scripts enforce. Of 1,700 assets made, 58 are live on Adobe. First sale paid $0.99. That is the real number, not a highlight reel.
The four scripts are the packaging layer. md2pdf.py turns a Markdown doc into a clean PDF in one command, which is how the written products got made:
python md2pdf.py content.md product.pdf "My Product Title"
None of this needed a framework or a cloud account. It needed the correct header, a ledger that stops double-posting, and a box that did not fall over.
If you are building the same thing, the whole system is in the Digital Asset Factory: the four scripts, the folder layout, and the rules they enforce, built from this exact work. No hard sell. Read it, or take the free header logic and write your own.
Building digital products? Free guides & kits for digital product sellers.
Top comments (0)