Accessing the last.fm API
🔗(19) | All📅2025-07-12 23:53:17 -0700
⏲️🔐2025-07-12 23:53:40 -0700 | ✍️infinivaeria | 🏷️[last.fm] [api access] [rustby-c]
(🪟)
🖥️...⌨️
Retrieving the Current “Now Playing” Track from Last.fm API in Rust, Ruby, and Crystal
Last.fm’s API allows you to fetch a user’s currently scrobbling song (“now playing” track) if available. In this guide, we’ll walk through obtaining an API key, calling the Last.fm API for the latest track, and writing the output to a text file. We demonstrate the process in three languages – Rust, Ruby, and Crystal (the “Rustby-C” paradigm) – with step-by-step instructions and code examples for each.
Last.fm API Setup and Key Registration
Before coding, you need to sign up for Last.fm’s API and get an API key:
- Create a Last.fm Account: If you don’t have one, register on Last.fm (or log in if you already have an account).
- Apply for an API Key: Visit the Last.fm API page and click “Get an API account”. Fill out the “Create an API account” form with a name and description for your application (you can use any name/description), and you can leave the callback URL blank. Submit the form.
- Copy Your API Key: After submission, Last.fm will display your new API Key (a string of letters and numbers) on the screen. Copy this key – you’ll use it in your API calls (Last.fm also provides a secret, but for reading public data like now-playing tracks, only the key is needed).
Your API key is essential for authenticating requests to Last.fm’s API. Keep it secure and do not share it publicly.
Identifying the Correct API Method (Now Playing Track)
Last.fm’s API method to retrieve a user’s recent tracks (including the current track) is user.getRecentTracks
. This REST endpoint returns a list of a user’s recently scrobbled tracks. If the user is currently listening to a song, the first track in the list will be marked with a special attribute indicating it’s now playing. Specifically, the JSON/XML includes nowplaying="true"
on that track entry.
- Endpoint:
https://ws.audioscrobbler.com/2.0/
- Method:
user.getRecentTracks
- Required Parameters:
•user
– the Last.fm username whose track you want to fetch.
•api_key
– your API key obtained earlier.
•format
– set tojson
for JSON response (easier to parse in code).
For example, a GET request URL looks like:
https://ws.audioscrobbler.com/2.0/?method=user.getRecentTracks&user=LASTFM_USERNAME&api_key=YOUR_API_KEY&format=json
Replace LASTFM_USERNAME
with the target username and YOUR_API_KEY
with the key you obtained. You can test this URL in a web browser or with a tool like curl
. The response will be a JSON object containing the recent tracks. The currently playing track (if any) will appear as the first track
entry with a @attr.nowplaying
flag set to "true"
. If the user isn’t playing anything at the moment, the first entry will just be the last played track (with a timestamp).
Response structure: The JSON will look roughly like:
{
"recenttracks": {
"track": [
{
"artist": { "#text": "Artist Name", ... },
"name": "Song Title",
"album": { "#text": "Album Name", ... },
"url": "https://www.last.fm/music/Artist+Name/_/Song+Title",
"@attr": { "nowplaying": "true" }
},
{
"artist": { "#text": "Previous Artist", ... },
"name": "Previous Song",
"date": { "uts": "1699478400", "#text": "08 Nov 2023, 10:00" }
},
...
]
}
}
In the above, the first track has "nowplaying": "true"
indicating it’s currently being scrobbled. Our code will need to check for this attribute and retrieve that track’s name and artist. According to Last.fm’s docs, user.getRecentTracks
does not require user authentication for public profiles, so the API key alone is sufficient.
💡 Note: The now playing attribute will only be present if the user’s scrobbler updates Last.fm in real-time. Some music players (or scrobbling apps) update “currently listening” status continuously, while others (like certain older scrobblers) only submit tracks after they finish. If you don’t see a nowplaying flag in the response even when music is playing, it could be due to the scrobbler’s behavior rather than an API limitation.
Implementing the Solution in Rust
In Rust, we can use the reqwest
crate to handle the HTTP GET request and serde_json
to parse the JSON response. This approach lets us easily fetch the data and extract the track information without writing a lot of low-level code. We’ll also use Rust’s file I/O from the standard library to write the output to a text file.
Setting up the Rust Environment
Make sure you have Rust installed (via rustup) and set up a new project (cargo new lastfm_nowplaying
). In your Cargo.toml
, add dependencies for reqwest
(with the json
feature) and serde_json
for JSON parsing, as well as tokio
if using async. For example, you can run:
cargo add tokio -F full
cargo add reqwest -F json
cargo add serde_json
This will include the necessary crates. The reqwest
crate’s JSON feature allows directly parsing the response as JSON. We’ll use the synchronous API here for simplicity (via reqwest::blocking
) to avoid dealing with async in the example.
Rust Code: Fetching and Writing Now Playing Track
Below is a Rust code snippet that retrieves the current track and writes “Artist - Title” to nowplaying.txt
:
use reqwest::blocking;
use serde_json::Value;
use std::fs;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let api_key = "YOUR_LASTFM_API_KEY";
let user = "LASTFM_USERNAME";
// Construct the API URL
let url = format!(
"https://ws.audioscrobbler.com/2.0/?method=user.getRecentTracks&user={}&api_key={}&format=json",
user, api_key
);
// Send GET request to Last.fm API
let resp_text = blocking::get(&url)?.text()?;
// Parse the JSON response
let data: Value = serde_json::from_str(&resp_text)?;
// Navigate to the first track in the JSON structure
let tracks = &data["recenttracks"]["track"];
if tracks.is_null() {
eprintln!("No tracks found for user {}", user);
return Ok(());
}
// Handle case where recenttracks.track might be an object or array
let first_track = if tracks.is_array() {
&tracks[0]
} else {
// If only one track, the API might return a single object instead of an array
tracks
};
// Extract track name and artist
let track_name = first_track["name"].as_str().unwrap_or("Unknown Track");
let artist_name = first_track["artist"]["#text"].as_str().unwrap_or("Unknown Artist");
// Check the nowplaying attribute
let nowplaying_attr = first_track.get("@attr").and_then(|attr| attr.get("nowplaying"));
let is_now_playing = nowplaying_attr.map_or(false, |v| v == "true");
// Prepare output string
let output_line = format!("{} - {}", artist_name, track_name);
// Write to text file
fs::write("nowplaying.txt", &output_line)?;
println!("{}", if is_now_playing {
format!("Currently playing: {}", output_line)
} else {
format!("Last played: {}", output_line)
});
Ok(())
}
How it works: This Rust program builds the request URL with your provided user
and api_key
, then uses reqwest::blocking::get
to fetch the data. The response body (in JSON text) is parsed into a serde_json::Value
structure for easy querying. We then access data["recenttracks"]["track"][0]
to get the first track entry. We retrieve the "name"
and the artist’s "#text"
field (the artist name) from the JSON. We also look for the optional @attr.nowplaying
flag. If nowplaying
is "true"
, we know this track is currently being played. Finally, we format “Artist - Track” as a single line and write it to nowplaying.txt
using Rust’s std::fs::write
function, which conveniently creates/overwrites the file and writes the given text in one call.
The Rust code uses safe unwrapping (
unwrap_or
) in case fields are missing, and it checks for both array and object cases forrecenttracks["track"]
because Last.fm’s JSON can return either an array of tracks or a single track object if there’s exactly one recent track. We handle both for robustness. Writing to the file is done viafs::write
, but you could also manually useFile::create
andwrite_all
if preferred.
Run the program: Build and run (cargo run --release
). The console should print out the track it found, and you should see a new file nowplaying.txt
with the content “Artist - Title” corresponding to the user’s current or last track.
Implementing the Solution in Ruby
Ruby has very convenient built-in libraries for making HTTP requests and handling JSON. We don’t even need an external gem to call the Last.fm API. We’ll use OpenURI (an easy wrapper over Net::HTTP) to fetch the URL and Ruby’s JSON library to parse the response. Then, we use Ruby’s file I/O to save the result.
Ruby Code: Fetching and Writing Now Playing Track
Make sure you have Ruby installed (any recent version 2.x or 3.x). The following script demonstrates the process:
require 'open-uri' # allows opening URLs like files
require 'json' # JSON parsing
api_key = "YOUR_LASTFM_API_KEY"
user = "LASTFM_USERNAME"
url = "https://ws.audioscrobbler.com/2.0/?method=user.getRecentTracks&user=#{user}&api_key=#{api_key}&format=json"
# Fetch the JSON data from Last.fm API
response = URI.open(url).read # OpenURI treats the URL like a file and reads it
data = JSON.parse(response)
# Extract the first track from the response
tracks = data.dig("recenttracks", "track")
first_track = tracks.is_a?(Array) ? tracks.first : tracks # handle array or single object
if first_track.nil?
puts "No track info found for user #{user}"
exit
end
track_name = first_track["name"] || "Unknown Track"
artist_name = first_track.dig("artist", "#text") || "Unknown Artist"
nowplaying = first_track.dig("@attr", "nowplaying") == "true"
output_line = "#{artist_name} - #{track_name}"
File.write("nowplaying.txt", output_line) # writes the string to file (overwrites if exists)
if nowplaying
puts "Currently playing: #{output_line}"
else
puts "Last played: #{output_line}"
end
Explanation: We use URI.open
(from OpenURI) to send a GET request to the Last.fm API endpoint. OpenURI makes it trivial to retrieve the contents of a URL as if it were a local file – the URI.open(...).read
call returns the response body as a string. We then parse that string into a Ruby hash using JSON.parse
. The current track data is nested under ["recenttracks"]["track"]
. In Ruby, this might be either an Array (if multiple recent tracks) or a Hash (if only one track in history), so we check is_a?(Array)
and take the first element if it’s an array. We then pull out the "name"
and "artist"]["#text"]
fields for the track title and artist. To see if it’s now playing, we look for the optional ["@attr"]["nowplaying"]
flag and compare it to "true"
.
Finally, we use File.write
to write the output to a text file. The File.write
method in Ruby is a simple one-liner that opens (or creates) the file, writes the given content, and closes the file automatically. In our case, it will create/overwrite nowplaying.txt
with a line like Artist Name - Song Title
. We also print a message to the console indicating whether it’s the current track or just the last played track.
Note: OpenURI is part of Ruby’s standard library and conveniently handles HTTP redirects and SSL. It’s suitable for simple use cases. For more complex needs or to handle errors, you might use
Net::HTTP
directly or a gem like HTTParty. Here, OpenURI keeps the code concise.
Run the script: Save it as lastfm_nowplaying.rb
and run ruby lastfm_nowplaying.rb
. The script will create/update nowplaying.txt
in the current directory with the now-playing info.
Alternative Ruby approach: Instead of manual requests, you could use the community-provided ruby-lastfm
gem, which wraps Last.fm API methods in Ruby objects. Using that gem, you would initialize a client with your API key and secret, then call something like lastfm.user.get_recent_tracks(user: "name")
to get a Ruby array/hash of tracks. Under the hood it’s doing the same HTTP call. For a short script, using the standard libraries as shown is straightforward and avoids extra dependencies.
Implementing the Solution in Crystal
Crystal is a language with Ruby-like syntax, compiled for performance. It has a standard library that includes an HTTP client and JSON parsing, making the task very similar to the Ruby approach. We’ll use Crystal’s built-in HTTP::Client for the GET request and JSON.parse for parsing.
Crystal Code: Fetching and Writing Now Playing Track
Make sure Crystal is installed on your system. Create a file lastfm_nowplaying.cr
with the following content:
require "http/client"
require "json"
api_key = "YOUR_LASTFM_API_KEY"
user = "LASTFM_USERNAME"
url = "https://ws.audioscrobbler.com/2.0/?method=user.getRecentTracks&user=#{user}&api_key=#{api_key}&format=json"
# Send GET request to Last.fm API
response = HTTP::Client.get(url) # perform HTTP GET and get a Response
if response.status_code != 200
puts "HTTP request failed with code #{response.status_code}"
exit
end
# Parse JSON response body into a dynamic structure (JSON::Any)
data = JSON.parse(response.body)
# Access the recenttracks.track data
tracks = data["recenttracks"]["track"]
first_track = tracks.is_a?(Array) ? tracks[0] : tracks # Crystal JSON::Any allows .is_a?(Array)
if first_track.nil?
puts "No track info found for user #{user}"
exit
end
track_name = first_track["name"].as_s || "Unknown Track"
artist_name = first_track["artist"]["#text"].as_s || "Unknown Artist"
now_playing_attr = first_track["@attr"]?["nowplaying"]? # use nil-safe navigation
is_now_playing = now_playing_attr.as_s? == "true"
output_line = "#{artist_name} - #{track_name}"
File.write("nowplaying.txt", output_line) # write output to file (creates or truncates)
if is_now_playing
puts "Currently playing: #{output_line}"
else
puts "Last played: #{output_line}"
end
Explanation: After requiring http/client
and json
, we construct the request URL similarly to the other languages. We then call HTTP::Client.get(url)
, which returns an HTTP::Client::Response
object synchronously. We check that the status_code
is 200 (OK). The response body is accessible via response.body
as a String
. Crystal’s JSON.parse
returns a JSON::Any
(a type that can hold any JSON structure). We navigate through this JSON structure by keys: data["recenttracks"]["track"]
. This returns another JSON::Any
which could be an Array or single object. We check tracks.is_a?(Array)
to decide how to extract the first track (Crystal’s JSON library provides type query methods like is_a?(Array)
on JSON::Any).
Once we have first_track
, we pull the "name"
and nested "artist"]["#text"]
values. In Crystal, JSON::Any
provides methods like .as_s
to convert to string, and returns nil
if the value is not present or not a string. We use || "Unknown Track"
to default in case of nil. To check the now playing flag, we safely navigate to first_track["@attr"]
and then ["nowplaying"]
using the ?
operator (which yields nil
if any part is missing). We then get the string value and compare to "true"
. The logic is similar to the Ruby version.
Finally, we use File.write("nowplaying.txt", output_line)
to write the result to a file. Crystal’s File.write
is analogous to Ruby’s: it opens (creates if needed) the file, writes the given content, and closes it, in one call. We print out a message indicating whether it’s currently playing or just last played.
Compile and run: Compile the Crystal program with crystal build --release lastfm_nowplaying.cr
. Running the resulting binary will create/update the nowplaying.txt
file with the song info.
Because Crystal compiles to a native binary, this tool can be very fast and suitable for command-line use or cron jobs (e.g., to periodically update a “now playing” text file for a streaming overlay or a website).
Alternate Approaches and Libraries
The above sections show how to directly use the Last.fm REST API in three languages. If the official API did not meet your needs or you prefer not to handle HTTP/JSON manually, there are a few alternatives and third-party libraries to consider:
Higher-level API Wrappers: Many community libraries simplify interactions with Last.fm. For example, in Rust you could use the
lastfm
crate, which provides aClient
abstraction and even anow_playing()
method to get the current track directly. In Ruby, theruby-lastfm
gem (by Youpy) offers methods corresponding to each API call (e.g.,lastfm.user.get_recent_tracks
) and handles the HTTP under the hood. Using these can reduce boilerplate; however, adding a dependency is only worth it if you plan to use many API features. In our simple scenario, the raw HTTP approach is fairly straightforward in each language.Alternate Data Sources: If Last.fm’s API didn’t provide the now playing song, one could consider other means. For instance, some users embed now playing info by using the Spotify API (if they listen via Spotify) or by reading from a local music player’s API/IPC (like MPD or iTunes integrations). Another option is using an open scrobbling service like ListenBrainz which has its own API. Fortunately, Last.fm’s
user.getRecentTracks
does support retrieving the current track as we’ve demonstrated, so usually you won’t need these workarounds.Automation and Integration: With the above code, you can periodically run the script/binary (using a scheduler or cron job) to update the text file whenever you want. If you are displaying this on a website, ensure the site reads the latest contents of
nowplaying.txt
or set up a small server to serve this info. For example, a simple approach could be to have a job update an HTML snippet or JSON file that your webpage can fetch. On a desktop (e.g., streaming setup), you might just load the text file contents directly into your overlay tool.
Remember that the Last.fm API is rate-limited (typically 1 request per second, and a few hundred calls per day for free accounts). For a “currently playing” display, updating once every 60 seconds is more than enough in most cases (or even less frequently, as tracks usually last a few minutes).
Summary of Steps and Tools
The table below summarizes the key steps to achieve this and the tools or libraries used in each language:
Step | Rust Implementation | Ruby Implementation | Crystal Implementation |
---|---|---|---|
1. Get API Key | Register via Last.fm website to obtain API key. | Same process (API key is language-agnostic). | Same process (API key is language-agnostic). |
2. Construct API Request | Use reqwest to build and send GET request (REST URL with query params). |
Use OpenURI (URI.open ) to fetch the URL as text. |
Use HTTP::Client.get from stdlib to send GET request. |
3. Parse JSON Response | Use serde_json to parse into Value (dynamic JSON) and traverse to track info. |
Use built-in JSON.parse to get Ruby Hash/Array. |
Use JSON.parse to get JSON::Any and navigate keys. |
4. Identify Now-Playing Track | Check Value for recenttracks.track[0].@attr.nowplaying == "true" to confirm current track. |
Check first track Hash for "@attr"]["nowplaying"] == "true" . |
Check first track JSON::Any for ["@attr"]["nowplaying"] == "true" . |
5. Extract Song & Artist | Extract track["name"] and track["artist"]["#text"] from JSON Value. |
Extract hash["name"] and hash["artist"]["#text"] from parsed Hash. |
Extract any["name"].as_s and any["artist"]["#text"].as_s from JSON::Any. |
6. Write to Text File | Use Rust std::fs::write or File::create + write_all to save output. |
Use File.write to create/overwrite file with content. |
Use File.write (Crystal) to write string to file. |
Tools/Libraries Used | reqwest HTTP client, serde_json for JSON, Rust std fs for file I/O. |
Ruby stdlib (OpenURI, JSON, File class), no extra gem required. |
Crystal stdlib (HTTP::Client, JSON, File class), no extra shard required. |
Optional: Third-Party Lib | lastfm crate provides a higher-level API (e.g. now_playing() method). |
ruby-lastfm gem wraps the API methods (e.g. user.get_recent_tracks ). |
(No dedicated Last.fm shard known; use HTTP/JSON as above.) |
Each implementation follows the same basic flow: get API key → call Last.fm API → parse JSON → find current track → write to file. By leveraging each language’s strengths (Rust’s type safety and speed, Ruby’s elegance and brevity with open-uri, Crystal’s convenient syntax with compiled performance), you can achieve the goal in whichever environment you prefer. The resulting nowplaying.txt
will contain the track info, ready to be used wherever needed (website, overlay, etc.), updating whenever the script is run.
I'll start researching how to interact with the Last.fm API to fetch the current scrobbling song and write it to a text file, using Rust, Ruby, and Crystal as part of the Rustby-C paradigm. This will include a step-by-step guide for signing up for the API, making the necessary API calls, and implementing the functionality. If the Last.fm API doesn't support this directly, I'll explore alternative methods or third-party libraries that can achieve the same goal. This will take me some time, so feel free to step away—I’ll keep working in the background and save the report in this conversation for you to review later!