Introduction to generating DDEX file using Python

Learn what DDEX files are and how to generate them using Python. Covers ERN, DSR, and RIN standards for digital music data exchange in the industry.
Introduction to generating DDEX file using Python

Key Takeaways

DDEX ERN files can be generated with Python's built-in xml.etree.ElementTree module.
A free DPID is required to identify the sender; no DDEX membership needed to obtain one.
Version compatibility varies by platform: YouTube supports 3.4-3.8, Facebook requires 3.8.3.
Always validate generated XML against official XSD schemas before submitting to any DSP.

What is a DDEX file?

DDEX (Digital Data Exchange) is a set of international standards for exchanging digital media data in the music industry. These XML-based standards ensure that information about releases, licensing, recordings, and sales flows accurately between creators, distributors, and retailers.

ERN (Electronic Release Notification)
Communicates release information — albums, singles, tracklists, and contributors.
DSR (Digital Sales Reporting)
Reports sales and usage data for digital music releases across platforms.
RIN (Recording Information Notification)
Exchanges details about the recording process, contributors, and technical specs.
MLC (Musical Works Licensing Creator)
Supports licensing data exchange between publishers and licensing agencies.

The most common use case today is proving rights to a piece of music (clearance) or sending takedown messages when content is used without permission. Different content providers support different DDEX versions — YouTube supports 3.4 to 3.8, Facebook supports 3.8.3, and the newest version is 4.2.

How to Generate a DDEX File Using Python

The simplest approach is to start from an existing DDEX file and create a template. Octolivery is one of the few DDEX file generators available.

To fill the template, you'll need:

  • A link to the already-published music or video
  • Your DPID (free to obtain — no DDEX membership required)
  • The DDEX ID of the recipient company
  • Track data: name, duration, artists, compositor, etc. (per scene for music videos)

Basic Example: ERN XML with Python

Here's a minimal example using Python's built-in xml.etree.ElementTree to create a DDEX ERN file:

import xml.etree.ElementTree as ET

# Create root element
root = ET.Element("ern:NewReleaseMessage", {
    "xmlns:ern": "http://ddex.net/xml/ern/43",
    "MessageSchemaVersionId": "ern/43",
})

# Message header
header = ET.SubElement(root, "MessageHeader")
ET.SubElement(header, "MessageId").text = "MSG-2023-001"
ET.SubElement(header, "MessageCreatedDateTime").text = "2023-12-07T10:00:00Z"

sender = ET.SubElement(header, "MessageSender")
ET.SubElement(sender, "PartyId").text = "PADPIDA2023010101A"
ET.SubElement(sender, "PartyName").text = "My Label"

recipient = ET.SubElement(header, "MessageRecipient")
ET.SubElement(recipient, "PartyId").text = "PADPIDA2020121501Y"
ET.SubElement(recipient, "PartyName").text = "YouTube"

# Resource list
resource_list = ET.SubElement(root, "ResourceList")
sound_recording = ET.SubElement(resource_list, "SoundRecording")
ET.SubElement(sound_recording, "ResourceReference").text = "A1"

details = ET.SubElement(sound_recording, "SoundRecordingDetailsByTerritory")
title = ET.SubElement(details, "Title")
ET.SubElement(title, "TitleText").text = "My Song Title"
ET.SubElement(details, "Duration").text = "PT3M45S"

# Write to file
tree = ET.ElementTree(root)
ET.indent(tree, space="  ")
tree.write("release.xml", xml_declaration=True, encoding="UTF-8")

print("DDEX ERN file generated: release.xml")

More Complete Example: With Artists and Deal Terms

A real-world DDEX file includes artist details, ISRC codes, and deal terms. Here's a more complete version:

import xml.etree.ElementTree as ET
from datetime import datetime


