Exspotify

View Source

A comprehensive Elixir client for the Spotify Web API with complete coverage of endpoints, type-safe struct parsing, and automatic token management.

Hex.pm Documentation

Features

  • 🎵 Comprehensive API Coverage - 13+ modules covering Albums, Artists, Tracks, Playlists, Search, Player, Shows, Episodes, Audiobooks, and more
  • 🏗️ Type-Safe Structs - All API responses parsed into well-defined Elixir structs with proper types
  • 🔐 Automatic Token Management - Built-in token handling with automatic refresh support for both client credentials and user authorization flows

Installation

Add exspotify to your list of dependencies in mix.exs:

def deps do
  [
    {:exspotify, "~> 0.1.0"}
  ]
end

Quick Start

1. Configuration

Add your Spotify app credentials to config/config.exs:

config :exspotify,
  client_id: "your_spotify_client_id",
  client_secret: "your_spotify_client_secret",
  redirect_uri: "http://localhost:4000/auth/callback"  # For user auth flows

2. Basic Usage

# Get an access token
{:ok, token} = Exspotify.TokenManager.get_token()

# Get album information
{:ok, album} = Exspotify.Albums.get_album("4aawyAB9vmqN3uQ7FjRGTy", token)
IO.puts(album.name)  # "Global Warming"

# Search for tracks
{:ok, results} = Exspotify.Search.search("Bohemian Rhapsody", "track", token)
track = List.first(results["tracks"].items)
IO.puts("#{track.name} by #{List.first(track.artists).name}")

# Get user's playlists (requires user authorization)
{:ok, playlists} = Exspotify.Playlists.get_current_users_playlists(user_token)

Authentication

Exspotify supports both authentication flows:

Client Credentials Flow (App-only access)

# Automatic token management
{:ok, token} = Exspotify.TokenManager.get_token()

# Manual token management
{:ok, %{"access_token" => token}} = Exspotify.Auth.get_access_token()

Authorization Code Flow (User access)

# 1. Build authorization URL
scopes = ["user-read-private", "playlist-read-private"]
{:ok, auth_url} = Exspotify.Auth.build_authorization_url(scopes, "state123")

# 2. Redirect user to auth_url, they'll return with a code

# 3. Exchange code for tokens
{:ok, %{"access_token" => token, "refresh_token" => refresh}} = 
  Exspotify.Auth.exchange_code_for_token(code)

# 4. Refresh when needed
{:ok, %{"access_token" => new_token}} = 
  Exspotify.Auth.refresh_access_token(refresh)

API Coverage

ModuleEndpointsDescription
Albums8 endpointsGet albums, user's saved albums, new releases
Artists3 endpointsGet artists, artist albums, top tracks
Tracks5 endpointsGet tracks, user's saved tracks
Playlists12 endpointsFull playlist management
Search1 endpointSearch all content types
Player11 endpointsPlayback control and state
Users6 endpointsUser profiles and social features
Shows5 endpointsPodcast show management
Episodes5 endpointsPodcast episode management
Audiobooks5 endpointsAudiobook management
Categories2 endpointsBrowse categories
Chapters2 endpointsAudiobook chapters
Markets1 endpointAvailable markets

Error Handling

Exspotify provides structured error handling with helpful suggestions:

case Exspotify.Albums.get_album("", token) do
  {:ok, album} -> 
    # Handle success
  {:error, %Exspotify.Error{type: :empty_id, suggestion: suggestion}} ->
    IO.puts("Error: #{suggestion}")
    # "album_id cannot be empty"
end

Common error types: :unauthorized, :not_found, :rate_limited, :empty_id, :invalid_token

Debug Logging

Enable debug logging to troubleshoot API issues:

# In config/dev.exs
config :exspotify, debug: true

This will log all HTTP requests and responses:

[debug] Exspotify API Request: GET https://api.spotify.com/v1/albums/123
[debug] Exspotify API Response: 200 - Success

Type-Safe Responses

All API responses are parsed into structured Elixir types:

{:ok, album} = Exspotify.Albums.get_album("4aawyAB9vmqN3uQ7FjRGTy", token)

# album is an %Exspotify.Structs.Album{} with typed fields:
album.id          # String.t()
album.name        # String.t()  
album.artists     # [%Exspotify.Structs.Artist{}]
album.images      # [%Exspotify.Structs.Image{}]
album.release_date # String.t() | nil

Configuration Options

config :exspotify,
  client_id: "your_client_id",           # Required for all flows
  client_secret: "your_client_secret",   # Required for all flows  
  redirect_uri: "http://localhost:4000", # Required for user auth
  base_url: "https://api.spotify.com/v1", # Optional, for testing
  debug: false                           # Optional, enables request logging

Examples

Get User's Top Artists

{:ok, user_token} = get_user_token() # Your user auth implementation
{:ok, top_artists} = Exspotify.Users.get_user_top_items("artists", user_token, limit: 10)

Enum.each(top_artists.items, fn artist ->
  IO.puts("#{artist.name} - #{artist.popularity}% popularity")
end)

Control Playback

# Get current playback state
{:ok, state} = Exspotify.Player.get_playback_state(user_token)

if state do
  IO.puts("Currently playing: #{state.item.name}")
  
  # Pause playback
  Exspotify.Player.pause_playback(user_token)
end

Search and Create Playlist

# Search for tracks
{:ok, results} = Exspotify.Search.search("indie rock 2023", ["track"], user_token)
track_uris = Enum.map(results["tracks"].items, & &1.uri)

# Create a playlist
{:ok, playlist} = Exspotify.Playlists.create_playlist(
  user_id, 
  "My Indie Rock Mix", 
  user_token,
  %{description: "Generated with Exspotify"}
)

# Add tracks to playlist
Exspotify.Playlists.add_items_to_playlist(
  playlist.id, 
  user_token, 
  %{uris: Enum.take(track_uris, 20)}
)

Documentation

Full documentation is available on HexDocs.

Contributing

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

License

This project is licensed under the MIT License.

Acknowledgments

  • Spotify Web API for providing a comprehensive music platform API
  • The Elixir community for excellent HTTP and JSON libraries