[KongchangAI]
· 2 min read· 1,336 words

Why Adobe Stock Keeps Rejecting Your Metadata CSV Headers — and How to Fix It

Why Adobe Stock Keeps Rejecting Your Metadata CSV Headers — and How to Fix It

An asset creator uses scripts to generate platform-compliant CSV headers and an idempotent ledger to avoid batch upload rejections and duplicate submissions.

After having his first 40 files rejected by Adobe Stock, a vector asset creator discovered the real problem wasn't the data but the CSV header format. Adobe requires a strict five-column order, Vecteezy requires 4 columns, and Dreamstime requires 15 — making manual spreadsheet maintenance error-prone across multiple platforms. He switched to a Python script, make_metadata.py, to generate headers per platform spec, eliminating spelling and column-count errors entirely. He then hit the duplicate-upload trap and introduced upload_tracker.py, a plain-text ledger of published assets enabling idempotent reruns. The whole pipeline runs on a 1 vCPU, 2 GB, GPU-less server, where metadata generation costs almost nothing — the real bottleneck is image processing. He distilled a four-item pre-upload checklist and packaged the scripts into a Pipeline Starter Kit.

Hand-assembled CSV files often get rejected at the very first line, yet the error messages point to the data itself — which makes debugging unusually painful. After having his entire first batch of 40 files rejected, one vector asset creator figured out that header formatting is the real gatekeeper for metadata uploads.

Adobe Stock metadata CSV headers rejected instantly

The Header Is Adobe's First Filter

Adobe Stock's uploader requires a CSV with exactly five columns, in this fixed order: Filename,Title,Keywords,Category,Releases. The author initially added a Description column based on intuition, and the entire batch was rejected at the parsing stage — with no human review ever involved.

Adobe isn't uniquely strict here. According to the author's testing across multiple platforms, Vecteezy requires a 4-column format and Dreamstime requires 15 columns. Three platforms, three different header specs, zero tolerance. Anyone maintaining spreadsheets by hand will eventually get a column name or column count wrong, and the failure mode is especially confusing because error messages usually point at data rows rather than the real header problem.

The design logic is actually straightforward: the platform's parser establishes a structural contract the moment it reads the first row. If column names don't match or the column count is off, every subsequent field mapping goes out of alignment. Rather than produce unreliable import results, the platform simply rejects at the entrance. There's no partial credit and no "close enough."

CSV (Comma-Separated Values) is a plain-text format for tabular data, where each row represents a record, fields are separated by commas, and the first row typically serves as the header declaring column names. It has no enforced schema definition and no type validation, which makes it extremely sensitive to structural errors.

In the context of asset platform uploads, CSV is a "contract-style" input: the parser first reads the header and builds a mapping from column names to internal fields. Once column spelling, capitalization, or order deviates from spec, every subsequent row gets misaligned or fails to parse entirely. This is similar to a database schema constraint — if the table structure is wrong, the inserted data is meaningless.

And precisely because CSV carries no self-describing type information, the platform can't be tolerant the way JSON or XML can with field names. It's forced into a strict "all correct or all rejected" policy. That's why header errors get intercepted at the first line, while error messages often point at data rows — creating a debugging nightmare.

Generate Headers with a Script, Not by Hand

The author's solution was to abandon hand-written CSVs entirely and use make_metadata.py to generate headers according to each platform's spec. The script relies only on the Python standard library — no extra dependencies required:

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

Three commands map to three correct header sets, eliminating the possibility of typos at the source. By default the script converts filenames into title case, which works fine for a first-pass review, but before final submission you should swap in pre-prepared titles and keywords via --titles titles.json.

The key design principle: the headers the script writes come from official platform specs verified through actual review behavior — not column names the creator invented. This matters more than the script's implementation itself. The tool's value lies in turning "I think it's these columns" into "the platform requires exactly these columns."

Duplicate Uploads: The Second Instant-Rejection Trap