def create_ern_release(track_data: dict, sender: dict, recipient: dict) -> ET.Element:
    """Generate a DDEX ERN 4.3 NewReleaseMessage."""
    root = ET.Element("ern:NewReleaseMessage", {
        "xmlns:ern": "http://ddex.net/xml/ern/43",
        "MessageSchemaVersionId": "ern/43",
    })

    # Header
    header = ET.SubElement(root, "MessageHeader")
    ET.SubElement(header, "MessageId").text = f"MSG-{datetime.now():%Y%m%d%H%M%S}"
    ET.SubElement(header, "MessageCreatedDateTime").text = datetime.now().isoformat()

    msg_sender = ET.SubElement(header, "MessageSender")
    ET.SubElement(msg_sender, "PartyId").text = sender["dpid"]
    ET.SubElement(msg_sender, "PartyName").text = sender["name"]

    msg_recipient = ET.SubElement(header, "MessageRecipient")
    ET.SubElement(msg_recipient, "PartyId").text = recipient["dpid"]
    ET.SubElement(msg_recipient, "PartyName").text = recipient["name"]

    # Resource list
    resource_list = ET.SubElement(root, "ResourceList")
    recording = ET.SubElement(resource_list, "SoundRecording")
    ET.SubElement(recording, "ResourceReference").text = "A1"

    sr_id = ET.SubElement(recording, "SoundRecordingId")
    ET.SubElement(sr_id, "ISRC").text = track_data["isrc"]

    details = ET.SubElement(recording, "SoundRecordingDetailsByTerritory")
    title_el = ET.SubElement(details, "Title")
    ET.SubElement(title_el, "TitleText").text = track_data["title"]
    ET.SubElement(details, "Duration").text = track_data["duration"]

    # Artists
    for artist in track_data.get("artists", []):
        contributor = ET.SubElement(details, "DisplayArtist")
        name_el = ET.SubElement(contributor, "PartyName")
        ET.SubElement(name_el, "FullName").text = artist["name"]
        ET.SubElement(contributor, "ArtistRole").text = artist.get("role", "MainArtist")

    # Release list
    release_list = ET.SubElement(root, "ReleaseList")
    release = ET.SubElement(release_list, "Release")
    release_id = ET.SubElement(release, "ReleaseId")
    ET.SubElement(release_id, "ICPN").text = track_data.get("upc", "")

    release_detail = ET.SubElement(release, "ReleaseDetailsByTerritory")
    ET.SubElement(release_detail, "TerritoryCode").text = "Worldwide"

    # Deal list
    deal_list = ET.SubElement(root, "DealList")
    deal = ET.SubElement(deal_list, "ReleaseDeal")
    deal_terms = ET.SubElement(deal, "Deal")
    ET.SubElement(deal_terms, "CommercialModelType").text = "SubscriptionModel"
    ET.SubElement(deal_terms, "Usage").text = "OnDemandStream"

    validity = ET.SubElement(deal_terms, "ValidityPeriod")
    ET.SubElement(validity, "StartDate").text = track_data.get("release_date", "")

    return root


# Usage
track = {
    "title": "Ocean Waves",
    "isrc": "USRC12345678",
    "duration": "PT4M12S",
    "upc": "0123456789012",
    "release_date": "2024-01-15",
    "artists": [
        {"name": "Jane Doe", "role": "MainArtist"},
        {"name": "John Smith", "role": "FeaturedArtist"},
    ],
}

ern = create_ern_release(
    track_data=track,
    sender={"dpid": "PADPIDA2023010101A", "name": "My Label"},
    recipient={"dpid": "PADPIDA2020121501Y", "name": "YouTube"},
)

tree = ET.ElementTree(ern)
ET.indent(tree, space="  ")
tree.write("release_complete.xml", xml_declaration=True, encoding="UTF-8")
print("Complete DDEX ERN file generated.")
Handle tracks with multiple artists carefully — ensure each artist element is properly structured with the correct role. This is a common source of validation errors.

What to Pay Attention To

  • DDEX for takedowns uses a different message structure with serious legal consequences — handle separately
  • Version compatibility varies by platform (YouTube: 3.4–3.8, Facebook: 3.8.3, newest: 4.2)
  • Validation is essential — always validate generated XML against the official XSD schemas before submission
  • This guide covers single music files only. Videos and other media types require different approaches and more detailed metadata

Why Use DDEX?

DDEX standards are widely adopted because they solve real problems: interoperability between systems, automated workflows that reduce manual errors, and consistent data formats that ensure artists and rights holders get paid accurately. Most major digital music platforms require DDEX compliance for content delivery.

Let's Build Something Together

Have a similar project in mind? We'd love to hear about it.

Get in touch to discuss how we can help bring your vision to life.