One Asset Folder, Three Platform CSVs: Stop Hand-Writing Metadata

A developer uses Python scripts to generate metadata CSVs for three stock platforms from one asset folder, with upload tracking and a low-resource packaging pipeline.
The article presents an automation scripting approach for stock asset contributors. The core tool, make_metadata.py, reads a single asset folder and generates CSVs matching the column templates of Adobe Stock, Vecteezy, and Dreamstime, eliminating the need to hand-write three separate spreadsheets. The author stresses that platform review validates header format first, and wrong column order leads to outright rejection, making automation an entry requirement. A companion upload_tracker.py uses a plain-text ledger to record uploaded items and prevent duplicate submissions that could trigger account reviews. The whole pipeline ran 39 products on 1 vCPU, 2GB RAM, and no GPU, with key habits including single-process processing, manual GC, peak RSS monitoring, and on-demand resolution rendering. Markdown-to-PDF conversion and product packaging with covers are also scripted, and the research directory is worth keeping for reuse.
Title
One Asset Folder, Three Platform CSVs: Stop Hand-Writing Metadata
Meta Description
Tired of hand-writing CSVs in different formats for Adobe Stock, Vecteezy, and Dreamstime? One Python script generates metadata for all three platforms from a single asset folder, plus duplicate-submission safeguards and minimal-hardware pipeline lessons.
Article Content
Rewriting a CSV by hand every time you upload assets to a stock platform? Adobe Stock, Vecteezy, and Dreamstime each use different spreadsheet formats, and copy-pasting late into the night is an easy way to make mistakes. One developer shared his automation script approach: one asset folder, one command to generate the metadata files all three platforms need, and no more repetitive busywork.

