Go library for talking to the Tidal API: authenticate, fetch track/video metadata, resolve streams, and download audio.
Inspired by oskvr37/tiddl. The examples use the same ~/.tiddl/auth.json format as the Python CLI, so you can share credentials between the two.
Requires Go 1.25+.
go get github.com/binozo/go-tiddlCreate a client, authenticate, set a country code, then call the API. Most endpoints need CountryCode on the client — GetSession is the usual way to get it after login.
client, err := tiddl.NewClient(
tiddl.WithLogger(slog.Default()),
tiddl.WithContext(ctx),
)
if err != nil {
return err
}
defer client.Close()
// device auth (see below) or load a saved token:
client.SetToken(token)
session, err := client.GetSession(ctx)
if err != nil {
return err
}
client.CountryCode = session.CountryCode
track, err := client.GetTrack(ctx, 302516870)The library implements Tidal's OAuth2 device code flow:
req, err := client.InitiateDeviceAuth(ctx)
// send the user to req.VerificationUriComplete
result, err := client.WaitForDeviceAuth(ctx, req)
client.SetToken(result.Token)WaitForDeviceAuth polls until the user approves, the code expires, or the context is cancelled. For token refresh and persistence, use WithAuthToken / SetToken with a refreshable oauth2.Token, and WithTokenChanged to save rotated tokens.
examples/authentication/authentication.go is a complete login flow that reads/writes ~/.tiddl/auth.json. See examples/README.md for how to run it.
track, _ := client.GetTrack(ctx, trackID)
quality := track.BestQuality()
stream, _ := client.GetTrackStream(ctx, track.ID, quality, false)
reader, _ := client.DownloadTrackStream(ctx, stream)
defer reader.Close()
io.Copy(out, reader)DownloadTrackStream returns an io.ReadCloser that stitches manifest segments together and prefetches the next segment in the background. Works for both BTS and MPEG-DASH manifests.
Pass immersiveAudio: true to GetTrackStream for Dolby Atmos when the track supports it.
examples/download/download_audio.go downloads a track from a URL or numeric ID to track.flac.
video, _ := client.GetVideo(ctx, videoID)
stream, _ := client.GetVideoStream(ctx, videoID, tiddl.VideoHigh)VideoStream exposes a parsed manifest with direct segment URLs. There is no DownloadVideoStream helper yet — you'd fetch the URLs yourself.
id, err := tiddl.ParseTrackID("https://tidal.com/track/302516870/u")Accepts bare numeric IDs and tidal.com/track/... URLs.
Passed to NewClient:
WithAuth— static bearer token (no refresh)WithAuthToken— refreshableoauth2.TokenWithTokenChanged— callback on token refresh/rotationWithCountryCode,WithContext,WithLogger,WithHTTPClient,WithBaseURL,WithClient
| Constant | |
|---|---|
Low |
low bitrate |
High |
320 kbps AAC |
Lossless |
16-bit / 44.1 kHz FLAC |
HiResLossless |
up to 24-bit / 192 kHz FLAC |
Track.BestQuality() picks the highest quality from the track's mediaMetadata.tags.
VideoAudioOnly, VideoLow, VideoMedium, VideoHigh
Sentinel errors for errors.Is:
ErrCountryCodeNotSetErrTokenExpired,ErrNoTokenProvided,ErrNoOpenAuthSessionErrResourceNotFoundErrDeviceAuthorizationPending,ErrDeviceCodeExpired
Everything else comes back wrapped with context.
Search, albums, playlists, artist pages, and video download helpers are not implemented. The streaming/download path for audio is covered by tests; catalog features would build on the existing client.
Not affiliated with Tidal or Aspiro AB. For personal use only.
Apache-2.0 - see LICENSE.