Once the headers were fixed, the author immediately hit another pitfall. To fix a single bad file, he reran the entire batch's upload command — and the same batch of assets got submitted twice. Duplicate uploads are one of the fastest ways to get an account flagged, and batch tools make this kind of mistake incredibly easy.

So a ledger mechanism was added to the pipeline. upload_tracker.py records published assets in a plain-text file:

python upload_tracker.py pending items.txt   # items not yet completed
python upload_tracker.py done "asset-001.jpg"
python upload_tracker.py status

On rerun, entries marked as done are automatically skipped. The tool is just a few dozen lines of standard-library Python plus a plain-text file, but the author says it has prevented at least two account-flagging-level mistakes. For any batch submission scenario, idempotency isn't optional — the cost of duplicate submission far exceeds the ten minutes needed to build a deduplication check.

Idempotency is a core concept in distributed systems and batch processing: the idea that running an operation once versus multiple times produces the same effect on system state. In the asset upload context, idempotency means that no matter how many times you rerun an upload command, the same asset never gets submitted twice.

Batch scripts naturally tend to break idempotency: rerunning an entire batch to fix one failed file pushes already-successful files back into the queue. Platforms typically treat duplicate uploads as abuse, because they consume review resources, disrupt inventory management, and can trigger account risk controls.

The common way to achieve idempotency is maintaining a "processed ledger" — exactly what upload_tracker.py does: recording completed assets in a plain-text file, and on rerun querying the ledger and skipping entries that already exist. This approach is identical in spirit to database upserts, message queue deduplication IDs, and incremental builds in CI/CD. For any batch submission task, idempotent design is the first line of defense against irreversible side effects.

Why Metadata Must Be Built with Code

Some peers argue that a spreadsheet is good enough. The author concedes that at a scale of 10 files per batch, it truly is. But once a batch reaches 100 files across three platforms simultaneously, manual CSV maintenance stops being an efficiency issue and becomes a reliability issue.

His parent project is roughly 19,000 lines of pipeline code, which has packaged 39 finished products totaling 856 MB of output. If every upload required opening a spreadsheet and praying nothing goes wrong, none of that output would have been possible.

The entire system runs on a server with 1 vCPU, 2 GB of RAM, and no GPU. Metadata generation costs almost nothing computationally, so it can run in any environment. The real resource hog is image processing, and the author's habits include: running only one process at a time, calling gc.collect() between assets, measuring peak memory with resource.getrusage, and rendering at only the resolution actually needed. Checking previews at 60 dpi instead of 300 dpi is the difference between finishing the work within 2 GB of RAM and falling into swap death.

Pre-Upload Checklist

The author now runs the following four checks before every upload:

  1. Headers match the platform exactly — Adobe 5 columns, Vecteezy 4 columns, Dreamstime 15 columns.
  2. Titles and keywords contain real content, not the default filename-to-title conversion.
  3. upload_tracker.py status confirms the current batch has no previously published assets.
  4. Files are in the output/ directory, the metadata CSV is regenerated from that directory, and it's never hand-edited afterward.

This checklist came from three rejections that could have been avoided. The author has accumulated over 300 pages of platform-rule research in his notes, but the metadata header is what bit everyone first — because it fails the upload before the work ever gets seen.

The Toolkit Distilled from This Experience

All of the above has been packaged into the Pipeline Starter Kit, containing four runnable scripts (make_metadata.py, upload_tracker.py, md2pdf.py, pack_product.py), a directory structure explanation, and small-server considerations. The author emphasizes that these are the same scripts that produced those 39 products — not versions rewritten for a tutorial. For creators building their own pipelines, the value of this toolkit is in skipping the inevitable rejection emails.

Automating asset uploads isn't about showing off — it's about removing repetitive human error from the process. Headers, deduplication, memory management — every one of these can, at some point, turn into account risk or wasted time. Handling them with code buys you a predictable output rhythm.

#Adobe Stock CSV header#stock metadata upload#Vecteezy CSV format#Dreamstime metadata#Python CSV generation#idempotent upload#asset pipeline automation
Share:

Related articles