Lastex

A Last.fm API client for Elixir.

Installation

Add lastex to your dependencies in mix.exs:

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

Configuration

Get an API key and secret by creating a Last.fm API account, then configure them:

config :lastex,
api_key: System.get_env("LASTFM_API_KEY"),
api_secret: System.get_env("LASTFM_API_SECRET")

The secret is only needed for authenticated calls.

Usage

Every function returns {:ok, result} or {:error, %Lastex.Error{}}. Read functions take options as a keyword list, which is passed through to the Last.fm query parameters — so any option the API documents (autocorrect, lang, from, to, …) works even when it isn't named below.

Names are always given artist-first: Lastex.Track.info("Opeth", "Blackwater Park").

User

Lastex.User.info("rj")
Lastex.User.friends("rj", limit: 50)
Lastex.User.recent_tracks("rj", limit: 200)
Lastex.User.loved_tracks("rj", limit: 50)
Lastex.User.top_artists("rj", period: "overall", limit: 10)
Lastex.User.top_albums("rj", period: "6month", limit: 10)
Lastex.User.top_tracks("rj", period: "1month", limit: 10)
Lastex.User.top_tags("rj", limit: 10)
Lastex.User.personal_tags("rj", "progressive metal", "artist")
# Weekly charts
Lastex.User.weekly_chart_list("rj")
Lastex.User.weekly_artist_chart("rj", from: 1_108_296_000, to: 1_108_900_800)
Lastex.User.weekly_album_chart("rj")
Lastex.User.weekly_track_chart("rj")

period is one of "overall", "7day", "1month", "3month", "6month", or "12month".

Artist

Lastex.Artist.info("Opeth")
Lastex.Artist.correction("opeth")
Lastex.Artist.similar("Opeth", limit: 10)
Lastex.Artist.tags("Opeth", user: "rj")
Lastex.Artist.top_tags("Opeth")
Lastex.Artist.top_albums("Opeth", limit: 10)
Lastex.Artist.top_tracks("Opeth", limit: 10)
Lastex.Artist.search("Opeth", limit: 5)

Album

Lastex.Album.info("Opeth", "Blackwater Park")
Lastex.Album.tags("Opeth", "Blackwater Park", user: "rj")
Lastex.Album.top_tags("Opeth", "Blackwater Park")
Lastex.Album.search("Blackwater Park", limit: 5)

Track

Lastex.Track.info("Opeth", "Blackwater Park")
Lastex.Track.correction("opeth", "blackwater park")
Lastex.Track.similar("Opeth", "Blackwater Park", limit: 10)
Lastex.Track.tags("Opeth", "Blackwater Park", user: "rj")
Lastex.Track.top_tags("Opeth", "Blackwater Park")
Lastex.Track.search("Blackwater Park", limit: 5)

Tag

Lastex.Tag.info("progressive metal")
Lastex.Tag.similar("progressive metal")
Lastex.Tag.top_artists("progressive metal", limit: 10)
Lastex.Tag.top_albums("progressive metal", limit: 10)
Lastex.Tag.top_tracks("progressive metal", limit: 10)
Lastex.Tag.top_tags()
Lastex.Tag.weekly_chart_list("progressive metal")

Chart, Geo, and Library

Lastex.Chart.top_artists(limit: 10)
Lastex.Chart.top_tracks(limit: 10)
Lastex.Chart.top_tags(limit: 10)
Lastex.Geo.top_artists("Sweden", limit: 10)
Lastex.Geo.top_tracks("Sweden", limit: 10)
Lastex.Library.artists("rj", limit: 50)

Authentication

Write operations need a session key, obtained by having the user authorize your API key. Session keys do not expire, so store them alongside the user.

Desktop flow

# 1. Fetch an unauthorized request token
{:ok, %{"token" => token}} = Lastex.Auth.token()
# 2. Send the user to authorize it in their browser
Lastex.Auth.url(token)
#=> "https://www.last.fm/api/auth/?api_key=YOUR_KEY&token=TOKEN"
# 3. Once they've authorized, exchange the token for a session key
{:ok, %{"session" => %{"key" => session_key, "name" => username}}} =
Lastex.Auth.session(token)

Web flow

Send the user to https://www.last.fm/api/auth/?api_key=YOUR_KEY. Last.fm redirects them to the callback URL registered on your API account with a token query parameter, which you exchange via Lastex.Auth.session/1 as above. Tokens are single-use and valid for 60 minutes.

Mobile flow

For applications that collect credentials in their own UI. Requires the mobile authentication permission on your API account.

{:ok, %{"session" => %{"key" => session_key}}} =
Lastex.Auth.mobile_session("username", "password")

Authenticated calls

Pass the session key as the last argument:

Lastex.Track.love("Opeth", "Blackwater Park", session_key)
Lastex.Track.unlove("Opeth", "Blackwater Park", session_key)
Lastex.Artist.add_tags("Opeth", "progressive metal,swedish", session_key)
Lastex.Artist.remove_tag("Opeth", "swedish", session_key)
Lastex.Album.add_tags("Opeth", "Blackwater Park", "progressive metal", session_key)
Lastex.Album.remove_tag("Opeth", "Blackwater Park", "progressive metal", session_key)
Lastex.Track.add_tags("Opeth", "Blackwater Park", "progressive metal", session_key)
Lastex.Track.remove_tag("Opeth", "Blackwater Park", "progressive metal", session_key)

Signing is automatic. Lastex builds the api_sig by sorting all parameters alphabetically (excluding format and callback), concatenating them as <name><value>, appending your API secret, and taking the MD5 hash.

Pagination

List endpoints return a Lastex.Page struct and accept page and limit:

{:ok, page} = Lastex.User.recent_tracks("rj", page: 1, limit: 200)
page.data # the list of results
page.page # 1
page.per_page # 200
page.total # 85_000
page.total_pages # 425

Endpoints returning a single entity — Lastex.User.info/1, Lastex.Artist.info/2, Lastex.Album.info/3, Lastex.Track.info/3, Lastex.Tag.info/2, the weekly_chart_list functions, and the correction functions — return the decoded response body directly.

Error handling

case Lastex.User.info("nonexistent") do
{:ok, user} -> user
{:error, %Lastex.Error{code: 6}} -> :not_found
{:error, reason} -> {:error, reason}
end

Lastex.Error is an exception, so it also works with raise. Common codes:

CodeMeaning
6Invalid parameters / resource not found
8Operation failed — try again
9Invalid session key
10Invalid API key
11Service offline
13Invalid method signature
26API key suspended
29Rate limit exceeded

Transport failures pass the underlying Req/Finch reason through unchanged.

HTTP client

Lastex uses Req. Options are merged into every request, which is how the test suite stubs the API:

config :lastex, req_options: [plug: {Req.Test, Lastex.Client}]

Last.fm asks for no more than 5 requests per second per key. Lastex does not throttle for you — rate limiting is on the roadmap.

Not yet supported

See the roadmap.

License

MIT — see LICENSE.