I once lost an entire afternoon because a file was named sunflower_final_v3(2).jpg. My CSV had 200 rows, Adobe rejected the upload with a header error, and when I finally fixed the header, half my titles were garbage because the script title-cased the filename and the filename was a mess. That was the day I stopped treating filenames as throwaway and started treating them as the primary key of my whole metadata system. Here is the naming system I now use, and why it works.
The filename is the primary key, whether you like it or not
My make_metadata.py script builds Adobe (5-column), Vecteezy (4-column) and Dreamstime (15-column) CSVs from a folder of files. When you do not pass a titles.json, the script title-cases the filename and uses it as the title. That is fine for a first pass, but it means every bad character in a filename ends up in a title that a reviewer sees.
So the rule is simple: a filename must survive title-casing. That means lowercase words separated by hyphens, no parentheses, no spaces, no version suffixes, no emoji. sunflower-final.jpg, not sunflower_final_v3(2).jpg. If you would not put it on a product page, do not put it in a filename.
One ID, three platforms
The same asset goes to three channels, and each platform wants a different CSV shape. Adobe wants 5 columns, Vecteezy wants 4, Dreamstime wants 15. All three reject a wrong header instantly, so the script writes exactly the header each platform demands and nothing else.
What makes this tractable is that all three CSVs are generated from the same source of truth: the filename. asset-001.jpg in the Adobe CSV is asset-001.jpg in the Vecteezy CSV and the Dreamstime CSV. When a file gets rejected on one platform, I can find the same row everywhere in seconds. When I fixed a title, I fixed it once in titles.json and regenerated all three.
The ledger prevents the worst mistake
The fastest way to get an account flagged is running your batch twice and double-posting. I did this once, early on, re-running a batch after a partial failure. Since then, upload_tracker.py is a plain ledger: done "asset-001.jpg" marks a file, pending items.txt shows what is actually left, status gives the count. A re-run never double-posts because the tracker remembers.
This only works because filenames are stable. If your export step regenerates names, or your editing tool appends suffixes, the ledger breaks. Fixed names are not aesthetics, they are what makes idempotency possible.
A folder layout that does not rot
The kit assumes one structure, and I have stopped fighting it:
project/
assets/ raw output, one file per asset
output/ ready-to-submit files + metadata.csv
packs/ finished product zips + covers
research/ notes and sources
scripts/ the tools
The one habit I would stress: keep research/ forever. I deleted my scrape logs once to "clean up" and later wanted to rebuild a related product from the same research. Gone. Your notes are raw material for future products.
Titles and keywords are a separate layer
The naming system gets you findable structure, but the title that sells is written, not derived. That is why make_metadata.py accepts --titles titles.json: the filename is the ID, the JSON is the human layer. My workflow is to generate the CSV with filename titles first, check what came out, then write real titles and keywords for anything going to review. Filename-derived titles are a first pass, replace them before you submit. Reviewers can tell.
Why this matters more as you scale
The parent project behind these scripts is about 19,000 lines of pipeline code, and it packaged 39 products, 856 MB of output, all validated on 1 vCPU with 2 GB RAM and no GPU. Nothing in that pipeline is clever. It works because the boring parts are consistent: one file per asset, one ID per file, one ledger tracking what shipped. When your whole pipeline runs unattended on a tiny box, a filename with a stray parenthesis is not a cosmetic problem, it is a failed batch at 2 AM with nobody watching.
If you want the same tools instead of rebuilding them, I put the exact scripts into the Pipeline Starter Kit: make_metadata.py, upload_tracker.py, md2pdf.py and pack_product.py. The metadata and tracker scripts use only the Python standard library, no install at all, and the other two need just Pillow and reportlab. The naming rules and the workflow around them are part of the ai sticker factory playbook, which walks through the whole pipeline from prompt to packaged product with the same numbers and constraints I have described here.
Get ai sticker factory playbook
Building digital products? Free guides & kits for digital product sellers.
Top comments (0)