# ytmusicapi: a borrowed cookie session, one runtime dependency, and two linters disagreeing

> An unofficial Python library that talks to YouTube Music by emulating the web client and authenticating with the user's own cookie data, covering browsing, library management, playlists, podcasts and uploads. The packaging is unusually lean, with a single runtime dependency and a version taken from git tags, and the type checker is configured strictly while the linter beside it is told to ignore undefined names.

**sigma67/ytmusicapi** — Unofficial API for YouTube Music

- Repository: https://github.com/sigma67/ytmusicapi
- Website: https://ytmusicapi.readthedocs.io
- Stars: 3,050 · Forks: 353
- Language: Python
- License: MIT
- Published: 2026-09-24 · Updated: 2026-09-24 · Language: en
- Canonical page: https://hysenlabs.com/projects/sigma67-ytmusicapi

## Authentication is a borrowed browser session, not a key

The library describes itself in two sentences, and the second is the important one: it does not use an API key or a developer project, it emulates the web client requests of the music site and authenticates using the user's own cookie data. That single design choice explains most of what else is true about the project, including why the readme points at an FAQ and an issue tracker rather than at an official support channel, and why the file carries a badge for how many commits have landed since the last release. The usage example constructs the client with a file path, and the name it passes looks like an OAuth credential file, while the description says cookies are what is used; the visible readme does not explain the relationship between the two, so read the setup documentation before assuming which one you need.

## The shortest example creates and edits a real playlist

The usage section is four lines of Python and two of them write:

```python
from ytmusicapi import YTMusic

yt = YTMusic('oauth.json')
playlistId = yt.create_playlist('test', 'test description')
search_results = yt.search('Oasis Wonderwall')
yt.add_playlist_items(playlistId, [search_results[0]['videoId']])
```

So a reader following the quick start end to end creates a playlist named for a test on their own account and then adds a search result to it. That is a reasonable demonstration of a write path the library needs, but it is worth knowing before pasting it. The readme then nominates the test suite as a source of usage examples, which is the better reference for anything beyond this. Two small things also stand out: the variable naming is camel case in a Python example, which is the opposite of what the project's own style tooling enforces elsewhere in the codebase.

## One runtime dependency, and the version number is not written down

The project metadata lists a single runtime dependency, the HTTP library, with a modest floor, and nothing else. That is about as small a surface as a library of this kind gets, and it is worth contrasting with the alternatives in the same ecosystem that pull in a client framework. The version, though, is declared dynamic, which means it is derived from the repository's own tags at build time by the versioning plugin rather than written in the file. That is tidy, and it also means a checkout without tags produces a development version, so anyone building from source needs tags present. The package data configuration is the other clue to the project's shape: it ships reStructuredText files, Python files and compiled message catalogues, which is how the sixteen supported languages get into the wheel.

## A strict type checker sits beside a linter told to ignore undefined names

The configuration block for the type checker is short and uncompromising: it covers the library directory, treats it as the path root, and turns on strict mode. Ten lines below it, the linter's configuration is explicit about what it will not report. Three of the five ignored codes concern names that do not exist or names pulled in wholesale from another module, and the list also waives the rule against assigning a lambda to a name and a rule about opening files by path. Ignoring undefined names in a codebase whose type checker is strict is a deliberate gap, and the usual reason for it is that the undefined names are real at runtime rather than missing, which is plausible in a library that reaches into optional attributes. It does mean a rename can pass the linter silently and be caught by the type checker instead, or by neither.

## The test suite retries twice and writes a report into the working tree

The test configuration does three things beyond finding and running tests. It retries, allowing two extra attempts with a five second pause between them, which is a sensible response to a suite that talks to a live third-party service and will occasionally be slow or rate limited. It writes a machine-readable report to a file in the project root. And it asks for timing output for every test, with no threshold, which produces a long report on a large suite. Coverage is configured to measure the library itself and to report to two decimal places, which is a detail that suggests the coverage number is read rather than scanned. Nothing here is wrong, but the retry count is the setting to lower if you want the suite to fail fast while developing.

## One classifier, and one capped development dependency

