eyeD3: a long-lived Python toolkit for reading and writing MP3 ID3 tags
eyeD3 is a Python module and command line program for processing ID3 tags. Information about mp3 files (i.e bit rate, sample frequency, play time, etc.) is also provided. The formats supported are ID3v1 (1.0/1.1) and ID3v2 (2.3/2.4).
At a glance
- What is it?
- eyeD3 is a Python library and command line program for working with ID3 metadata in MP3 files, supporting ID3v1, v2.2 read only, v2.3 and v2.4, with a plugin architecture for output formats, cover art and collection statistics. It has been maintained by one developer since 2002 and its release naming still uses album titles.
- Who is it for?
- eyeD3 suits a Python developer who needs programmatic control over ID3 tags, especially across a catalogue where tag consistency matters, because the library exposes the tag object directly and the plugin system covers display formats the core does not.
- Can I use it commercially?
- Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
- Is it still maintained?
- Yes. The repository last received commits 150 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Two front ends over one tag model
eyeD3 is a Python tool for working with audio files, specifically MP3 files containing ID3 metadata, and it ships as two things: a command-line tool called eyeD3 and a Python library you import as eyed3.
The README is explicit about why both exist. The library is for writing your own applications or plugins that are callable from the command line tool. So the plugin system is not an add-on bolted onto a CLI, it is the reason the library exists, and the CLI is one consumer of it.
The command line side is plugin driven too. The tag editing and viewing operations are implemented as a plugin, and it is the default plugin when no other is specified. The README describes the interface as the standard interface that eyeD3 has always had in the past, which tells you the CLI surface has been deliberately stable across a long life.
The set of bundled plugins covers the things people actually want from a tagger. There are plugins for tag to string formatting for display, for album fixing, for cover art downloading, for collection statistics, and for json, yaml, jabber and nfo output formats, and more included. The output format plugins are the interesting design choice here: rather than adding a flag for every machine-readable format, the tool has a plugin contract that formats can plug into.
The project is written and maintained by Travis Shirk, and the licence is version 3 of the GPL. The homepage is eyed3.nicfit.net, the primary language is Python, and the default branch is 0.9.x, which is worth noticing: the branch name tracks the release line rather than being called main.
Reading and writing a tag from the command line
The quick example in the README sets four properties at once, which is the shape of the whole command line interface: short flags, a file at the end.
eyeD3 -a Integrity -A "Humanity Is The Devil" -t "Hollow" -n 2 song.mp3That sets the artist with -a, the album with -A, the title with -t and the track number with -n, all in the ID3 tag of the file.
Reading the tag back is the same tool with no options at all.
eyeD3 song.mp3The output the README shows is a plain text listing with the ID3 version at the top, then title, artist, album, album artist and track, each on its own line. The album artist line showing None when it was never set is a small honesty about the difference between a field that is absent and one that is empty, and it is the kind of detail that matters when you are diffing tags across a catalogue.
The same work in Python is a load, a set of attribute assignments and a save.
import eyed3
audiofile = eyed3.load("song.mp3")
audiofile.tag.artist = "Token Entry"
audiofile.tag.album = "Free For All Comp LP"
audiofile.tag.album_artist = "Various Artists"
audiofile.tag.title = "The Edge"
audiofile.tag.track_num = 3
audiofile.tag.save()The difference between the two routes is the one that matters for library use. The command line maps flags onto the same tag attributes, but in Python you can compute the values, loop over a directory, and read back anything the tag object exposes. The album_artist attribute is the clearest illustration, because the CLI offers the field but the library is where you would set it programmatically across a whole release.
The library also gives you access to the audio properties rather than only the metadata. The tool exposes details such as play time, bit rate and sampling frequency for the MP3 format, which means a script can reject files whose bit rate is wrong or whose duration does not match what the catalogue expects, without opening a second library.
ID3v2.2 is readable but not writable, and that asymmetry is deliberate
The supported format list is worth reading closely: ID3 versions 1.x, 2.2 read-only, 2.3 and 2.4.
The parenthetical is the important part. ID3v2.2 is supported for reading only. The library can pull information out of an old 2.2 tag and can write 2.3 and 2.4, so you can migrate a legacy file forward but you cannot produce a 2.2 tag.
That is the right asymmetry. ID3v2.2 was the short frame version, and nothing that needs to be written today needs it. Reading it is what matters for archival work and for tooling that has to cope with what is actually in a collection, and writing it is not a capability anyone is missing.
On the audio side the scope is MP3, and the README states the intent to go further: an abstract design allowing future support for different audio formats and metadata containers. The repository layout backs that up. The setuptools package list names eyed3, eyed3.id3, eyed3.mp3, eyed3.plugins and eyed3.utils, so the ID3 handling, the MP3 handling and the plugin machinery are separate modules rather than one file.
The dependencies are correspondingly thin. The runtime list is filetype and deprecation, and nothing else. filetype is there for identifying the file type by content rather than by extension, which matters when you are processing a directory of files whose names cannot be trusted, and deprecation is used to mark parts of the API that are on their way out.
The Python floor is 3.10, with requires-python set to >=3.10,<4.0, and the classifiers cover 3.10 through 3.14. The operating system classifier is POSIX, which is a signal rather than a restriction, but it is the one thing to check if you plan to run this in a Windows pipeline.
Optional extras decide what the plugins can actually do
The packaging shows the plugin system carried through into extras, and this is where the operational detail lives.
The yaml-plugin extra pulls in PyYAML, so the yaml output format plugin needs it. The art-plugin extra pulls in Pillow, pylast and requests, so the cover art downloading plugin needs all three: Pillow to write the image, requests to fetch it and pylast to talk to Last.fm.
That is a deliberate separation. A user who only wants to read and write tags does not want Pillow in their environment, and a user who wants cover art should not have to install it to get the core tool. The extras are declared in pyproject.toml as optional-dependencies entries named yaml-plugin and art-plugin, so installation is a matter of naming the extra.
The core install has no third-party requirements beyond filetype and deprecation. The project metadata names it a Python audio data toolkit with the description of ID3 and MP3, and the keywords are id3, mp3 and python.
The test extra is a good sign about how the project is checked. It includes pytest, coverage, tox, factory-boy, pytest-cov, check-manifest and flake8, and the check-manifest dependency in particular means the packaging is verified against the source tree, which catches the class of bug where a module exists in the repository but is missing from the distributed package. The classifiers list both End Users/Desktop and Developers as intended audiences, and the topics include the Sound/Audio/Editors category alongside Python Modules.
The console entry point is registered as eyeD3 = eyed3.main:_main, which means installing the package puts an eyeD3 command on your path, and the top level eyed3 package is what you import.
The Makefile, the release names and a two decade project
Two details in the build files say more about this project than any feature list.
The first is the release naming. The pyproject.toml carries a release_name of Guilty Conscience, and the release history follows the same convention, with v0.9.9 named Guilty Conscience, v0.9.8 Armed & Dangerous and v0.9.7 Sunshine. The Makefile reads that name out of pyproject.toml with the same sed pattern it uses for the project name and the version, and builds the release tag as v plus the version. So every release of eyeD3 is named after a song, and the packaging tooling knows it.
The consequence for anyone automating a release is that the version and the release name are separate fields and both have to be right. The Makefile enforces this: it greps the name and the version out of pyproject.toml and calls an error if either comes back empty. A build with a missing version stops rather than publishing something mislabelled.
The second detail is the Makefile's own configuration, which is unusually user-facing. PYTEST_ARGS defaults to ./tests and the help text says that if it is defined, PDB options are added when pytest is invoked. PYPI_REPO selects the package index, pypi by default. BUMP defaults to prerelease, which tells you the release flow distinguishes prereleases from finals. TEST_DATA_DIR points at the tests directory. And CC_MERGE and CC_OPTS control the cookiecutter merging used for the project template, which is what the .cookiecutter.yml at the repository root is for.
The default goal is build, and the all target runs clean, build and test in sequence. The repository also carries tox.ini for multi environment testing, a .readthedocs.yaml for the documentation build, HISTORY.rst as the changelog, CONTRIBUTING.rst, AUTHORS.rst and an etc/ directory.
The timing is worth stating plainly. The last push was on 2026-05-26, and the three most recent releases are v0.9.9 from 2025-11-16, v0.9.8 from 2025-04-26 and v0.9.7 from 2025-04-19. This is a mature project on a steady release cadence rather than something that changes weekly.
Where eyeD3 is the wrong tool
The first wrong-tool case is a project that has moved to a modern metadata container. ID3 is an MP3-era format, and the newest version the tool supports is 2.4. If your catalogue is FLAC, Ogg Vorbis, MP4 or anything else, the tags you need are not ID3 tags and this tool has nothing to say about them. The README's abstract design note about future support for other formats is an intention, not a shipped capability, and the package layout shows the scope is still eyed3.id3 and eyed3.mp3.
The second case is a team that wants tags written correctly at scale on every platform. The packaging classifiers list POSIX as the operating system, and while eyeD3 itself is pure Python, the practical risk on Windows is not the library but the file semantics around rewriting a file that a player may have open. Batch tag writing across a large music library is also where you need conflict handling for duplicate files and partial writes, and the README does not describe any of that.
The third case is a user who wants the tags to be clean rather than just present. eyeD3 sets what you tell it to set. It does not deduplicate, does not normalise case across a release, does not fix a mislabelled year or infer missing album artists from a folder structure. There is a fixup plugin for album fixing, but the point stands that the tool does the mechanical work, not the editorial one.
The fourth case is somebody expecting the API to be stable in the way a modern library is. The runtime dependency on deprecation is the visible signal that some parts of the API are being retired, and a project with a 0.x version number is by convention allowed to break. Pinning a version is the practical answer, and the release names make it easy to remember which one you pinned.
What you get in exchange is a tag object you can drive from Python, a CLI with a stable flag vocabulary, and a plugin contract that already has display, statistics, cover art and several machine-readable output formats. For a Python codebase that has to write tags for a catalogue it controls, that combination is harder to assemble from parts than it looks.
Editorial conclusion
eyeD3 suits a Python developer who needs programmatic control over ID3 tags, especially across a catalogue where tag consistency matters, because the library exposes the tag object directly and the plugin system covers display formats the core does not. It does not suit someone who needs a modern container format, since the supported metadata standards stop at ID3v2.4 and the audio format at MP3, and it does not suit a team that needs tags written at scale on Windows without checking the POSIX-only classifier. Before adopting, install the optional extras you actually need, since the art plugin pulls in Pillow, pylast and requests, and confirm the eyeD3 --help output lists the plugins your workflow expects.
Frequently asked questions
What is eyeD3?
eyeD3 is a Python tool for working with audio files, specifically MP3 files containing ID3 metadata. It provides a command-line tool called eyeD3 and an importable library, and it supports ID3 versions 1.x, 2.2 read-only, 2.3 and 2.4.
How do I set ID3 tags with the eyeD3 command line?
Pass short flags followed by the file, for example eyeD3 -a Integrity -A "Humanity Is The Devil" -t "Hollow" -n 2 song.mp3, which sets the artist, album, title and track number. Running eyeD3 with no options on the file prints the tag back.
How do I use eyeD3 from Python?
Load the file with eyed3.load, assign to attributes on audiofile.tag such as artist, album, album_artist, title and track_num, then call audiofile.tag.save(). The library is also the mechanism for building plugins that the command line tool can call.
What Python version does eyeD3 need?
Python 3.10 or newer is required. The pyproject.toml sets requires-python to >=3.10,<4.0, and the classifiers list Python 3.10 through 3.14, with POSIX as the operating system.
What license is eyeD3 released under?
eyeD3 is licensed under version 3 of the GPL, with the LICENSE file at the root of the repository and a GPLv3 classifier in the package metadata. The project is written and maintained by Travis Shirk.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/nicfit-eyed3)Was this useful?