Digital Files Management

Overview

The defining feature of digital products is the downloadable file. This chapter covers:

Digital File Basics

Each digital listing can have multiple downloadable files. When a customer purchases, they get access to download all files attached to that listing.

File Limitations

Constraint Limit
Max file size 20 MB per file
Max files per listing 5 files
Allowed formats Most common formats (PDF, PNG, JPG, ZIP, etc.)

For larger files, you’ll need to:

Uploading Files

Upload a Single File

def upload_digital_file(client, shop_id, listing_id, file_path, rank=1):
    """Upload a digital file to a listing."""
    with open(file_path, "rb") as f:
        response = client.post(
            f"/application/shops/{shop_id}/listings/{listing_id}/files",
            files={"file": f},
            data={"rank": rank, "name": file_path.name}
        )
    return response

The rank parameter determines download order (1 = first).

Upload Multiple Files

from pathlib import Path

def upload_all_files(client, shop_id, listing_id, file_paths):
    """Upload multiple files to a single listing."""
    results = []
    for rank, path in enumerate(file_paths, start=1):
        path = Path(path)
        if not path.exists():
            results.append({"file": str(path), "status": "not found"})
            continue
            
        try:
            result = upload_digital_file(client, shop_id, listing_id, path, rank)
            results.append({"file": path.name, "status": "uploaded"})
        except Exception as e:
            results.append({"file": path.name, "status": "error", "error": str(e)})
    
    return results

Listing Existing Files

def list_digital_files(client, shop_id, listing_id):
    """Get all digital files for a listing."""
    response = client.get(
        f"/application/shops/{shop_id}/listings/{listing_id}/files"
    )
    return response["results"]

Returns:

[
    {
        "listing_file_id": 123456,
        "listing_id": 1234567890,
        "rank": 1,
        "filename": "planner-2024.pdf",
        "filesize": "2.5 MB",
        "create_timestamp": 1699900000
    }
]

Deleting Files

def delete_digital_file(client, shop_id, listing_id, listing_file_id):
    """Remove a digital file from a listing."""
    client.delete(
        f"/application/shops/{shop_id}/listings/{listing_id}/files/{listing_file_id}"
    )

Replacing Files (Update Product)

When you update a product, you need to replace the file. Unfortunately, Etsy doesn’t have a direct “replace” endpoint—you must delete and re-upload:

def replace_digital_file(client, shop_id, listing_id, new_file_path):
    """Replace all files for a listing with a new file."""
    # 1. Get existing files
    existing = list_digital_files(client, shop_id, listing_id)
    
    # 2. Delete old files
    for file in existing:
        delete_digital_file(client, shop_id, listing_id, file["listing_file_id"])
    
    # 3. Upload new file
    return upload_digital_file(client, shop_id, listing_id, new_file_path)

⚠️ Important: Customers who already purchased retain access to the original files. New downloads get the updated files.

Practical Workflows

Batch File Upload for New Products

See code/digital_files.py for complete implementation.

def create_listing_with_files(client, shop_id, listing_data, file_paths):
    """Create a new listing and upload its files."""
    # 1. Create the listing
    listing = client.post(
        f"/application/shops/{shop_id}/listings",
        data=listing_data
    )
    listing_id = listing["listing_id"]
    
    # 2. Upload files
    for rank, path in enumerate(file_paths, 1):
        upload_digital_file(client, shop_id, listing_id, path, rank)
    
    return listing_id

Update Files Across Multiple Listings

If you have a common file (like a license or bonus) across listings:

def update_common_file_all_listings(client, shop_id, old_filename, new_file_path):
    """Replace a specific file across all listings that have it."""
    listings = get_all_listings(client, shop_id)
    updated = []
    
    for listing in listings:
        files = list_digital_files(client, shop_id, listing["listing_id"])
        
        for file in files:
            if file["filename"] == old_filename:
                # Delete old, upload new
                delete_digital_file(
                    client, shop_id, listing["listing_id"], file["listing_file_id"]
                )
                upload_digital_file(
                    client, shop_id, listing["listing_id"], new_file_path, file["rank"]
                )
                updated.append(listing["listing_id"])
    
    return updated

File Organization Best Practices

Naming Conventions

Use clear, consistent file names:

def format_filename(product_name, variant=None, extension="pdf"):
    """Create a clean, customer-friendly filename."""
    name = product_name.lower().replace(" ", "-")
    if variant:
        name = f"{name}-{variant}"
    return f"{name}.{extension}"

# Examples:
# monthly-planner.pdf
# monthly-planner-a4.pdf
# monthly-planner-letter.pdf

ZIP Files for Multiple Formats

When offering multiple formats, consider ZIPping them:

import zipfile
from pathlib import Path

def create_bundle_zip(files, output_name):
    """Create a ZIP bundle from multiple files."""
    zip_path = Path(output_name)
    with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zf:
        for file in files:
            zf.write(file, Path(file).name)
    return zip_path

README/Instructions File

Include a text file with instructions:

README_TEMPLATE = """
Thank you for your purchase!

FILES INCLUDED:
{file_list}

HOW TO PRINT:
1. Open the PDF file
2. Set print size to 100% (Actual Size)
3. Use high-quality paper for best results

NEED HELP?
Contact me through Etsy messages.

Terms: Personal use only. Do not redistribute.
"""

Handling Large Files

If your files exceed 20MB:

Option 1: Compression

import zipfile

def compress_file(input_path, output_path=None):
    """Compress a file using maximum ZIP compression."""
    input_path = Path(input_path)
    output_path = output_path or input_path.with_suffix('.zip')
    
    with zipfile.ZipFile(output_path, 'w', zipfile.ZIP_DEFLATED, compresslevel=9) as zf:
        zf.write(input_path, input_path.name)
    
    return output_path, output_path.stat().st_size

Option 2: Split Files

For very large products, create multiple listings or use external delivery with a link file.

Verifying File Uploads

After uploading, verify files are attached:

def verify_listing_files(client, shop_id, listing_id, expected_count):
    """Verify the correct number of files are attached."""
    files = list_digital_files(client, shop_id, listing_id)
    actual_count = len(files)
    
    if actual_count != expected_count:
        return {
            "status": "mismatch",
            "expected": expected_count,
            "actual": actual_count,
            "files": [f["filename"] for f in files]
        }
    return {"status": "ok", "files": [f["filename"] for f in files]}

Common File Issues

“File too large”

“Invalid file type”

“Upload failed”

What’s Next

With listings created and files uploaded, the next chapter covers handling orders—what happens after customers purchase your digital products.


← Previous: Creating Listings Next: Orders & Fulfillment →

← Back to Table of Contents