DEV Community

Cover image for How A Local Prompt Engine Replaced Paid Generation Across Three Stock Platforms

How A Local Prompt Engine Replaced Paid Generation Across Three Stock Platforms

Last March I checked my generation spend and closed the laptop. Three stock platforms, two paid image APIs, and a month where the credits cost more than the uploads earned. I had 40 files sitting in a folder and no margin left after paying to make them. That was the moment I decided to stop renting generation and run a local prompt engine instead, with the paid services as a fallback, not the default.

This post is what actually changed, with the numbers and the platform rules that forced the redesign. It is not a "local AI is free" post, because it is not free, it just moves the cost to hardware and time.

The metadata wall comes first, not the model

The unglamorous truth: the model was never the bottleneck. The bottleneck was that Adobe Stock, Vecteezy, and Dreamstime each demand a CSV in their own shape, and each rejects a wrong header instantly at upload. Adobe wants 5 columns. Vecteezy wants 4. Dreamstime wants 15. Get one header wrong and the whole batch bounces, and you find out after the upload finishes, not before.

Once I had a local engine producing files, I wrote make_metadata.py so the CSVs come out of the same script per platform:

python make_metadata.py ./output --platform adobe      --out adobe.csv      --ext .jpg
python make_metadata.py ./output --platform vecteezy   --out vecteezy.csv   --ext .jpg
python make_metadata.py ./output --platform dreamstime --out dreamstime.csv --ext .jpg
Enter fullscreen mode Exit fullscreen mode

Without a titles file it title-cases the filename, which is fine for a first pass and not fine for submission, so I always pass --titles titles.json with real titles and keywords before anything goes out. The point is that a local pipeline only pays off if the last mile, the exact upload format each marketplace demands, is automated too. Otherwise you traded API credits for an hour of spreadsheet work every night.

Double-posting is the fastest way to get flagged

Here is the failure I have seen hurt people, including me once: you run a batch, the connection drops at file 60 of 120, and you re-run the script. Now the marketplace sees duplicate submissions. Repeated duplicates are one of the quickest ways to get an account reviewed or flagged.

So the second script is dumber than the first and more important. upload_tracker.py is a plain ledger:

python upload_tracker.py pending items.txt
python upload_tracker.py done "asset-001.jpg"
python upload_tracker.py status
Enter fullscreen mode Exit fullscreen mode

Any batch job checks pending before it starts and marks done as each file ships. A re-run skips what is already posted. It uses the standard library alone, no install, and it has prevented exactly one catastrophe per quarter since I wrote it.

What local generation actually costs

Honest numbers. I run on a 1 vCPU, 2 GB RAM server with no GPU. That rules out diffusion models entirely, so my local engine is template and rule based: parameterized prompts, composited layouts, and procedural variation, rendered with lightweight tools. For anything that genuinely needs a heavy model, I pay per image, and the discipline is that paid generation is now a per-asset decision instead of a subscription I burn by default.

The trade looks like this. Paid API: fast, high ceiling, cost scales with volume. Local template engine: near-zero marginal cost, lower ceiling per image, but volume is free, so 39 products and 856 MB of output later, the math worked. The catch is that a 2 GB box punishes bad habits, so the rules that made it survive:

  • One process at a time. Never fan out image jobs in parallel on a single core.
  • Call gc.collect() after each asset.
  • Measure peak RSS with resource.getrusage(RUSAGE_SELF).ru_maxrss, not average usage, because peak is what gets you OOM-killed.
  • One file per subprocess for heavy render steps, so memory returns to the OS.
  • Inspect previews at 60 dpi, render finals at 300. Never render at 300 to check a layout.

Three platforms, one folder

The third thing that changed was structure. A catalogue becomes a junk drawer without a fixed layout, so everything now lives in one shape:

project/
  assets/      raw output
  output/      ready-to-submit files + metadata CSVs
  packs/       finished product zips + covers
  research/    notes, scrape logs, sources
  scripts/     the tools
Enter fullscreen mode Exit fullscreen mode

The one rule I would tattoo on the wall: never delete research/. I cleaned it out once to "tidy up" and lost the raw material for two products I later had to rebuild from scratch. Scrape logs and notes are future inventory.

From files to products

Local generation produces files. Buyers buy products. Closing that gap took two more scripts. md2pdf.py turns a Markdown doc into a styled PDF in one command, headings, tables, lists, links, using reportlab. pack_product.py bundles a folder into a zip and renders a 1280×720 cover and a 600×600 thumbnail with Pillow, output into a _system folder you can move with --dir or PACK_ROOT. A zip with a real cover sells, a bare file does not, and I stopped arguing with myself about covers once they were generated.

Between them the whole chain is four Python scripts and two third-party packages, Pillow and reportlab. The metadata and tracking scripts need nothing but Python 3.9+.

What I would tell myself last March

The paid APIs did not lose because local generation is better art. They lost because the expensive part of this business was never generation, it was everything after: exact CSV headers, duplicate protection, packaging, and a pipeline that survives on 2 GB of RAM. Once those were scripts instead of chores, the per-image cost stopped being the thing that decided whether a month was profitable.

I packaged the exact scripts from this work, the four tools that shipped 39 products, into a small kit. No framework, no cloud account, install in one pip install -r requirements.txt, quick start in the README. If you are building the same kind of pipeline, it is the tool I wish I had before the month I described at the top.

See the kit


Building digital products? Free guides & kits for digital product sellers.

Top comments (0)