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:
| Code | Meaning |
|---|---|
| 6 | Invalid parameters / resource not found |
| 8 | Operation failed — try again |
| 9 | Invalid session key |
| 10 | Invalid API key |
| 11 | Service offline |
| 13 | Invalid method signature |
| 26 | API key suspended |
| 29 | Rate 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
- Scrobbling (
track.scrobble,track.updateNowPlaying) - Client-side rate limiting
See the roadmap.
License
MIT — see LICENSE.