The published metadata is unusually spare in one place and careful in another. There is a single classifier, naming only the language, with no versions, no operating system and no topic. Everything else is spelled out properly: the required Python floor, the licence as both a name and a file, the author with a contact address, and links for the home page, documentation and repository. In the development dependencies, almost everything has a floor, and one does not: the documentation tool is capped below a major version. There is also a type stub package pinned to a specific dated build of the HTTP client's stubs, which is the kind of pin that ages quietly. The lockfile at the root belongs to a different tool from the build backend, so the project has a lock manager for development and a separate build system for distribution.

## The readme gives no install command at all

The requirements section states one thing: Python 3.10 or higher. The setup section that follows does not give a command, a package name to install, or a configuration step, and instead refers the reader to the documentation for detailed instructions. That is the largest gap in an otherwise thorough readme, because authentication is the hard part of this library and authentication is exactly what the readme does not explain. The features section is the most complete part: seven groups covering browsing, exploring music, library management, playlists, podcasts, uploads and localization, each itemised, with the localization entries stating that all regions are supported and sixteen languages are, both linking to FAQ pages that list the accepted values. The feature block is also wrapped in comment markers, so the same text is generated into the documentation rather than maintained twice.

## One badge advertises how far the branch is ahead of the release

Five badges sit at the top: package downloads, a chat room, code coverage, the latest release, and a count of commits since that release. The last one is unusual, and it is the badge that tells you how this project is actually maintained. Work continues on the default branch and is published to the package index without a tag being cut for every commit, which is consistent with a version derived from tags rather than from a file. The release history supports that reading: three recent versions, a few months apart, with the last push to the repository dated 2026-10-01 and the newest release from the middle of September. So if you are installing, pin the release you tested against; if you are tracking the default branch, expect the badge to be the honest measure of the gap.

## Conclusion

ytmusicapi is a reasonable choice if you want to script against your own YouTube Music account, since the coverage is broad and the runtime surface is one HTTP library, but two things decide whether it suits you. It has no API key and no official access; it borrows a logged-in browser session, so it breaks when the client changes and it inherits whatever terms your account is under. And the version you install is derived from a git tag rather than written down, so pin an explicit release instead of tracking the default branch. Read the tests as documentation too, since the readme points there for examples and the shortest example in it writes to your account.

## FAQ

### how to use ytmusicapi

Python 3.10 or higher is required. Construct the client with a path to your credential file, then call methods on it: the readme's example searches for a track, creates a playlist and adds a result to it. The setup section gives no install command and refers to the documentation, and the test suite is suggested as a further source of examples.

### How does ytmusicapi authenticate?

By emulating YouTube Music web client requests and using the user's own cookie data, rather than with an API key. The readme's usage example constructs the client with a file whose name suggests OAuth credentials, while the description says cookies are used, and the visible file does not explain the relationship between the two.

### What can ytmusicapi do?

Seven groups of operations: searching with all filters plus suggestions, artist and release information, user channels and playlists, albums, song metadata, watch playlists and lyrics, mood and genre playlists, charts globally and per country, library contents, rating and subscriptions, play history, playlist creation and editing, podcasts with episodes and channels, song uploads, and localization.

### Which regions and languages does ytmusicapi support?

All regions are supported, and sixteen languages are, with the readme linking to separate FAQ pages that list the accepted values for each. The compiled message catalogues shipped in the package data are what carry those languages inside the wheel.

### What does ytmusicapi depend on?

One runtime dependency, the HTTP library, at a modest floor. The version number itself is not written down: it is derived from git tags at build time. Development tooling includes a test runner with retries and coverage, a linter, a strict type checker, and a documentation tool capped below a major version.

### Does ytmusicapi have a command line tool?

The project metadata declares a console script named after the package, pointing at the main function of a setup module, so an executable is installed. The readme itself does not document that command and refers readers to the usage documentation instead.

## Sources

- [License: MIT](https://github.com/sigma67/ytmusicapi/blob/main/LICENSE)
- [Project website](https://ytmusicapi.readthedocs.io)
- [README](https://github.com/sigma67/ytmusicapi/blob/main/README.md)
- [Releases](https://github.com/sigma67/ytmusicapi/releases)
- [sigma67/ytmusicapi on GitHub](https://github.com/sigma67/ytmusicapi)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/sigma67-ytmusicapi