The Core Idea: Same Assets, Different Wrapping
The author once had a 40-JPG submission instantly rejected by Adobe Stock because of a column-order mistake. He then discovered that Vecteezy requires 4 columns and Dreamstime requires 15, meaning the same metadata had to be hand-written three times. His solution is a script called make_metadata.py that reads a single folder and outputs the CSV each platform requires:
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
The key point is that platform review processes check the header first — if the format is wrong, you're rejected by a machine outright. Column order isn't a nice-to-have; it's the entry requirement. The script uses only the Python standard library, with no pip installs or API keys, lowering the barrier to use.
Titles and keywords can be specified via a titles.json file; if you don't provide one, the script converts filenames into title format. But the author warns that a filename like floral_wreath_v2.jpg makes a terrible stock title, so be sure to replace it before a formal submission.
A stock platform's metadata CSV is essentially a structured table where each column corresponds to a field (such as title, keywords, category, description), and the platform's backend review program reads data by predefined column index. A wrong column order means the program treats the title as keywords, or keywords as the category, triggering a format validation failure. Adobe Stock, Vecteezy, and Dreamstime each define their own column templates and field names — that's the fundamental reason the same asset metadata must be generated as three separate CSVs. Once you understand this, the script's logic becomes clear: it only changes the output format, never the assets themselves.
The so-called "header row" is the first line of a CSV declaring the column names. Many stock platforms' automated review matches the header string before parsing the data, and rejects the entire batch if it doesn't match the official template. This is precisely why the developer emphasizes that column order is the entry requirement: stock platforms process massive volumes of submissions daily, making manual inspection extremely costly, so automated pre-checks become the first — and highest-elimination-rate — gate. That's why an automation script must precisely replicate the platform template's column names and order, rather than merely "containing the same information."
The Overlooked Risk of Duplicate Submissions
Automation brings convenience, but also hidden risks: re-running an upload batch can put the same file into the queue twice, triggering the platform's duplicate-submission review for contributors and potentially endangering your account. To address this, the author added a minimal upload_tracker.py that records uploaded items in a text file:
python upload_tracker.py pending items.txt # view incomplete items
python upload_tracker.py done "asset-001.jpg"
python upload_tracker.py status
This tool is deliberately simple — a text ledger you can read directly with cat is more trustworthy than a SQLite database plus an admin panel, especially when cross-checking before submission.
Stock platforms have explicit policies on contributor duplicate submissions: if the same file is identified as a duplicate upload, the batch may be rejected at best, or your account may be flagged as an abnormal contributor at worst, affecting future review priority or even leading to account restrictions. Duplicate submissions happen easily in practice because automation scripts can be re-run indefinitely, and developers often regenerate an entire batch of CSVs after tweaking parameters and upload them directly, forgetting which files were already submitted.
A plain-text ledger of uploaded records is more reliable than a database because it has no hidden state: any text editor can view and modify it, a quick cat before submission confirms it, and records won't be lost due to database locks, migrations, or version issues. In a personal or small-team pipeline, this "readability-first" design significantly reduces the chance of operational errors.
A Minimal Hardware Setup Runs the Full Pipeline
The author emphasizes that the entire workflow needs no server. He produced 39 finished products and 856MB of output using just 1 vCPU, 2GB of RAM, and no GPU. Key habits include:
- Single-process processing — don't parallelize image tasks on a single core, avoiding resource contention.
- Call
gc.collect()after processing each asset; on a 2GB machine, memory leaks are the main cause of mid-run crashes. - Monitor peak RSS rather than average; only
resource.getrusage(RUSAGE_SELF).ru_maxrssreflects the true memory ceiling. - One subprocess per file for heavy rendering steps, ensuring memory is returned to the OS.
- Render at the resolution you need — 60dpi instead of 300dpi for previews saves far more time and memory than any other optimization.
The metadata and tracking scripts themselves consume very few resources, well within this budget.
RSS (Resident Set Size) refers to the actual physical memory a process is currently using. Monitoring average RSS masks short-term memory spikes, yet it's precisely those spikes that trigger the out-of-memory process kills. resource.getrusage(RUSAGE_SELF).ru_maxrss returns the maximum RSS over the process's lifetime, reflecting the true memory ceiling and helping developers identify in advance which processing step is most dangerous on a 2GB machine.
gc.collect() is Python's manual trigger for the garbage collector. Python uses reference counting as its primary memory management mechanism, supplemented by cyclic garbage collection; when processing large images, cyclic references and unreleased temporary objects appear easily. In low-memory environments, actively calling gc.collect() after each asset forces these objects to be reclaimed, preventing memory from slowly accumulating until a crash. As for "one subprocess per heavy rendering step," this leverages the operating system's behavior of automatically reclaiming all of a child process's memory when it exits, ensuring memory is returned to the system rather than lingering in the parent process's heap.
Packaging Deserves Its Own Scripts Too
Two repeatedly stalling steps were also scripted: md2pdf.py converts Markdown into a styled PDF supporting headings, tables, lists, and links; pack_product.py packages files and generates a 1280x720 cover and a 600x600 thumbnail, depending on Pillow (one of only two third-party dependencies in the whole toolkit). The author notes that a zip with a cover is what actually sells, and hand-making covers for 39 products isn't realistic.
Folder structure matters too:
project/
assets/ raw output
output/ files to submit + metadata.csv
packs/ finished zips + covers
research/ notes and sources
scripts/ tools
He especially reminds you to keep the research/ directory — he once cleaned up old scraping logs and notes, only to urgently need those materials for a later product. A hard-learned lesson.
Pillow is Python's most commonly used image processing library, supporting opening, resizing, cropping, format conversion, and text drawing. pack_product.py uses it to generate 1280x720 covers and 600x600 thumbnails, a typical batch image post-processing task. 1280x720 is the 16:9 cover ratio commonly used by video platforms and most product listings, while 600x600 is the square thumbnail spec common on e-commerce and stock platforms.
"A zip with a cover is what actually sells" reflects the display logic of the digital product market: the first thing a buyer sees in a product listing is the cover image, and a zip without one lacks visual credibility, significantly lowering click-through rates. Hand-making covers for 39 products means opening image software, adjusting dimensions, and exporting 39 times — time-consuming and prone to inconsistent specs. Scripted packaging merges cover generation and zip compression into one repeatable step, ensuring uniform cover specs across every product pack and making future batch updates easy.
Use Cases and How to Get It
This set of scripts is the packaging layer of a larger pipeline, backed by roughly 19,000 lines of pipeline code and 300+ pages of research on platform review rules. The author extracted four scripts that solve real bottlenecks into a standalone toolkit. For stock contributors tired of hand-writing CSVs late at night, it offers a low-barrier path to automation. Toolkit download link: Get vector prompt pack.
For more digital product seller guides, visit: Free guides & kits for digital product sellers.
Related articles

JobRunner MCP Server: AI Agents Can Now Fix Failed Java Jobs
JobRunner launches an open-source MCP server enabling AI agents to view cluster status, diagnose failures, and recover Java jobs—incident response from any cloud coding environment or existing agent.

MLAI Digital Launches 9 Security Agents: MCP Server Safeguards the Full Lifecycle of Agentic AI
MLAI Digital launches Security MCP Server with nine built-in specialized security agents covering vulnerability discovery, attack simulation, prompt injection detection, API and infrastructure assessment, and accelerated remediation—embedding security validation across the full Agentic AI lifecycle from development to production.

Running Isaac Lab on H100 GPUs: Exploring Better Approaches
Exploring optimization methods for running Isaac Lab on NVIDIA H100 GPUs, including MIG configuration, mixed precision, TensorRT acceleration, and community experience to improve robotics simulation and reinforcement learning efficiency.