When My Python Scripts Hit Their Limit And I Started Outsourcing The Boring Parts
The moment I knew something had to change was a Tuesday night in front of a rejected upload. Adobe had bounced a batch of 40 JPGs because my CSV had six columns instead of five. Not bad art, not bad keywords. A header row. I had spent an evening generating assets, and the whole batch died on a spreadsheet formatting rule I could have looked up in two minutes.
That was the point where I stopped trying to write everything myself and started treating the boring parts as solved problems. Here is what I learned about which parts are worth your time and which are not.
The metadata wall is real and it is unforgiving
Every stock platform has its own CSV format and every one of them rejects a wrong header instantly, with no partial credit. Adobe wants 5 columns. Vecteezy wants 4. Dreamstime wants 15. If you write one generic CSV and try to reuse it, you will get rejections, and repeated rejections are how accounts get flagged.
The fix that actually worked for me was a script that writes the exact header each platform demands and nothing else. It reads a folder of files, title-cases the filenames as a first-pass title, and emits the right CSV per platform:
python make_metadata.py ./my-assets --platform adobe --out metadata.csv --ext .jpg
I still review titles and keywords before submitting, because a filename-based title is fine for a draft and bad for a listing. But the formatting failure mode is gone. That one script removed the single most common reason my batches died.
Double-posting will get you flagged faster than bad art
This one cost me real anxiety. When you run a batch job twice, because you forgot whether you ran it, or a terminal session died halfway, you double-post. Platforms notice. The fastest way to get an account flagged is running your batch twice.
The solution is embarrassingly simple: a ledger. One script that records what you have already posted, so a re-run skips finished items instead of repeating them.
python upload_tracker.py pending items.txt
python upload_tracker.py done "asset-001.jpg"
python upload_tracker.py status
No database, no cloud. Standard library only. It is the least impressive code in my project and the one I would least want to live without.
You do not need a big server, you need habits
The entire pipeline that produced 39 finished products ran on 1 vCPU, 2 GB RAM, no GPU. That is the tier of the cheapest VPS you can rent. It works if you follow a few rules that I learned by crashing the box more than once.
One process at a time. Never fan out image jobs in parallel on a single core. Free memory between items with gc.collect(). Measure peak RSS, not average, with resource.getrusage(RUSAGE_SELF).ru_maxrss, because the average tells you nothing about when the OOM killer shows up. For heavy render steps, process one file per subprocess so memory actually returns to the OS. And render at the resolution you need: inspecting a preview at 60 dpi instead of 300 is the difference between finishing a batch and swapping into oblivion.
The parent project is around 19,000 lines of pipeline code, and most of it exists because I did these things wrong first.
Packaging is where drafts become products
Making the art is the fun part. Turning it into something that looks buyable is not, and it is where most of my evenings went. Two scripts closed that gap.
md2pdf.py turns a Markdown file into a styled PDF in one command, headings, tables, lists, links included, using reportlab. pack_product.py bundles a product: it zips the files and renders a cover at 1280×720 and a thumbnail at 600×600 using Pillow. A zip with a real cover sells. A bare file does not. I learned that by selling bare files.
python pack_product.py my-product "My Product" "A short subtitle" 9
Between the two of them, plus Pillow and reportlab, that is the entire third-party dependency list. Everything else is standard library. On a 2 GB box, that restraint is not a philosophy, it is a requirement.
Keep your research folder forever
One structural decision changed how fast I could ship. A fixed folder layout: assets/ for raw output, output/ for ready-to-submit files, packs/ for finished zips, research/ for notes and sources, scripts/ for the tools.
The mistake I made exactly once was deleting research/ to clean up. Your scrape logs and notes are the raw material for the next product. Behind the rules these scripts enforce sit more than 300 pages of research. Deleting that to tidy a directory is a loss you cannot undo.
What outsourcing actually meant for me
I want to be honest about the word in the title. I did not hire anyone. I outsourced the boring parts to small, boring, single-purpose scripts that each do one thing and fail loudly. Metadata headers, upload tracking, PDF rendering, packaging. None of these are creative work. All of them were eating the hours I should have spent making things and writing listings.
The result was 39 finished products, 856 MB of packaged output, running on hardware that costs a few dollars a month. The four scripts that did the packaging are the ones I kept coming back to, so I cleaned them up and put them in one place as the pipeline starter kit. It is not a framework and there is no cloud account. It is the working tool built from this exact workflow, with the platform rules and the RAM habits baked in. If you are stuck at the same wall I was, a rejected CSV on a Tuesday night, it is built for you.
Building digital products? Free guides & kits for digital product sellers.
Top comments (0)