diff --git a/docs/album.docs b/docs/album.docs
deleted file mode 100644
--- a/docs/album.docs
+++ /dev/null
@@ -1,37 +0,0 @@
-/* THIS FILE IS NOT INTENDED FOR USER READING. USE HADDOCK DOCUMENTATION INSTEAD. */
-
-#define __addTags__ \
--- | Tag an album using a list of user supplied tags. \
-More: <http://www.last.fm/api/show/album.addTags>
-
-#define __getBuyLinks__ \
--- | Get a list of Buy Links for a particular Album. It is required that you supply either the artist and track params or the mbid param. \
-More: <http://www.last.fm/api/show/album.getBuylinks>
-
-#define __getInfo__ \
--- | Get the metadata for an album on Last.fm using the album name or a musicbrainz id. See playlist.fetch on how to get the album playlist. \
-More: <http://www.last.fm/api/show/album.getInfo>
-
-#define __getShouts__ \
--- | Get shouts for this album. \
-More: <http://www.last.fm/api/show/album.getShouts>
-
-#define __getTags__ \
--- | Get the tags applied by an individual user to an album on Last.fm. \
-More: <http://www.last.fm/api/show/album.getTags>
-
-#define __getTopTags__ \
--- | Get the top tags for an album on Last.fm, ordered by popularity. \
-More: <http://www.last.fm/api/show/album.getTopTags>
-
-#define __removeTag__ \
--- | Remove a user's tag from an album. \
-More: <http://www.last.fm/api/show/album.removeTag>
-
-#define __search__ \
--- | Search for an album by name. Returns album matches sorted by relevance. \
-More: <http://www.last.fm/api/show/album.search>
-
-#define __share__ \
--- | Share an album with one or more Last.fm users or other friends. \
-More: <http://www.last.fm/api/show/album.share>
diff --git a/docs/artist.docs b/docs/artist.docs
deleted file mode 100644
--- a/docs/artist.docs
+++ /dev/null
@@ -1,73 +0,0 @@
-/* THIS FILE IS NOT INTENDED FOR USER READING. USE HADDOCK DOCUMENTATION INSTEAD. */
-
-#define __addTags__ \
--- | Tag an album using a list of user supplied tags. \
-More: <http://www.last.fm/api/show/artist.addTags>
-
-#define __getCorrection__ \
--- | Use the last.fm corrections data to check whether the supplied artist has a correction to a canonical artist \
-More: <http://www.last.fm/api/show/artist.getCorrection>
-
-#define __getEvents__ \
--- | Get a list of upcoming events for this artist. \
-More: <http://www.last.fm/api/show/artist.getEvents>
-
-#define __getImages__ \
--- | Get Images for this artist in a variety of sizes. \
-More: <http://www.last.fm/api/show/artist.getImages>
-
-#define __getInfo__ \
--- | Get the metadata for an artist. Includes biography. \
-More: <http://www.last.fm/api/show/artist.getInfo>
-
-#define __getPastEvents__ \
--- | Get a paginated list of all the events this artist has played at in the past. \
-More: <http://www.last.fm/api/show/artist.getPastEvents>
-
-#define __getPodcast__ \
--- | Get a podcast of free mp3s based on an artist. \
-More: <http://www.last.fm/api/show/artist.getPodcast>
-
-#define __getShouts__ \
--- | Get shouts for this artist. Also available as an rss feed. \
-More: <http://www.last.fm/api/show/artist.getShouts>
-
-#define __getSimilar__ \
--- | Get all the artists similar to this artist. \
-More: <http://www.last.fm/api/show/artist.getSimilar>
-
-#define __getTags__ \
--- | Get the tags applied by an individual user to an artist on Last.fm. If accessed as an authenticated service /and/ you don't supply a user parameter then this service will return tags for the authenticated user. \
-More: <http://www.last.fm/api/show/artist.getTags>
-
-#define __getTopAlbums__ \
--- | Get the top albums for an artist on Last.fm, ordered by popularity. \
-More: <http://www.last.fm/api/show/artist.getTopAlbums>
-
-#define __getTopFans__ \
--- | Get the top fans for an artist on Last.fm, based on listening data. \
-More: <http://www.last.fm/api/show/artist.getTopFans>
-
-#define __getTopTags__ \
--- | Get the top tags for an artist on Last.fm, ordered by popularity. \
-More: <http://www.last.fm/api/show/artist.getTopTags>
-
-#define __getTopTracks__ \
--- | Get the top tracks by an artist on Last.fm, ordered by popularity. \
-More: <http://www.last.fm/api/show/artist.getTopTracks>
-
-#define __removeTag__ \
--- | Remove a user's tag from an artist. \
-More: <http://www.last.fm/api/show/artist.removeTag>
-
-#define __search__ \
--- | Search for an artist by name. Returns artist matches sorted by relevance. \
-More: <http://www.last.fm/api/show/artist.search>
-
-#define __share__ \
--- | Share an artist with Last.fm users or other friends. \
-More: <http://www.last.fm/api/show/artist.share>
-
-#define __shout__ \
--- | Shout in this artist's shoutbox. \
-More: <http://www.last.fm/api/show/artist.shout>
diff --git a/docs/chart.docs b/docs/chart.docs
deleted file mode 100644
--- a/docs/chart.docs
+++ /dev/null
@@ -1,25 +0,0 @@
-/* THIS FILE IS NOT INTENDED FOR USER READING. USE HADDOCK DOCUMENTATION INSTEAD. */
-
-#define __getHypedArtists__ \
--- | Get the hyped artists chart. \
-More: <http://www.last.fm/api/show/chart.getHypedArtists>
-
-#define __getHypedTracks__ \
--- | Get the hyped tracks chart. \
-More: <http://www.last.fm/api/show/chart.getHypedTracks>
-
-#define __getLovedTracks__ \
--- | Get the most loved tracks chart. \
-More: <http://www.last.fm/api/show/chart.getLovedTracks>
-
-#define __getTopArtists__ \
--- | Get the top artists chart. \
-More: <http://www.last.fm/api/show/chart.getTopArtists>
-
-#define __getTopTags__ \
--- | Get top tags chart. \
-More: <http://www.last.fm/api/show/chart.getTopTags>
-
-#define __getTopTracks__ \
--- | Get the top tracks chart. \
-More: <http://www.last.fm/api/show/chart.getTopTracks>
diff --git a/docs/event.docs b/docs/event.docs
deleted file mode 100644
--- a/docs/event.docs
+++ /dev/null
@@ -1,25 +0,0 @@
-/* THIS FILE IS NOT INTENDED FOR USER READING. USE HADDOCK DOCUMENTATION INSTEAD. */
-
-#define __attend__ \
--- | Set a user's attendance status for an event. \
-More: <http://www.last.fm/api/show/event.attend>
-
-#define __getAttendees__ \
--- | Get a list of attendees for an event. \
-More: <http://www.last.fm/api/show/event.getAttendees>
-
-#define __getInfo__ \
--- | Get the metadata for an event on Last.fm. Includes attendance and lineup information. \
-More: <http://www.last.fm/api/show/event.getInfo>
-
-#define __getShouts__ \
--- | Get shouts for this event. \
-More: <http://www.last.fm/api/show/event.getShouts>
-
-#define __share__ \
--- | Share an event with one or more Last.fm users or other friends. \
-More: <http://www.last.fm/api/show/event.share>
-
-#define __shout__ \
--- | Shout in this event's shoutbox. \
-More: <http://www.last.fm/api/show/event.shout>
diff --git a/docs/geo.docs b/docs/geo.docs
deleted file mode 100644
--- a/docs/geo.docs
+++ /dev/null
@@ -1,45 +0,0 @@
-/* THIS FILE IS NOT INTENDED FOR USER READING. USE HADDOCK DOCUMENTATION INSTEAD. */
-
-#define __getEvents__ \
--- | Get all events in a specific location by country or city name. \
-More: <http://www.last.fm/api/show/geo.getEvents>
-
-#define __getMetroArtistChart__ \
--- | Get a chart of artists for a metro. \
-More: <http://www.last.fm/api/show/geo.getMetroArtistChart>
-
-#define __getMetroHypeArtistChart__ \
--- | Get a chart of hyped (up and coming) artists for a metro. \
-More: <http://www.last.fm/api/show/geo.getMetroHypeArtistChart>
-
-#define __getMetroHypeTrackChart__ \
--- | Get a chart of hyped (up and coming) tracks for a metro. \
-More: <http://www.last.fm/api/show/geo.getMetroHypeTrackChart>
-
-#define __getMetroTrackChart__ \
--- | Get a chart of tracks for a metro. \
-More: <http://www.last.fm/api/show/geo.getMetroTrackChart>
-
-#define __getMetroUniqueArtistChart__ \
--- | Get a chart of the artists which make that metro unique. \
-More: <http://www.last.fm/api/show/geo.getMetroUniqueArtistChart>
-
-#define __getMetroUniqueTrackChart__ \
--- | Get a chart of the tracks which make that metro unique. \
-More: <http://www.last.fm/api/show/geo.getMetroUniqueTrackChart>
-
-#define __getMetroWeeklyChartlist__ \
--- | Get a list of available chart periods for this metro, expressed as date ranges which can be sent to the chart services. \
-More: <http://www.last.fm/api/show/geo.getMetroWeeklyChartlist>
-
-#define __getMetros__ \
--- | Get a list of valid countries and metros for use in the other webservices. \
-More: <http://www.last.fm/api/show/geo.getMetros>
-
-#define __getTopArtists__ \
--- | Get the most popular artists on Last.fm by country. \
-More: <http://www.last.fm/api/show/geo.getTopArtists>
-
-#define __getTopTracks__ \
--- | Get the most popular tracks on Last.fm last week by country. \
-More: <http://www.last.fm/api/show/geo.getTopTracks>
diff --git a/docs/group.docs b/docs/group.docs
deleted file mode 100644
--- a/docs/group.docs
+++ /dev/null
@@ -1,25 +0,0 @@
-/* THIS FILE IS NOT INTENDED FOR USER READING. USE HADDOCK DOCUMENTATION INSTEAD. */
-
-#define __getHype__ \
--- | Get the hype list for a group. \
-More: <http://www.last.fm/api/show/group.getHype>
-
-#define __getMembers__ \
--- | Get a list of members for this group. \
-More: <http://www.last.fm/api/show/group.getMembers>
-
-#define __getWeeklyAlbumChart__ \
--- | Get an album chart for a group, for a given date range. If no date range is supplied, it will return the most recent album chart for this group. \
-More: <http://www.last.fm/api/show/group.getWeeklyAlbumChart>
-
-#define __getWeeklyArtistChart__ \
--- | Get an artist chart for a group, for a given date range. If no date range is supplied, it will return the most recent artist chart for this group. \
-More: <http://www.last.fm/api/show/group.getWeeklyArtistChart>
-
-#define __getWeeklyChartList__ \
--- | Get a list of available charts for this group, expressed as date ranges which can be sent to the chart services. \
-More: <http://www.last.fm/api/show/group.getWeeklyChartList>
-
-#define __getWeeklyTrackChart__ \
--- | Get a track chart for a group, for a given date range. If no date range is supplied, it will return the most recent track chart for this group. \
-More: <http://www.last.fm/api/show/group.getWeeklyTrackChart>
diff --git a/docs/library.docs b/docs/library.docs
deleted file mode 100644
--- a/docs/library.docs
+++ /dev/null
@@ -1,41 +0,0 @@
-/* THIS FILE IS NOT INTENDED FOR USER READING. USE HADDOCK DOCUMENTATION INSTEAD. */
-
-#define __addAlbum__ \
--- | Add an album or collection of albums to a user's Last.fm library. \
-More: <http://www.last.fm/api/show/library.addAlbum>
-
-#define __addArtist__ \
--- | Add an artist to a user's Last.fm library. \
-More: <http://www.last.fm/api/show/library.addArtist>
-
-#define __addTrack__ \
--- | Add a track to a user's Last.fm library. \
-More: <http://www.last.fm/api/show/library.addTrack>
-
-#define __getAlbums__ \
--- | A paginated list of all the albums in a user's library, with play counts and tag counts. \
-More: <http://www.last.fm/api/show/library.getAlbums>
-
-#define __getArtists__ \
--- | A paginated list of all the artists in a user's library, with play counts and tag counts. \
-More: <http://www.last.fm/api/show/library.getArtists>
-
-#define __getTracks__ \
--- | A paginated list of all the tracks in a user's library, with play counts and tag counts. \
-More: <http://www.last.fm/api/show/library.getTracks>
-
-#define __removeAlbum__ \
--- | Remove an album from a user's Last.fm library. \
-More: <http://www.last.fm/api/show/library.removeAlbum>
-
-#define __removeArtist__ \
--- | Remove an artist from a user's Last.fm library. \
-More: <http://www.last.fm/api/show/library.removeArtist>
-
-#define __removeScrobble__ \
--- | Remove a scrobble from a user's Last.fm library. \
-More: <http://www.last.fm/api/show/library.removeScrobble>
-
-#define __removeTrack__ \
--- | Remove a track from a user's Last.fm library. \
-More: <http://www.last.fm/api/show/library.removeTrack>
diff --git a/docs/playlist.docs b/docs/playlist.docs
deleted file mode 100644
--- a/docs/playlist.docs
+++ /dev/null
@@ -1,9 +0,0 @@
-/* THIS FILE IS NOT INTENDED FOR USER READING. USE HADDOCK DOCUMENTATION INSTEAD. */
-
-#define __addTrack__ \
--- | Add a track to a Last.fm user's playlist. \
-More: <http://www.last.fm/api/show/playlist.addTrack>
-
-#define __create__ \
--- | Create a Last.fm playlist on behalf of a user. \
-More: <http://www.last.fm/api/show/playlist.create>
diff --git a/docs/radio.docs b/docs/radio.docs
deleted file mode 100644
--- a/docs/radio.docs
+++ /dev/null
@@ -1,13 +0,0 @@
-/* THIS FILE IS NOT INTENDED FOR USER READING. USE HADDOCK DOCUMENTATION INSTEAD. */
-
-#define __getPlaylist__ \
--- | Fetch new radio content periodically in an XSPF format. \
-More: <http://www.last.fm/api/show/radio.getPlaylist>
-
-#define __search__ \
--- | Resolve the name of a resource into a station depending on which resource it is most likely to represent. \
-More: <http://www.last.fm/api/show/radio.search>
-
-#define __tune__ \
--- | Tune in to a Last.fm radio station. \
-More: <http://www.last.fm/api/show/radio.tune>
diff --git a/docs/tag.docs b/docs/tag.docs
deleted file mode 100644
--- a/docs/tag.docs
+++ /dev/null
@@ -1,37 +0,0 @@
-/* THIS FILE IS NOT INTENDED FOR USER READING. USE HADDOCK DOCUMENTATION INSTEAD. */
-
-#define __getInfo__ \
--- | Get the metadata for a tag. \
-More: <http://www.last.fm/api/show/tag.getInfo>
-
-#define __getSimilar__ \
--- | Search for tags similar to this one. Returns tags ranked by similarity, based on listening data. \
-More: <http://www.last.fm/api/show/tag.getSimilar>
-
-#define __getTopAlbums__ \
--- | Get the top albums tagged by this tag, ordered by tag count. \
-More: <http://www.last.fm/api/show/tag.getTopAlbums>
-
-#define __getTopArtists__ \
--- | Get the top artists tagged by this tag, ordered by tag count. \
-More: <http://www.last.fm/api/show/tag.getTopArtists>
-
-#define __getTopTags__ \
--- | Fetches the top global tags on Last.fm, sorted by popularity (number of times used). \
-More: <http://www.last.fm/api/show/tag.getTopTags>
-
-#define __getTopTracks__ \
--- | Get the top tracks tagged by this tag, ordered by tag count. \
-More: <http://www.last.fm/api/show/tag.getTopTracks>
-
-#define __getWeeklyArtistChart__ \
--- | Get an artist chart for a tag, for a given date range. If no date range is supplied, it will return the most recent artist chart for this tag. \
-More: <http://www.last.fm/api/show/tag.getWeeklyArtistChart>
-
-#define __getWeeklyChartList__ \
--- | Get a list of available charts for this tag, expressed as date ranges which can be sent to the chart services. \
-More: <http://www.last.fm/api/show/tag.getWeeklyChartList>
-
-#define __search__ \
--- | Search for a tag by name. Returns matches sorted by relevance. \
-More: <http://www.last.fm/api/show/tag.search>
diff --git a/docs/tasteometer.docs b/docs/tasteometer.docs
deleted file mode 100644
--- a/docs/tasteometer.docs
+++ /dev/null
@@ -1,5 +0,0 @@
-/* THIS FILE IS NOT INTENDED FOR USER READING. USE HADDOCK DOCUMENTATION INSTEAD. */
-
-#define __compare__ \
--- | Get a Tasteometer score from two inputs, along with a list of shared artists. If the input is a User some additional information is returned. \
-More: <http://www.last.fm/api/show/tasteometer.compare>
diff --git a/docs/track.docs b/docs/track.docs
deleted file mode 100644
--- a/docs/track.docs
+++ /dev/null
@@ -1,77 +0,0 @@
-/* THIS FILE IS NOT INTENDED FOR USER READING. USE HADDOCK DOCUMENTATION INSTEAD. */
-
-#define __addTags__ \
--- | Tag a track using a list of user supplied tags. \
-More: <http://www.last.fm/api/show/track.addTags>
-
-#define __ban__ \
--- | Ban a track for a given user profile. \
-More: <http://www.last.fm/api/show/track.ban>
-
-#define __getBuyLinks__ \
--- | Get a list of Buy Links for a particular track. \
-More: <http://www.last.fm/api/show/track.getBuylinks>
-
-#define __getCorrection__ \
--- | Use the last.fm corrections data to check whether the supplied track has a correction to a canonical track. \
-More: <http://www.last.fm/api/show/track.getCorrection>
-
-#define __getFingerprintMetadata__ \
--- | Retrieve track metadata associated with a fingerprint id generated by the Last.fm Fingerprinter. Returns track elements, along with a 'rank' value between 0 and 1 reflecting the confidence for each match. \
-More: <http://www.last.fm/api/show/track.getFingerprintMetadata>
-
-#define __getInfo__ \
--- | Get the metadata for a track on Last.fm. \
-More: <http://www.last.fm/api/show/track.getInfo>
-
-#define __getShouts__ \
--- | Get shouts for this track. Also available as an rss feed. \
-More: <http://www.last.fm/api/show/track.getShouts>
-
-#define __getSimilar__ \
--- | Get the similar tracks for this track on Last.fm, based on listening data. \
-More: <http://www.last.fm/api/show/track.getSimilar>
-
-#define __getTags__ \
--- | Get the tags applied by an individual user to a track on Last.fm. \
-More: <http://www.last.fm/api/show/track.getTags>
-
-#define __getTopFans__ \
--- | Get the top fans for this track on Last.fm, based on listening data. \
-More: <http://www.last.fm/api/show/track.getTopFans>
-
-#define __getTopTags__ \
--- | Get the top tags for this track on Last.fm, ordered by tag count. \
-More: <http://www.last.fm/api/show/track.getTopTags>
-
-#define __love__ \
--- | Love a track for a user profile. \
-More: <http://www.last.fm/api/show/track.love>
-
-#define __removeTag__ \
--- | Remove a user's tag from a track. \
-More: <http://www.last.fm/api/show/track.removeTag>
-
-#define __scrobble__ \
--- | Used to add a track-play to a user's profile. \
-More: <http://www.last.fm/api/show/track.scrobble>
-
-#define __search__ \
--- | Search for a track by track name. Returns track matches sorted by relevance. \
-More: <http://www.last.fm/api/show/track.search>
-
-#define __share__ \
--- | Share a track twith one or more Last.fm users or other friends. \
-More: <http://www.last.fm/api/show/track.share>
-
-#define __unban__ \
--- | Unban a track for a user profile. \
-More: <http://www.last.fm/api/show/track.unban>
-
-#define __unlove__ \
--- | Unlove a track for a user profile. \
-More: <http://www.last.fm/api/show/track.unlove>
-
-#define __updateNowPlaying__ \
--- | Used to notify Last.fm that a user has started listening to a track. Parameter names are case sensitive. \
-More: <http://www.last.fm/api/show/track.updateNowPlaying>
diff --git a/docs/user.docs b/docs/user.docs
deleted file mode 100644
--- a/docs/user.docs
+++ /dev/null
@@ -1,101 +0,0 @@
-/* THIS FILE IS NOT INTENDED FOR USER READING. USE HADDOCK DOCUMENTATION INSTEAD. */
-
-#define __getArtistTracks__ \
--- | Get a list of tracks by a given artist scrobbled by this user, including scrobble time. Can be limited to specific timeranges, defaults to all time. \
-More: <http://www.last.fm/api/show/user.getArtistTracks>
-
-#define __getBannedTracks__ \
--- | Returns the tracks banned by the user. \
-More: <http://www.last.fm/api/show/user.getBannedTracks>
-
-#define __getEvents__ \
--- | Get a list of upcoming events that this user is attending. \
-More: <http://www.last.fm/api/show/user.getEvents>
-
-#define __getFriends__ \
--- | Get a list of the user's friends on Last.fm. \
-More: <http://www.last.fm/api/show/user.getFriends>
-
-#define __getInfo__ \
--- | Get information about a user profile. \
-More: <http://www.last.fm/api/show/user.getInfo>
-
-#define __getLovedTracks__ \
--- | Get tracks loved by a user. \
-More: <http://www.last.fm/api/show/user.getLovedTracks>
-
-#define __getNeighbours__ \
--- | Get a list of a user's neighbours on Last.fm. \
-More: <http://www.last.fm/api/show/user.getNeighbours>
-
-#define __getNewReleases__ \
--- | Gets a list of forthcoming releases based on a user's musical taste. \
-More: <http://www.last.fm/api/show/user.getNewReleases>
-
-#define __getPastEvents__ \
--- | Get a paginated list of all events a user has attended in the past. \
-More: <http://www.last.fm/api/show/user.getPastEvents>
-
-#define __getPersonalTags__ \
--- | Get the user's personal tags. \
-More: <http://www.last.fm/api/show/user.getPersonalTags>
-
-#define __getPlaylists__ \
--- | Get a list of a user's playlists on Last.fm. \
-More: <http://www.last.fm/api/show/user.getPlaylists>
-
-#define __getRecentStations__ \
--- | Get a list of the recent Stations listened to by this user. \
-More: <http://www.last.fm/api/show/user.getRecentStations>
-
-#define __getRecentTracks__ \
--- | Get a list of the recent tracks listened to by this user. Also includes the currently playing track with the nowplaying="true" attribute if the user is currently listening. \
-More: <http://www.last.fm/api/show/user.getRecentTracks>
-
-#define __getRecommendedArtists__ \
--- | Get Last.fm artist recommendations for a user. \
-More: <http://www.last.fm/api/show/user.getRecommendedArtists>
-
-#define __getRecommendedEvents__ \
--- | Get a paginated list of all events recommended to a user by Last.fm, based on their listening profile. \
-More: <http://www.last.fm/api/show/user.getRecommendedEvents>
-
-#define __getShouts__ \
--- | Get shouts for this user. Also available as an rss feed. \
-More: <http://www.last.fm/api/show/user.getShouts>
-
-#define __getTopAlbums__ \
--- | Get the top albums listened to by a user. You can stipulate a time period. Sends the overall chart by default. \
-More: <http://www.last.fm/api/show/user.getTopAlbums>
-
-#define __getTopArtists__ \
--- | Get the top artists listened to by a user. You can stipulate a time period. Sends the overall chart by default. \
-More: <http://www.last.fm/api/show/user.getTopArtists>
-
-#define __getTopTags__ \
--- | Get the top tags used by this user. \
-More: <http://www.last.fm/api/show/user.getTopTags>
-
-#define __getTopTracks__ \
--- | Get the top tracks listened to by a user. You can stipulate a time period. Sends the overall chart by default. \
-More: <http://www.last.fm/api/show/user.getTopTracks>
-
-#define __getWeeklyAlbumChart__ \
--- | Get an album chart for a user profile, for a given date range. If no date range is supplied, it will return the most recent album chart for this user. \
-More: <http://www.last.fm/api/show/user.getWeeklyAlbumChart>
-
-#define __getWeeklyArtistChart__ \
--- | Get an artist chart for a user profile, for a given date range. If no date range is supplied, it will return the most recent artist chart for this user. \
-More: <http://www.last.fm/api/show/user.getWeeklyArtistChart>
-
-#define __getWeeklyChartList__ \
--- | Get a list of available charts for this user, expressed as date ranges which can be sent to the chart services. \
-More: <http://www.last.fm/api/show/user.getWeeklyChartList>
-
-#define __getWeeklyTrackChart__ \
--- | Get a track chart for a user profile, for a given date range. If no date range is supplied, it will return the most recent track chart for this user. \
-More: <http://www.last.fm/api/show/user.getWeeklyTrackChart>
-
-#define __shout__ \
--- | Shout on this user's shoutbox. \
-More: <http://www.last.fm/api/show/user.shout>
diff --git a/docs/venue.docs b/docs/venue.docs
deleted file mode 100644
--- a/docs/venue.docs
+++ /dev/null
@@ -1,13 +0,0 @@
-/* THIS FILE IS NOT INTENDED FOR USER READING. USE HADDOCK DOCUMENTATION INSTEAD. */
-
-#define __getEvents__ \
--- | Get a list of upcoming events at this venue. \
-More: <http://www.last.fm/api/show/venue.getEvents>
-
-#define __getPastEvents__ \
--- | Get a paginated list of all the events held at this venue in the past. \
-More: <http://www.last.fm/api/show/venue.getPastEvents>
-
-#define __search__ \
--- | Search for a venue by venue name. \
-More: <http://www.last.fm/api/show/venue.search>
diff --git a/examples/json-auth.hs b/examples/json-auth.hs
deleted file mode 100644
--- a/examples/json-auth.hs
+++ /dev/null
@@ -1,51 +0,0 @@
-module Main where
-
-import Control.Applicative (empty)
-import System.Exit (ExitCode(ExitFailure), exitSuccess, exitWith)
-
-import qualified Data.ByteString.Lazy as B
-import Data.Aeson
-import Network.Lastfm
-import Test.HUnit
-import qualified JSON.Album as Album
-import qualified JSON.Artist as Artist
-import qualified JSON.Event as Event
-import qualified JSON.Library as Library
-import qualified JSON.Playlist as Playlist
-import qualified JSON.Radio as Radio
-import qualified JSON.User as User
-import qualified JSON.Track as Track
-
-
-main ∷ IO ()
-main =
-  do keys ← B.readFile "examples/lastfm-keys.json"
-     case decode keys of
-       Just (Keys ak sk s) →
-         do rs ← mapM (runTestTT . TestList . \f → f ak sk s)
-              [ Album.private
-              , Artist.private
-              , Event.private
-              , Library.private
-              , Playlist.private
-              , Radio.private
-              , User.private
-              , Track.private
-              ]
-            let fs = sum $ map failures rs
-            case fs of
-              0 → exitSuccess
-              n → exitWith (ExitFailure n)
-       Nothing → exitWith (ExitFailure 127)
-
-
-data Keys = Keys APIKey SessionKey Secret
-
-
-instance FromJSON Keys where
-  parseJSON (Object o) =
-    do ak ← o .: "APIKey"
-       sk ← o .: "SessionKey"
-       s ← o .: "Secret"
-       return $ Keys (APIKey ak) (SessionKey sk) (Secret s)
-  parseJSON _ = empty
diff --git a/examples/json-no-auth.hs b/examples/json-no-auth.hs
deleted file mode 100644
--- a/examples/json-no-auth.hs
+++ /dev/null
@@ -1,42 +0,0 @@
-{-# LANGUAGE UnicodeSyntax #-}
-module Main where
-
-import System.Exit (ExitCode(ExitFailure), exitSuccess, exitWith)
-
-import Test.HUnit
-import qualified JSON.Album as Album
-import qualified JSON.Artist as Artist
-import qualified JSON.Chart as Chart
-import qualified JSON.Event as Event
-import qualified JSON.Geo as Geo
-import qualified JSON.Group as Group
-import qualified JSON.Library as Library
-import qualified JSON.Radio as Radio
-import qualified JSON.Tag as Tag
-import qualified JSON.Tasteometer as Tasteometer
-import qualified JSON.Track as Track
-import qualified JSON.Venue as Venue
-import qualified JSON.User as User
-
-
-main ∷ IO ()
-main =
-  do rs ← mapM (runTestTT . TestList)
-       [ Album.public
-       , Artist.public
-       , Chart.public
-       , Event.public
-       , Geo.public
-       , Group.public
-       , Library.public
-       , Radio.public
-       , Tag.public
-       , Tasteometer.public
-       , Track.public
-       , Venue.public
-       , User.public
-       ]
-     let fs = sum $ map failures rs
-     case fs of
-       0 → exitSuccess
-       n → exitWith (ExitFailure n)
diff --git a/liblastfm.cabal b/liblastfm.cabal
--- a/liblastfm.cabal
+++ b/liblastfm.cabal
@@ -1,121 +1,53 @@
 name: liblastfm
-version: 0.0.3.8
-synopsis: Wrapper to Lastfm API
+version: 0.1.0.0
+synopsis: Lastfm API interface
 license: MIT
 license-file: LICENSE
 author: Matvey Aksenov, Dmitry Malikov
 maintainer: Matvey Aksenov <matvey.aksenov@gmail.com>
 category: Network APIs
-description: Simple but complete wrapper to Lastfm API. Can send some stuff and receive reponses from Lastfm. JSON/XML parsing is an exercise for the user.
-cabal-version: >= 1.9.2
+description:
+  Provides interface to Lastfm REST API, supports XML and JSON formats.
+cabal-version: >= 1.8
 build-type: Simple
-extra-source-files: docs/*.docs
 
-flag test-authentication
-  description: Test authentication dependent LastFM API too.
-  default: False
-
 library
-  build-depends: base >= 3 && < 5,
-                 template-haskell >= 2.4 && < 2.9,
-                 bytestring >= 0.9 && < 0.11,
-                 mtl == 2.*,
-                 curl >= 1.3.7,
-                 pureMD5 == 2.1.*,
-                 urlencoded >= 0.3 && < 0.5,
-                 attoparsec >= 0.10,
-                 aeson == 0.6.*,
-                 xml == 1.3.12,
-                 utf8-string == 0.3.*
+  build-depends: base >= 4.6 && < 5,
+                 bytestring,
+                 containers >= 0.5,
+                 text,
+                 cereal,
+                 http-conduit,
+                 http-types,
+                 pureMD5,
+                 crypto-api,
+                 network >= 2.4,
+                 aeson
   hs-source-dirs: src
   exposed-modules: Network.Lastfm
-                   Network.Lastfm.JSON.Album
-                   Network.Lastfm.JSON.Artist
-                   Network.Lastfm.JSON.Auth
-                   Network.Lastfm.JSON.Chart
-                   Network.Lastfm.JSON.Event
-                   Network.Lastfm.JSON.Geo
-                   Network.Lastfm.JSON.Group
-                   Network.Lastfm.JSON.Library
-                   Network.Lastfm.JSON.Playlist
-                   Network.Lastfm.JSON.Radio
-                   Network.Lastfm.JSON.Tag
-                   Network.Lastfm.JSON.Tasteometer
-                   Network.Lastfm.JSON.Track
-                   Network.Lastfm.JSON.User
-                   Network.Lastfm.JSON.Venue
-                   Network.Lastfm.XML.Album
-                   Network.Lastfm.XML.Artist
-                   Network.Lastfm.XML.Auth
-                   Network.Lastfm.XML.Chart
-                   Network.Lastfm.XML.Event
-                   Network.Lastfm.XML.Geo
-                   Network.Lastfm.XML.Group
-                   Network.Lastfm.XML.Library
-                   Network.Lastfm.XML.Playlist
-                   Network.Lastfm.XML.Radio
-                   Network.Lastfm.XML.Tag
-                   Network.Lastfm.XML.Tasteometer
-                   Network.Lastfm.XML.Track
-                   Network.Lastfm.XML.User
-                   Network.Lastfm.XML.Venue
-  other-modules:   Network.Lastfm.Internal
-                   Network.Lastfm.TH
-                   Network.Lastfm.Error
-                   Network.Lastfm.JSON
-                   Network.Lastfm.XML
-                   Network.Lastfm.API.Album
-                   Network.Lastfm.API.Artist
-                   Network.Lastfm.API.Auth
-                   Network.Lastfm.API.Chart
-                   Network.Lastfm.API.Event
-                   Network.Lastfm.API.Geo
-                   Network.Lastfm.API.Group
-                   Network.Lastfm.API.Library
-                   Network.Lastfm.API.Playlist
-                   Network.Lastfm.API.Radio
-                   Network.Lastfm.API.Tag
-                   Network.Lastfm.API.Tasteometer
-                   Network.Lastfm.API.Track
-                   Network.Lastfm.API.User
-                   Network.Lastfm.API.Venue
-  extensions: UnicodeSyntax
+                   Network.Lastfm.Authentication
+                   Network.Lastfm.Album
+                   Network.Lastfm.Artist
+                   Network.Lastfm.Chart
+                   Network.Lastfm.Event
+                   Network.Lastfm.Geo
+                   Network.Lastfm.Group
+                   Network.Lastfm.Library
+                   Network.Lastfm.Playlist
+                   Network.Lastfm.Radio
+                   Network.Lastfm.Tag
+                   Network.Lastfm.Tasteometer
+                   Network.Lastfm.Track
+                   Network.Lastfm.User
+                   Network.Lastfm.Venue
+                   Network.Lastfm.Request
+                   Network.Lastfm.Response
+                   Network.Lastfm.Internal
   ghc-options: -Wall
                -fno-warn-unused-do-bind
-  include-dirs: docs
+               -funbox-strict-fields
 
-test-suite json-no-authentication
-  build-depends: base >= 3 && < 5,
-                 bytestring >= 0.9 && < 0.11,
-                 attoparsec >= 0.10,
-                 aeson == 0.6.*,
-                 HUnit >= 1.2.4,
-                 liblastfm
-  type: exitcode-stdio-1.0
-  main-is: json-no-auth.hs
-  hs-source-dirs: examples
-  extensions: OverloadedStrings, UnicodeSyntax
-  ghc-options: -Wall
-               -fno-warn-unused-do-bind
-               -fno-warn-orphans
 
-test-suite json-authentication
-  build-depends: base >= 3 && < 5,
-                 bytestring >= 0.9 && < 0.11,
-                 attoparsec >= 0.10,
-                 aeson == 0.6.*,
-                 HUnit >= 1.2.4,
-                 liblastfm
-  type: exitcode-stdio-1.0
-  main-is: json-auth.hs
-  hs-source-dirs: examples
-  extensions: OverloadedStrings, UnicodeSyntax
-  ghc-options: -Wall
-               -fno-warn-unused-do-bind
-               -fno-warn-orphans
-  if !flag(test-authentication)
-     buildable: False
-
 source-repository head
   type:     git
-  location: https://github.com/supki/haskell-liblastfm
+  location: https://github.com/supki/liblastfm
diff --git a/src/Network/Lastfm.hs b/src/Network/Lastfm.hs
--- a/src/Network/Lastfm.hs
+++ b/src/Network/Lastfm.hs
@@ -1,251 +1,13 @@
-{-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE TemplateHaskell #-}
 module Network.Lastfm
-  ( Lastfm, Response, LastfmError(..)
-  , Secret(..)
-  , Argument(..)
-  , Album(..)
-  , AlbumArtist(..)
-  , APIKey(..)
-  , Artist(..)
-  , AuthToken(..)
-  , Autocorrect(..)
-  , Bitrate(..)
-  , ChosenByUser(..)
-  , Context(..)
-  , Country(..)
-  , Description(..)
-  , Group(..)
-  , Language(..)
-  , Latitude(..)
-  , Location(..)
-  , Longitude(..)
-  , Mbid(..)
-  , Message(..)
-  , Method(..)
-  , Metro(..)
-  , Multiplier(..)
-  , Name(..)
-  , Order(..)
-  , Period(..)
-  , Recipient(..)
-  , SessionKey(..)
-  , Status(..)
-  , Station(..)
-  , StreamId(..)
-  , Tag(..)
-  , TaggingType(..)
-  , Title(..)
-  , Token(..)
-  , Track(..)
-  , User(..)
-  , Username(..)
-  , Value(..)
-  , Venuename(..)
-  , BuyLinks(..)
-  , Discovery(..)
-  , FestivalsOnly(..)
-  , Public(..)
-  , RecentTracks(..)
-  , RTP(..)
-  , UseRecs(..)
-  , Distance(..)
-  , Duration(..)
-  , Event(..)
-  , Limit(..)
-  , Page(..)
-  , Playlist(..)
-  , TrackNumber(..)
-  , Venue(..)
-  , End(..)
-  , EndTimestamp(..)
-  , Fingerprint(..)
-  , From(..)
-  , Start(..)
-  , StartTimestamp(..)
-  , Timestamp(..)
-  , To(..)
-  , simple
+  ( -- * Utilities for constructing requests
+    module Network.Lastfm.Request
+    -- * Utilities for signing and sending requests
+  , module Network.Lastfm.Response
+    -- * Control.Applicative is re-exported for convenience
+  , module Control.Applicative
   ) where
 
-import Control.Applicative ((<$>), empty)
-import Control.Monad (liftM)
-import Data.List (intercalate)
-import Data.Maybe (fromJust)
-
-import Data.Aeson ((.:), FromJSON, decode, parseJSON)
-import qualified Data.Aeson
-import Data.ByteString.Lazy.Char8 (ByteString)
-
-import Network.Lastfm.Error
-import Network.Lastfm.TH
-
-
-type Lastfm a = IO (Either LastfmError a)
-type Response = ByteString
-
-
-newtype Secret = Secret String deriving Show
-
-
-$(newtypes "String" ["Album", "AlbumArtist", "APIKey", "Artist", "AuthToken",
-  "Context", "Country", "Description", "Group", "Language", "Latitude",
-  "Location", "Longitude", "Mbid", "Message", "Method", "Metro", "Name",
-  "Recipient", "SessionKey", "Station", "StreamId", "Tag", "TaggingType",
-  "Title", "Token", "Track", "User", "Username", "Venuename", "ChosenByUser"])
-$(newtypes "Bool" ["Autocorrect", "BuyLinks", "Discovery", "FestivalsOnly", "Public", "RecentTracks", "RTP", "UseRecs"])
-$(newtypes "Int" ["Distance", "Duration", "Event", "Limit", "Page", "Playlist", "TrackNumber", "Venue"])
-$(newtypes "Integer" ["End", "EndTimestamp", "Fingerprint", "From", "Start", "StartTimestamp", "Timestamp", "To"])
-
-
-data Bitrate = B64 | B128 deriving Show
-data Multiplier = M1 | M2 deriving Show
-data Order = Popularity | DateAdded deriving Show
-data Status = Yes | Maybe | No deriving Show
-data Value = ValueUser User
-           | ValueArtists [Artist]
-data Period = Week | Quater | HalfYear | Year | Overall deriving Show
-
-
-instance Show Value where
-  show (ValueUser _) = "user"
-  show (ValueArtists _) = "artists"
-
-
-instance FromJSON Token where
-  parseJSON (Data.Aeson.Object v) = Token <$> v .: "token"
-  parseJSON _ = empty
-
-
-instance FromJSON SessionKey where
-  parseJSON (Data.Aeson.Object v) = SessionKey <$> ((v .: "session") >>= (.: "key"))
-  parseJSON _ = empty
-
-
-simple ∷ (FromJSON a, Monad m) ⇒ m ByteString → m a
-simple = liftM (fromJust . decode)
-
-
-class Argument a where
-  key ∷ a → String
-  value ∷ a → String
-
-
-instance Argument a ⇒ Argument (Maybe a) where key = maybe "" key; value = maybe "" value
-instance Argument a ⇒ Argument [a] where
-  key (a:_) = key a ++ "s"
-  key [] = ""
-  value = intercalate "," . map value
-
-
-boolToString ∷ Bool → String
-boolToString True = "1"
-boolToString False = "0"
-
-
---instance Argument $first where key = const $second; value ($first a) = $func a
-$(instances "id"
-  [ ("Album","album")
-  , ("AlbumArtist", "albumartist")
-  , ("APIKey", "api_key")
-  , ("Artist", "artist")
-  , ("AuthToken", "authToken")
-  , ("ChosenByUser", "chosenByUser")
-  , ("Context", "context")
-  , ("Country", "country")
-  , ("Description", "description")
-  , ("Group", "group")
-  , ("Language", "lang")
-  , ("Latitude", "lat")
-  , ("Location", "location")
-  , ("Longitude", "long")
-  , ("Mbid", "mbid")
-  , ("Message", "message")
-  , ("Method", "method")
-  , ("Metro", "metro")
-  , ("Name", "name")
-  , ("Recipient", "recipient")
-  , ("SessionKey", "sk")
-  , ("Station", "station")
-  , ("StreamId", "streamId")
-  , ("Tag", "tag")
-  , ("TaggingType", "taggingtype")
-  , ("Title", "title")
-  , ("Token", "token")
-  , ("Track", "track")
-  , ("User", "user")
-  , ("Username", "username")
-  , ("Venuename", "venue")
-  ])
-
-
-$( instances "boolToString"
-  [ ("Autocorrect", "autocorrect")
-  , ("BuyLinks", "buylinks")
-  , ("Discovery", "discovery")
-  , ("FestivalsOnly", "festivalsonly")
-  , ("Public", "public")
-  , ("RecentTracks", "recenttracks")
-  , ("RTP", "rtp")
-  , ("UseRecs", "userecs")
-  ])
-
-
-$( instances "show"
-  [ ("Distance", "distance")
-  , ("Duration", "duration")
-  , ("Event", "event")
-  , ("Limit", "limit")
-  , ("Page", "page")
-  , ("Playlist", "playlistID")
-  , ("TrackNumber", "tracknumber")
-  , ("Venue", "venue")
-  , ("End", "end")
-  , ("EndTimestamp", "endTimestamp")
-  , ("Fingerprint", "fingerprintid")
-  , ("From", "from")
-  , ("Start", "start")
-  , ("StartTimestamp", "startTimestamp")
-  , ("Timestamp", "timestamp")
-  , ("To", "to")
-  ])
-
-
-instance Argument Bitrate where
-  key = const "bitrate"
-  value B64 = "64"
-  value B128 = "128"
-
-
-instance Argument Multiplier where
-  key = const "speed_multiplier"
-  value M1 = "1.0"
-  value M2 = "2.0"
-
-
-instance Argument Order where
-  key = const "order"
-  value Popularity = "popularity"
-  value DateAdded = "dateadded"
-
-
-instance Argument Status where
-  key = const "status"
-  value Yes = "0"
-  value Maybe = "1"
-  value No = "2"
-
-
-instance Argument Value where
-  key = const "value"
-  value (ValueUser u) = value u
-  value (ValueArtists as) = value as
-
+import Control.Applicative
 
-instance Argument Period where
-  key = const "period"
-  value Week = "7day"
-  value Quater = "3month"
-  value HalfYear = "6month"
-  value Year = "12month"
-  value Overall = "overall"
+import Network.Lastfm.Request
+import Network.Lastfm.Response
diff --git a/src/Network/Lastfm/API/Album.hs b/src/Network/Lastfm/API/Album.hs
deleted file mode 100644
--- a/src/Network/Lastfm/API/Album.hs
+++ /dev/null
@@ -1,98 +0,0 @@
-module Network.Lastfm.API.Album
-  ( addTags, getBuyLinks, getInfo, getShouts, getTags
-  , getTopTags, removeTag, search, share
-  ) where
-
-import Control.Arrow ((|||))
-import Network.Lastfm.Internal
-
-addTags ∷ Format → (Artist, Album) → [Tag] → APIKey → SessionKey → Secret → Lastfm Response
-addTags t (artist, album) tags apiKey sessionKey secret = callAPIsigned t secret
-  [ (#) (Method "album.addTags")
-  , (#) artist
-  , (#) album
-  , (#) tags
-  , (#) apiKey
-  , (#) sessionKey
-  ]
-
-getBuyLinks ∷ Format → Either (Artist, Album) Mbid → Maybe Autocorrect → Country → APIKey → Lastfm Response
-getBuyLinks t a autocorrect country apiKey = callAPI t $
-  target a ++
-  [ (#) (Method "album.getBuyLinks")
-  , (#) autocorrect
-  , (#) country
-  , (#) apiKey
-  ]
-
-getInfo ∷ Format → Either (Artist, Album) Mbid → Maybe Autocorrect → Maybe Language → Maybe Username → APIKey → Lastfm Response
-getInfo t a autocorrect lang username apiKey = callAPI t $
-  target a ++
-  [ (#) (Method "album.getInfo")
-  , (#) autocorrect
-  , (#) lang
-  , (#) username
-  , (#) apiKey
-  ]
-
-getShouts ∷ Format → Either (Artist, Album) Mbid → Maybe Autocorrect → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-getShouts t a autocorrect page limit apiKey = callAPI t $
-  target a ++
-  [ (#) (Method "album.getShouts")
-  , (#) autocorrect
-  , (#) page
-  , (#) limit
-  , (#) apiKey
-  ]
-
-getTags ∷ Format → Either (Artist, Album) Mbid → Maybe Autocorrect → Either User (SessionKey, Secret) → APIKey → Lastfm Response
-getTags t a autocorrect b apiKey = case b of
-  Left user → callAPI t $ target a ++ [(#) user] ++ args
-  Right (sessionKey, secret) → callAPIsigned t secret $ target a ++ [(#) sessionKey] ++ args
-  where args =
-          [ (#) (Method "album.getTags")
-          , (#) autocorrect
-          , (#) apiKey
-          ]
-
-getTopTags ∷ Format → Either (Artist, Album) Mbid → Maybe Autocorrect → APIKey → Lastfm Response
-getTopTags t a autocorrect apiKey = callAPI t $
-  target a ++
-  [ (#) (Method "album.getTopTags")
-  , (#) autocorrect
-  , (#) apiKey
-  ]
-
-removeTag ∷ Format → Artist → Album → Tag → APIKey → SessionKey → Secret → Lastfm Response
-removeTag t artist album tag apiKey sessionKey secret = callAPIsigned t secret
-  [ (#) (Method "album.removeTag")
-  , (#) artist
-  , (#) album
-  , (#) tag
-  , (#) apiKey
-  , (#) sessionKey
-  ]
-
-search ∷ Format → Album → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-search t album page limit apiKey = callAPI t
-  [ (#) (Method "album.search")
-  , (#) album
-  , (#) page
-  , (#) limit
-  , (#) apiKey
-  ]
-
-share ∷ Format → Artist → Album → Recipient → Maybe Message → Maybe Public → APIKey → SessionKey → Secret → Lastfm Response
-share t artist album recipient message public apiKey sessionKey secret = callAPIsigned t secret
-  [ (#) (Method "album.share")
-  , (#) artist
-  , (#) album
-  , (#) public
-  , (#) message
-  , (#) recipient
-  , (#) apiKey
-  , (#) sessionKey
-  ]
-
-target ∷ Either (Artist, Album) Mbid → [(String, String)]
-target = (\(artist, album) → [(#) artist, (#) album]) ||| return . (#)
diff --git a/src/Network/Lastfm/API/Artist.hs b/src/Network/Lastfm/API/Artist.hs
deleted file mode 100644
--- a/src/Network/Lastfm/API/Artist.hs
+++ /dev/null
@@ -1,180 +0,0 @@
-module Network.Lastfm.API.Artist
-  ( addTags, getCorrection, getEvents, getImages, getInfo
-  , getPastEvents, getPodcast, getShouts, getSimilar, getTags, getTopAlbums
-  , getTopFans, getTopTags, getTopTracks, removeTag, search, share, shout
-  ) where
-
-import Control.Arrow ((|||))
-import Network.Lastfm.Internal
-
-addTags ∷ Format → Artist → [Tag] → APIKey → SessionKey → Secret → Lastfm Response
-addTags t artist tags apiKey sessionKey secret = callAPIsigned t secret
-  [ (#) (Method "artist.addTags")
-  , (#) artist
-  , (#) tags
-  , (#) apiKey
-  , (#) sessionKey
-  ]
-
-getCorrection ∷ Format → Artist → APIKey → Lastfm Response
-getCorrection t artist apiKey = callAPI t
-  [ (#) (Method "artist.getCorrection")
-  , (#) artist
-  , (#) apiKey
-  ]
-
-getEvents ∷ Format → Either Artist Mbid → Maybe Autocorrect → Maybe Page → Maybe Limit → Maybe FestivalsOnly → APIKey → Lastfm Response
-getEvents t a autocorrect page limit festivalsOnly apiKey = callAPI t $
-  target a ++
-  [ (#) (Method "artist.getEvents")
-  , (#) autocorrect
-  , (#) page
-  , (#) limit
-  , (#) festivalsOnly
-  , (#) apiKey
-  ]
-
-getImages ∷ Format → Either Artist Mbid → Maybe Autocorrect → Maybe Page → Maybe Limit → Maybe Order → APIKey → Lastfm Response
-getImages t a autocorrect page limit order apiKey = callAPI t $
-  target a ++
-  [ (#) (Method "artist.getImages")
-  , (#) autocorrect
-  , (#) page
-  , (#) limit
-  , (#) order
-  , (#) apiKey
-  ]
-
-getInfo ∷ Format → Either Artist Mbid → Maybe Autocorrect → Maybe Language → Maybe Username → APIKey → Lastfm Response
-getInfo t a autocorrect language username apiKey = callAPI t $
-  target a ++
-  [ (#) (Method "artist.getInfo")
-  , (#) autocorrect
-  , (#) language
-  , (#) username
-  , (#) apiKey
-  ]
-
-getPastEvents ∷ Format → Either Artist Mbid → Maybe Autocorrect → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-getPastEvents t a autocorrect page limit apiKey = callAPI t $
-  target a ++
-  [ (#) (Method "artist.getPastEvents")
-  , (#) autocorrect
-  , (#) page
-  , (#) limit
-  , (#) apiKey
-  ]
-
-getPodcast ∷ Format → Either Artist Mbid → Maybe Autocorrect → APIKey → Lastfm Response
-getPodcast t a autocorrect apiKey = callAPI t $
-  target a ++
-  [ (#) (Method "artist.getPodcast")
-  , (#) autocorrect
-  , (#) apiKey
-  ]
-
-getShouts ∷ Format → Either Artist Mbid → Maybe Autocorrect → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-getShouts t a autocorrect page limit apiKey = callAPI t $
-  target a ++
-  [ (#) (Method "artist.getShouts")
-  , (#) autocorrect
-  , (#) page
-  , (#) limit
-  , (#) apiKey
-  ]
-
-getSimilar ∷ Format → Either Artist Mbid → Maybe Autocorrect → Maybe Limit → APIKey → Lastfm Response
-getSimilar t a autocorrect limit apiKey = callAPI t $
-  target a ++
-  [ (#) (Method "artist.getSimilar")
-  , (#) autocorrect
-  , (#) limit
-  , (#) apiKey
-  ]
-
-getTags ∷ Format → Either Artist Mbid → Maybe Autocorrect → Either User (SessionKey, Secret) → APIKey → Lastfm Response
-getTags t a autocorrect b apiKey = case b of
-  Left user → callAPI t $ target a ++ [(#) user] ++ args
-  Right (sessionKey, secret) → callAPIsigned t secret $ target a ++ [(#) sessionKey] ++ args
-  where args =
-          [ (#) (Method "artist.getTags")
-          , (#) autocorrect
-          , (#) apiKey
-          ]
-
-getTopAlbums ∷ Format → Either Artist Mbid → Maybe Autocorrect → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-getTopAlbums t a autocorrect page limit apiKey = callAPI t $
-  target a ++
-  [ (#) (Method "artist.getTopAlbums")
-  , (#) autocorrect
-  , (#) page
-  , (#) limit
-  , (#) apiKey
-  ]
-
-getTopFans ∷ Format → Either Artist Mbid → Maybe Autocorrect → APIKey → Lastfm Response
-getTopFans t a autocorrect apiKey = callAPI t $
-  target a ++
-  [ (#) (Method "artist.getTopFans")
-  , (#) autocorrect
-  , (#) apiKey
-  ]
-
-getTopTags ∷ Format → Either Artist Mbid → Maybe Autocorrect → APIKey → Lastfm Response
-getTopTags t a autocorrect apiKey = callAPI t $
-  target a ++
-  [ (#) (Method "artist.getTopTags")
-  , (#) autocorrect
-  , (#) apiKey
-  ]
-
-getTopTracks ∷ Format → Either Artist Mbid → Maybe Autocorrect → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-getTopTracks t a autocorrect page limit apiKey = callAPI t $
-  target a ++
-  [ (#) (Method "artist.getTopTracks")
-  , (#) autocorrect
-  , (#) page
-  , (#) limit
-  , (#) apiKey
-  ]
-
-removeTag ∷ Format → Artist → Tag → APIKey → SessionKey → Secret → Lastfm Response
-removeTag t artist tag apiKey sessionKey secret = callAPIsigned t secret
-  [ (#) (Method "artist.removeTag")
-  , (#) artist
-  , (#) tag
-  , (#) apiKey
-  , (#) sessionKey
-  ]
-
-search ∷ Format → Artist → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-search t artist page limit apiKey = callAPI t
-  [ (#) (Method "artist.search")
-  , (#) artist
-  , (#) apiKey
-  , (#) page
-  , (#) limit
-  ]
-
-share ∷ Format → Artist → Recipient → Maybe Message → Maybe Public → APIKey → SessionKey → Secret → Lastfm Response
-share t artist recipient message public apiKey sessionKey secret = callAPIsigned t secret
-  [ (#) (Method "artist.share")
-  , (#) artist
-  , (#) recipient
-  , (#) apiKey
-  , (#) sessionKey
-  , (#) public
-  , (#) message
-  ]
-
-shout ∷ Format → Artist → Message → APIKey → SessionKey → Secret → Lastfm Response
-shout t artist message apiKey sessionKey secret = callAPIsigned t secret
-  [ (#) (Method "artist.shout")
-  , (#) artist
-  , (#) message
-  , (#) apiKey
-  , (#) sessionKey
-  ]
-
-target ∷ Either Artist Mbid → [(String, String)]
-target = return . (#) ||| return . (#)
diff --git a/src/Network/Lastfm/API/Auth.hs b/src/Network/Lastfm/API/Auth.hs
deleted file mode 100644
--- a/src/Network/Lastfm/API/Auth.hs
+++ /dev/null
@@ -1,25 +0,0 @@
-module Network.Lastfm.API.Auth
-  ( getMobileSession, getSession, getToken
-  , getAuthorizeTokenLink
-  ) where
-
-import Data.Functor ((<$>))
-
-import Network.Lastfm.Internal
-import Network.Lastfm.JSON (json)
-
-
-getMobileSession ∷ Username → APIKey → AuthToken → Lastfm SessionKey
-getMobileSession username apiKey token = simple <$> callAPI json [(#) (Method "auth.getMobileSession"), (#) username, (#) token, (#) apiKey]
-
-
-getSession ∷ APIKey → Token → Secret → Lastfm SessionKey
-getSession apiKey token secret = simple <$> callAPIsigned json secret [(#) (Method "auth.getSession"), (#) apiKey, (#) token]
-
-
-getToken ∷ APIKey → Lastfm Token
-getToken apiKey = simple <$> callAPI json [(#) (Method "auth.getToken"), (#) apiKey]
-
-
-getAuthorizeTokenLink ∷ APIKey → Token → String
-getAuthorizeTokenLink apiKey token = "http://www.last.fm/api/auth/?api_key=" ++ value apiKey ++ "&token=" ++ value token
diff --git a/src/Network/Lastfm/API/Chart.hs b/src/Network/Lastfm/API/Chart.hs
deleted file mode 100644
--- a/src/Network/Lastfm/API/Chart.hs
+++ /dev/null
@@ -1,32 +0,0 @@
-module Network.Lastfm.API.Chart
-  ( getHypedArtists, getHypedTracks, getLovedTracks
-  , getTopArtists, getTopTags, getTopTracks
-  ) where
-
-import Network.Lastfm.Internal
-
-getHypedArtists ∷ Format → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-getHypedArtists = get "getHypedArtists"
-
-getHypedTracks ∷ Format → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-getHypedTracks = get "getHypedTracks"
-
-getLovedTracks ∷ Format → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-getLovedTracks = get "getLovedTracks"
-
-getTopArtists ∷ Format → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-getTopArtists = get "getTopArtists"
-
-getTopTags ∷ Format → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-getTopTags = get "getTopTags"
-
-getTopTracks ∷ Format → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-getTopTracks = get "getTopTracks"
-
-get ∷ String → Format → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-get method t page limit apiKey = callAPI t
-  [ (#) (Method $ "chart." ++ method)
-  , (#) page
-  , (#) limit
-  , (#) apiKey
-  ]
diff --git a/src/Network/Lastfm/API/Event.hs b/src/Network/Lastfm/API/Event.hs
deleted file mode 100644
--- a/src/Network/Lastfm/API/Event.hs
+++ /dev/null
@@ -1,59 +0,0 @@
-module Network.Lastfm.API.Event
-  ( attend, getAttendees, getInfo, getShouts, share, shout
-  ) where
-
-import Network.Lastfm.Internal
-
-attend ∷ Format → Event → Status → APIKey → SessionKey → Secret → Lastfm Response
-attend t event status apiKey sessionKey secret = callAPIsigned t secret
-  [ (#) (Method "event.attend")
-  , (#) event
-  , (#) status
-  , (#) apiKey
-  , (#) sessionKey
-  ]
-
-getAttendees ∷ Format → Event → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-getAttendees t event page limit apiKey = callAPI t
-  [ (#) (Method "event.getAttendees")
-  , (#) event
-  , (#) page
-  , (#) limit
-  , (#) apiKey
-  ]
-
-getInfo ∷ Format → Event → APIKey → Lastfm Response
-getInfo t event apiKey = callAPI t
-  [ (#) (Method "event.getInfo")
-  , (#) event
-  , (#) apiKey
-  ]
-
-getShouts ∷ Format → Event → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-getShouts t event page limit apiKey = callAPI t
-  [ (#) (Method "event.getShouts")
-  , (#) event
-  , (#) page
-  , (#) limit
-  , (#) apiKey
-  ]
-
-share ∷ Format → Event → Recipient → Maybe Message → Maybe Public → APIKey → SessionKey → Secret → Lastfm Response
-share t event recipient message public apiKey sessionKey secret = callAPIsigned t secret
-  [ (#) (Method "event.share")
-  , (#) event
-  , (#) public
-  , (#) message
-  , (#) recipient
-  , (#) apiKey
-  , (#) sessionKey
-  ]
-
-shout ∷ Format → Event → Message → APIKey → SessionKey → Secret → Lastfm Response
-shout t event message apiKey sessionKey secret = callAPIsigned t secret
-  [ (#) (Method "event.shout")
-  , (#) event
-  , (#) message
-  , (#) apiKey
-  , (#) sessionKey
-  ]
diff --git a/src/Network/Lastfm/API/Geo.hs b/src/Network/Lastfm/API/Geo.hs
deleted file mode 100644
--- a/src/Network/Lastfm/API/Geo.hs
+++ /dev/null
@@ -1,88 +0,0 @@
-module Network.Lastfm.API.Geo
-  ( getEvents, getMetroArtistChart, getMetroHypeArtistChart, getMetroHypeTrackChart
-  , getMetroTrackChart, getMetroUniqueArtistChart, getMetroUniqueTrackChart
-  , getMetroWeeklyChartlist, getMetros, getTopArtists, getTopTracks
-  ) where
-
-import Network.Lastfm.Internal
-
-getEvents ∷ Format
-          → Maybe Latitude
-          → Maybe Longitude
-          → Maybe Location
-          → Maybe Distance
-          → Maybe Page
-          → Maybe Limit
-          → APIKey
-          → Lastfm Response
-getEvents t latitude longitude location distance page limit apiKey = callAPI t
-  [ (#) (Method "geo.getEvents")
-  , (#) latitude
-  , (#) longitude
-  , (#) location
-  , (#) distance
-  , (#) page
-  , (#) limit
-  , (#) apiKey
-  ]
-
-getMetroArtistChart ∷ Format → Country → Metro → Maybe Start → Maybe End → APIKey → Lastfm Response
-getMetroArtistChart = getMetroChart "geo.getMetroArtistChart"
-
-getMetroHypeArtistChart ∷ Format → Country → Metro → Maybe Start → Maybe End → APIKey → Lastfm Response
-getMetroHypeArtistChart = getMetroChart "geo.getMetroHypeArtistChart"
-
-getMetroHypeTrackChart ∷ Format → Country → Metro → Maybe Start → Maybe End → APIKey → Lastfm Response
-getMetroHypeTrackChart = getMetroChart "geo.getMetroHypeTrackChart"
-
-getMetroTrackChart ∷ Format → Country → Metro → Maybe Start → Maybe End → APIKey → Lastfm Response
-getMetroTrackChart = getMetroChart "geo.getMetroTrackChart"
-
-getMetroUniqueArtistChart ∷ Format → Country → Metro → Maybe Start → Maybe End → APIKey → Lastfm Response
-getMetroUniqueArtistChart = getMetroChart "geo.getMetroUniqueArtistChart"
-
-getMetroUniqueTrackChart ∷ Format → Country → Metro → Maybe Start → Maybe End → APIKey → Lastfm Response
-getMetroUniqueTrackChart = getMetroChart "geo.getMetroUniqueTrackChart"
-
-getMetroWeeklyChartlist ∷ Format → Metro → APIKey → Lastfm Response
-getMetroWeeklyChartlist t metro apiKey = callAPI t
-  [ (#) (Method "geo.getMetroWeeklyChartlist")
-  , (#) metro
-  , (#) apiKey
-  ]
-
-getMetros ∷ Format → Maybe Country → APIKey → Lastfm Response
-getMetros t country apiKey = callAPI t
-  [ (#) (Method "geo.getMetros")
-  , (#) country
-  , (#) apiKey
-  ]
-
-getTopArtists ∷ Format → Country → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-getTopArtists t country page limit apiKey = callAPI t
-  [ (#) (Method "geo.getTopArtists")
-  , (#) country
-  , (#) page
-  , (#) limit
-  , (#) apiKey
-  ]
-
-getTopTracks ∷ Format → Country → Maybe Location → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-getTopTracks t country location page limit apiKey = callAPI t
-  [ (#) (Method "geo.getTopTracks")
-  , (#) country
-  , (#) location
-  , (#) page
-  , (#) limit
-  , (#) apiKey
-  ]
-
-getMetroChart ∷ String → Format → Country → Metro → Maybe Start → Maybe End → APIKey → Lastfm Response
-getMetroChart method t country metro start end apiKey = callAPI t
-  [ (#) (Method method)
-  , (#) country
-  , (#) metro
-  , (#) start
-  , (#) end
-  , (#) apiKey
-  ]
diff --git a/src/Network/Lastfm/API/Group.hs b/src/Network/Lastfm/API/Group.hs
deleted file mode 100644
--- a/src/Network/Lastfm/API/Group.hs
+++ /dev/null
@@ -1,56 +0,0 @@
-module Network.Lastfm.API.Group
-  ( getHype, getMembers, getWeeklyChartList, getWeeklyAlbumChart, getWeeklyArtistChart, getWeeklyTrackChart
-  ) where
-
-import Network.Lastfm.Internal
-
-getHype ∷ Format → Group → APIKey → Lastfm Response
-getHype t group apiKey = callAPI t
-  [ (#) (Method "group.getHype")
-  , (#) group
-  , (#) apiKey
-  ]
-
-getMembers ∷ Format → Group → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-getMembers t group page limit apiKey = callAPI t
-  [ (#) (Method "group.getMembers")
-  , (#) group
-  , (#) page
-  , (#) limit
-  , (#) apiKey
-  ]
-
-getWeeklyChartList ∷ Format → Group → APIKey → Lastfm Response
-getWeeklyChartList t group apiKey = callAPI t
-  [ (#) (Method "group.getWeeklyChartList")
-  , (#) group
-  , (#) apiKey
-  ]
-
-getWeeklyAlbumChart ∷ Format → Group → Maybe From → Maybe To → APIKey → Lastfm Response
-getWeeklyAlbumChart t group from to apiKey = callAPI t
-  [ (#) (Method "group.getWeeklyAlbumChart")
-  , (#) group
-  , (#) from
-  , (#) to
-  , (#) apiKey
-  ]
-
-getWeeklyArtistChart ∷ Format → Group → Maybe From → Maybe To → APIKey → Lastfm Response
-getWeeklyArtistChart t group from to apiKey = callAPI t
-  [ (#) (Method "group.getWeeklyArtistChart")
-  , (#) group
-  , (#) from
-  , (#) to
-  , (#) apiKey
-  ]
-
-
-getWeeklyTrackChart ∷ Format → Group → Maybe From → Maybe To → APIKey → Lastfm Response
-getWeeklyTrackChart t group from to apiKey = callAPI t
-  [ (#) (Method "group.getWeeklyTrackChart")
-  , (#) group
-  , (#) from
-  , (#) to
-  , (#) apiKey
-  ]
diff --git a/src/Network/Lastfm/API/Library.hs b/src/Network/Lastfm/API/Library.hs
deleted file mode 100644
--- a/src/Network/Lastfm/API/Library.hs
+++ /dev/null
@@ -1,98 +0,0 @@
-module Network.Lastfm.API.Library
-  ( addAlbum, addArtist, addTrack, getAlbums, getArtists, getTracks
-  , removeAlbum, removeArtist, removeScrobble, removeTrack
-  ) where
-
-import Network.Lastfm.Internal
-
-addAlbum ∷ Format → Artist → Album → APIKey → SessionKey → Secret → Lastfm Response
-addAlbum t artist album apiKey sessionKey secret = callAPIsigned t secret
-  [ (#) (Method "library.addAlbum")
-  , (#) artist
-  , (#) album
-  , (#) apiKey
-  , (#) sessionKey
-  ]
-
-addArtist ∷ Format → Artist → APIKey → SessionKey → Secret → Lastfm Response
-addArtist t artist apiKey sessionKey secret = callAPIsigned t secret
-  [ (#) (Method "library.addArtist")
-  , (#) artist
-  , (#) apiKey
-  , (#) sessionKey
-  ]
-
-addTrack ∷ Format → Artist → Track → APIKey → SessionKey → Secret → Lastfm Response
-addTrack t artist track apiKey sessionKey secret = callAPIsigned t secret
-  [ (#) (Method "library.addTrack")
-  , (#) artist
-  , (#) track
-  , (#) apiKey
-  , (#) sessionKey
-  ]
-
-getAlbums ∷ Format → User → Maybe Artist → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-getAlbums t user artist page limit apiKey = callAPI t
-  [ (#) (Method "library.getAlbums")
-  , (#) user
-  , (#) artist
-  , (#) page
-  , (#) limit
-  , (#) apiKey
-  ]
-
-getArtists ∷ Format → User → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-getArtists t user page limit apiKey = callAPI t
-  [ (#) (Method "library.getArtists")
-  , (#) user
-  , (#) page
-  , (#) limit
-  , (#) apiKey
-  ]
-
-getTracks ∷ Format → User → Maybe Artist → Maybe Album → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-getTracks t user artist album page limit apiKey = callAPI t
-  [ (#) (Method "library.getTracks")
-  , (#) user
-  , (#) artist
-  , (#) album
-  , (#) page
-  , (#) limit
-  , (#) apiKey
-  ]
-
-removeAlbum ∷ Format → Artist → Album → APIKey → SessionKey → Secret → Lastfm Response
-removeAlbum t artist album apiKey sessionKey secret = callAPIsigned t secret
-  [ (#) (Method "library.removeAlbum")
-  , (#) artist
-  , (#) album
-  , (#) apiKey
-  , (#) sessionKey
-  ]
-
-removeArtist ∷ Format → Artist → APIKey → SessionKey → Secret → Lastfm Response
-removeArtist t artist apiKey sessionKey secret = callAPIsigned t secret
-  [ (#) (Method "library.removeArtist")
-  , (#) artist
-  , (#) apiKey
-  , (#) sessionKey
-  ]
-
-removeScrobble ∷ Format → Artist → Track → Timestamp → APIKey → SessionKey → Secret → Lastfm Response
-removeScrobble t artist track timestamp apiKey sessionKey secret = callAPIsigned t secret
-  [ (#) (Method "library.removeScrobble")
-  , (#) artist
-  , (#) track
-  , (#) timestamp
-  , (#) apiKey
-  , (#) sessionKey
-  ]
-
-removeTrack ∷ Format → Artist → Track → APIKey → SessionKey → Secret → Lastfm Response
-removeTrack t artist track apiKey sessionKey secret = callAPIsigned t secret
-  [ (#) (Method "library.removeTrack")
-  , (#) artist
-  , (#) track
-  , (#) apiKey
-  , (#) sessionKey
-  ]
diff --git a/src/Network/Lastfm/API/Playlist.hs b/src/Network/Lastfm/API/Playlist.hs
deleted file mode 100644
--- a/src/Network/Lastfm/API/Playlist.hs
+++ /dev/null
@@ -1,24 +0,0 @@
-module Network.Lastfm.API.Playlist
-  ( addTrack, create
-  ) where
-
-import Network.Lastfm.Internal
-
-addTrack ∷ Format → Playlist → Artist → Track → APIKey → SessionKey → Secret → Lastfm Response
-addTrack t playlist artist track apiKey sessionKey secret = callAPIsigned t secret
-  [ (#) (Method "playlist.addTrack")
-  , (#) playlist
-  , (#) artist
-  , (#) track
-  , (#) apiKey
-  , (#) sessionKey
-  ]
-
-create ∷ Format → Maybe Title → Maybe Description → APIKey → SessionKey → Secret → Lastfm Response
-create t title description apiKey sessionKey secret = callAPIsigned t secret
-  [ (#) (Method "playlist.create")
-  , (#) title
-  , (#) description
-  , (#) apiKey
-  , (#) sessionKey
-  ]
diff --git a/src/Network/Lastfm/API/Radio.hs b/src/Network/Lastfm/API/Radio.hs
deleted file mode 100644
--- a/src/Network/Lastfm/API/Radio.hs
+++ /dev/null
@@ -1,42 +0,0 @@
-module Network.Lastfm.API.Radio
-  ( getPlaylist, search, tune
-  ) where
-
-import Network.Lastfm.Internal
-
-getPlaylist ∷ Format
-            → Maybe Discovery
-            → Maybe RTP
-            → Maybe BuyLinks
-            → Multiplier
-            → Bitrate
-            → APIKey
-            → SessionKey
-            → Secret
-            → Lastfm Response
-getPlaylist t discovery rtp buylinks multiplier bitrate apiKey sessionKey secret = callAPIsigned t secret
-  [ (#) (Method "radio.getPlaylist")
-  , (#) discovery
-  , (#) rtp
-  , (#) buylinks
-  , (#) multiplier
-  , (#) bitrate
-  , (#) apiKey
-  , (#) sessionKey
-  ]
-
-search ∷ Format → Name → APIKey → Lastfm Response
-search t name apiKey = callAPI t
-  [ (#) (Method "radio.search")
-  , (#) name
-  , (#) apiKey
-  ]
-
-tune ∷ Format → Maybe Language → Station → APIKey → SessionKey → Secret → Lastfm Response
-tune t language station apiKey sessionKey secret = callAPIsigned t secret
-  [ (#) (Method "radio.tune")
-  , (#) language
-  , (#) station
-  , (#) apiKey
-  , (#) sessionKey
-  ]
diff --git a/src/Network/Lastfm/API/Tag.hs b/src/Network/Lastfm/API/Tag.hs
deleted file mode 100644
--- a/src/Network/Lastfm/API/Tag.hs
+++ /dev/null
@@ -1,80 +0,0 @@
-module Network.Lastfm.API.Tag
-  ( getInfo, getSimilar, getTopAlbums, getTopArtists, getTopTags, getTopTracks
-  , getWeeklyArtistChart, getWeeklyChartList, search
-  ) where
-
-import Network.Lastfm.Internal
-
-getInfo ∷ Format → Tag → Maybe Language → APIKey → Lastfm Response
-getInfo t tag language apiKey = callAPI t
-  [ (#) (Method "tag.getInfo")
-  , (#) tag
-  , (#) language
-  , (#) apiKey
-  ]
-
-getSimilar ∷ Format → Tag → APIKey → Lastfm Response
-getSimilar t tag apiKey = callAPI t
-  [ (#) (Method "tag.getSimilar")
-  , (#) tag
-  , (#) apiKey
-  ]
-
-getTopAlbums ∷ Format → Tag → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-getTopAlbums t tag page limit apiKey = callAPI t
-  [ (#) (Method "tag.getTopAlbums")
-  , (#) tag
-  , (#) page
-  , (#) limit
-  , (#) apiKey
-  ]
-
-getTopArtists ∷ Format → Tag → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-getTopArtists t tag limit page apiKey = callAPI t
-  [ (#) (Method "tag.getTopArtists")
-  , (#) tag
-  , (#) page
-  , (#) limit
-  , (#) apiKey
-  ]
-
-getTopTags ∷ Format → APIKey → Lastfm Response
-getTopTags t apiKey = callAPI t
-  [ (#) (Method "tag.getTopTags")
-  , (#) apiKey
-  ]
-
-getTopTracks ∷ Format → Tag → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-getTopTracks t tag limit page apiKey = callAPI t
-  [ (#) (Method "tag.getTopTracks")
-  , (#) tag
-  , (#) page
-  , (#) limit
-  , (#) apiKey
-  ]
-
-getWeeklyArtistChart ∷ Format → Tag → Maybe From → Maybe To → Maybe Limit → APIKey → Lastfm Response
-getWeeklyArtistChart t tag from to limit apiKey = callAPI t
-  [ (#) (Method "tag.getWeeklyArtistChart")
-  , (#) tag
-  , (#) from
-  , (#) to
-  , (#) limit
-  , (#) apiKey
-  ]
-
-getWeeklyChartList ∷ Format → Tag → APIKey → Lastfm Response
-getWeeklyChartList t tag apiKey = callAPI t
-  [ (#) (Method "tag.getWeeklyChartList")
-  , (#) tag
-  , (#) apiKey
-  ]
-
-search ∷ Format → Tag → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-search t tag page limit apiKey = callAPI t
-  [ (#) (Method "tag.search")
-  , (#) tag
-  , (#) page
-  , (#) limit
-  , (#) apiKey
-  ]
diff --git a/src/Network/Lastfm/API/Tasteometer.hs b/src/Network/Lastfm/API/Tasteometer.hs
deleted file mode 100644
--- a/src/Network/Lastfm/API/Tasteometer.hs
+++ /dev/null
@@ -1,20 +0,0 @@
-module Network.Lastfm.API.Tasteometer
-  ( compare
-  ) where
-
-import Network.Lastfm.Internal
-import Prelude hiding (compare)
-
-(?<) ∷ Argument a ⇒ a → Int → (String, String)
-a ?< n = (key a ++ show n, value a)
-
-compare ∷ Format → Value → Value → Maybe Limit → APIKey → Lastfm Response
-compare t value1 value2 limit apiKey = callAPI t
-  [ (#) (Method "tasteometer.compare")
-  , (,) "type1" (show value1)
-  , (,) "type2" (show value2)
-  , (?<) value1 1
-  , (?<) value2 2
-  , (#) limit
-  , (#) apiKey
-  ]
diff --git a/src/Network/Lastfm/API/Track.hs b/src/Network/Lastfm/API/Track.hs
deleted file mode 100644
--- a/src/Network/Lastfm/API/Track.hs
+++ /dev/null
@@ -1,218 +0,0 @@
-module Network.Lastfm.API.Track
-  ( addTags, ban, getBuyLinks, getCorrection, getFingerprintMetadata
-  , getInfo, getShouts, getSimilar, getTags, getTopFans, getTopTags
-  , love, removeTag, scrobble, search, share, unban, unlove, updateNowPlaying
-  ) where
-
-import Control.Arrow ((|||))
-import Network.Lastfm.Internal
-
-addTags ∷ Format → Artist → Track → [Tag] → APIKey → SessionKey → Secret → Lastfm Response
-addTags t artist track tags apiKey sessionKey secret = callAPIsigned t secret
-  [ (#) (Method "track.addTags")
-  , (#) artist
-  , (#) track
-  , (#) tags
-  , (#) apiKey
-  , (#) sessionKey
-  ]
-
-ban ∷ Format → Artist → Track → APIKey → SessionKey → Secret → Lastfm Response
-ban t artist track apiKey sessionKey secret = callAPIsigned t secret
-  [ (#) (Method "track.ban")
-  , (#) artist
-  , (#) track
-  , (#) apiKey
-  , (#) sessionKey
-  ]
-
-getBuyLinks ∷ Format → Either (Artist, Track) Mbid → Maybe Autocorrect → Country → APIKey → Lastfm Response
-getBuyLinks t a autocorrect country apiKey = callAPI t $
-  target a ++
-  [ (#) (Method "track.getBuyLinks")
-  , (#) autocorrect
-  , (#) country
-  , (#) apiKey
-  ]
-
-getCorrection ∷ Format → Artist → Track → APIKey → Lastfm Response
-getCorrection t artist track apiKey = callAPI t
-  [ (#) (Method "track.getCorrection")
-  , (#) artist
-  , (#) track
-  , (#) apiKey
-  ]
-
-getFingerprintMetadata ∷ Format → Fingerprint → APIKey → Lastfm Response
-getFingerprintMetadata t fingerprint apiKey = callAPI t
-  [ (#) (Method "track.getFingerprintMetadata")
-  , (#) fingerprint
-  , (#) apiKey
-  ]
-
-getInfo ∷ Format → Either (Artist, Track) Mbid → Maybe Autocorrect → Maybe Username → APIKey → Lastfm Response
-getInfo t a autocorrect username apiKey = callAPI t $
-  target a ++
-  [ (#) (Method "track.getInfo")
-  , (#) autocorrect
-  , (#) username
-  , (#) apiKey
-  ]
-
-getShouts ∷ Format → Either (Artist, Track) Mbid → Maybe Autocorrect → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-getShouts t a autocorrect page limit apiKey = callAPI t $
-  target a ++
-  [ (#) (Method "track.getShouts")
-  , (#) autocorrect
-  , (#) page
-  , (#) limit
-  , (#) apiKey
-  ]
-
-getSimilar ∷ Format → Either (Artist, Track) Mbid → Maybe Autocorrect → Maybe Limit → APIKey → Lastfm Response
-getSimilar t a autocorrect limit apiKey = callAPI t $
-  target a ++
-  [ (#) (Method "track.getSimilar")
-  , (#) autocorrect
-  , (#) limit
-  , (#) apiKey
-  ]
-
-getTags ∷ Format → Either (Artist, Track) Mbid → Maybe Autocorrect → Either User (SessionKey, Secret) → APIKey → Lastfm Response
-getTags t a autocorrect b apiKey = case b of
-  Left user → callAPI t $ target a ++ [(#) user] ++ args
-  Right (sessionKey, secret) → callAPIsigned t secret $ target a ++ [(#) sessionKey] ++ args
-  where args =
-          [ (#) (Method "track.getTags")
-          , (#) autocorrect
-          , (#) apiKey
-          ]
-
-getTopFans ∷ Format → Either (Artist, Track) Mbid → Maybe Autocorrect → APIKey → Lastfm Response
-getTopFans t a autocorrect apiKey = callAPI t $
-  target a ++
-  [ (#) (Method "track.getTopFans")
-  , (#) autocorrect
-  , (#) apiKey
-  ]
-
-getTopTags ∷ Format → Either (Artist, Track) Mbid → Maybe Autocorrect → APIKey → Lastfm Response
-getTopTags t a autocorrect apiKey = callAPI t $
-  target a ++
-  [ (#) (Method "track.getTopTags")
-  , (#) autocorrect
-  , (#) apiKey
-  ]
-
-love ∷ Format → Artist → Track → APIKey → SessionKey → Secret → Lastfm Response
-love t artist track apiKey sessionKey secret = callAPIsigned t secret
-  [ (#) (Method "track.love")
-  , (#) artist
-  , (#) track
-  , (#) apiKey
-  , (#) sessionKey
-  ]
-
-removeTag ∷ Format → Artist → Track → Tag → APIKey → SessionKey → Secret → Lastfm Response
-removeTag t artist track tag apiKey sessionKey secret = callAPIsigned t secret
-  [ (#) (Method "track.removeTag")
-  , (#) artist
-  , (#) track
-  , (#) tag
-  , (#) apiKey
-  , (#) sessionKey
-  ]
-
-scrobble ∷ Format → ( Timestamp, Maybe Album, Artist, Track, Maybe AlbumArtist
-           , Maybe Duration, Maybe StreamId, Maybe ChosenByUser
-           , Maybe Context, Maybe TrackNumber, Maybe Mbid )
-         → APIKey
-         → SessionKey
-         → Secret
-         → Lastfm Response
-scrobble t (timestamp, album, artist, track, albumArtist, duration, streamId, chosenByUser, context, trackNumber, mbid) apiKey sessionKey secret = callAPIsigned t secret
-  [ (#) (Method "track.scrobble")
-  , (#) timestamp
-  , (#) artist
-  , (#) track
-  , (#) album
-  , (#) albumArtist
-  , (#) duration
-  , (#) streamId
-  , (#) chosenByUser
-  , (#) context
-  , (#) trackNumber
-  , (#) mbid
-  , (#) apiKey
-  , (#) sessionKey
-  ]
-
-search ∷ Format → Track → Maybe Page → Maybe Limit → Maybe Artist → APIKey → Lastfm Response
-search t track page limit artist apiKey = callAPI t
-  [ (#) (Method "track.search")
-  , (#) track
-  , (#) page
-  , (#) limit
-  , (#) artist
-  , (#) apiKey
-  ]
-
-share ∷ Format → Artist → Track → Recipient → Maybe Message → Maybe Public → APIKey → SessionKey → Secret → Lastfm Response
-share t artist track recipient message public apiKey sessionKey secret = callAPIsigned t secret
-  [ (#) (Method "track.share")
-  , (#) artist
-  , (#) track
-  , (#) recipient
-  , (#) public
-  , (#) message
-  , (#) apiKey
-  , (#) sessionKey
-  ]
-
-unban ∷ Format → Artist → Track → APIKey → SessionKey → Secret → Lastfm Response
-unban t artist track apiKey sessionKey secret = callAPIsigned t secret
-  [ (#) (Method "track.unban")
-  , (#) artist
-  , (#) track
-  , (#) apiKey
-  , (#) sessionKey
-  ]
-
-unlove ∷ Format → Artist → Track → APIKey → SessionKey → Secret → Lastfm Response
-unlove t artist track apiKey sessionKey secret = callAPIsigned t secret
-  [ (#) (Method "track.unlove")
-  , (#) artist
-  , (#) track
-  , (#) apiKey
-  , (#) sessionKey
-  ]
-
-
-updateNowPlaying ∷ Format → Artist
-                 → Track
-                 → Maybe Album
-                 → Maybe AlbumArtist
-                 → Maybe Context
-                 → Maybe TrackNumber
-                 → Maybe Mbid
-                 → Maybe Duration
-                 → APIKey
-                 → SessionKey
-                 → Secret
-                 → Lastfm Response
-updateNowPlaying t artist track album albumArtist context trackNumber mbid duration apiKey sessionKey secret = callAPIsigned t secret
-  [ (#) (Method "track.updateNowPlaying")
-  , (#) artist
-  , (#) track
-  , (#) album
-  , (#) albumArtist
-  , (#) context
-  , (#) trackNumber
-  , (#) mbid
-  , (#) duration
-  , (#) apiKey
-  , (#) sessionKey
-  ]
-
-target ∷ Either (Artist, Track) Mbid → [(String, String)]
-target = (\(artist, track) → [(#) artist, (#) track]) ||| return . (#)
diff --git a/src/Network/Lastfm/API/User.hs b/src/Network/Lastfm/API/User.hs
deleted file mode 100644
--- a/src/Network/Lastfm/API/User.hs
+++ /dev/null
@@ -1,244 +0,0 @@
-module Network.Lastfm.API.User
-  ( getArtistTracks, getBannedTracks, getEvents, getFriends, getInfo, getLovedTracks
-  , getNeighbours, getNewReleases, getPastEvents, getPersonalTags, getPlaylists, getRecentStations
-  , getRecentTracks, getRecommendedArtists, getRecommendedEvents, getShouts, getTopAlbums
-  , getTopArtists, getTopTags, getTopTracks, getWeeklyAlbumChart, getWeeklyArtistChart
-  , getWeeklyChartList, getWeeklyTrackChart, shout
-  ) where
-
-import Network.Lastfm.Internal
-
-getArtistTracks ∷ Format → User → Artist → Maybe StartTimestamp → Maybe EndTimestamp → Maybe Page → APIKey → Lastfm Response
-getArtistTracks t user artist startTimestamp endTimestamp page apiKey = callAPI t
-  [ (#) (Method "user.getArtistTracks")
-  , (#) user
-  , (#) artist
-  , (#) startTimestamp
-  , (#) page
-  , (#) endTimestamp
-  , (#) apiKey
-  ]
-
-getBannedTracks ∷ Format → User → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-getBannedTracks t user page limit apiKey = callAPI t
-  [ (#) (Method "user.getBannedTracks")
-  , (#) user
-  , (#) page
-  , (#) limit
-  , (#) apiKey
-  ]
-
-getEvents ∷ Format → User → Maybe Page → Maybe Limit → Maybe FestivalsOnly → APIKey → Lastfm Response
-getEvents t user page limit festivalsOnly apiKey = callAPI t
-  [ (#) (Method "user.getEvents")
-  , (#) user
-  , (#) page
-  , (#) limit
-  , (#) festivalsOnly
-  , (#) apiKey
-  ]
-
-getFriends ∷ Format → User → Maybe RecentTracks → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-getFriends t user recentTracks page limit apiKey = callAPI t
-  [ (#) (Method "user.getFriends")
-  , (#) user
-  , (#) recentTracks
-  , (#) page
-  , (#) limit
-  , (#) apiKey
-  ]
-
-getInfo ∷ Format → Maybe User → APIKey → Lastfm Response
-getInfo t user apiKey = callAPI t
-  [ (#) (Method "user.getInfo")
-  , (#) user
-  , (#) apiKey
-  ]
-
-getLovedTracks ∷ Format → User → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-getLovedTracks t user page limit apiKey = callAPI t
-  [ (#) (Method "user.getLovedTracks")
-  , (#) user
-  , (#) page
-  , (#) limit
-  , (#) apiKey
-  ]
-
-getNeighbours ∷ Format → User → Maybe Limit → APIKey → Lastfm Response
-getNeighbours t user limit apiKey = callAPI t
-  [ (#) (Method "user.getNeighbours")
-  , (#) user
-  , (#) limit
-  , (#) apiKey
-  ]
-
-getNewReleases ∷ Format → User → Maybe UseRecs → APIKey → Lastfm Response
-getNewReleases t user useRecs apiKey = callAPI t
-  [ (#) (Method "user.getNewReleases")
-  , (#) user
-  , (#) useRecs
-  , (#) apiKey
-  ]
-
-getPastEvents ∷ Format → User → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-getPastEvents t user page limit apiKey = callAPI t
-  [ (#) (Method "user.getPastEvents")
-  , (#) user
-  , (#) page
-  , (#) limit
-  , (#) apiKey
-  ]
-
-getPersonalTags ∷ Format
-                → User
-                → Tag
-                → TaggingType
-                → Maybe Page
-                → Maybe Limit
-                → APIKey
-                → Lastfm Response
-getPersonalTags t user tag taggingType page limit apiKey = callAPI t
-  [ (#) (Method "user.getPersonalTags")
-  , (#) user
-  , (#) tag
-  , (#) taggingType
-  , (#) page
-  , (#) limit
-  , (#) apiKey
-  ]
-
-getPlaylists ∷ Format → User → APIKey → Lastfm Response
-getPlaylists t user apiKey = callAPI t
-  [ (#) (Method "user.getPlaylists")
-  , (#) user
-  , (#) apiKey
-  ]
-
-getRecentStations ∷ Format → User → Maybe Page → Maybe Limit → APIKey → SessionKey → Secret → Lastfm Response
-getRecentStations t user page limit apiKey sessionKey secret = callAPIsigned t secret
-  [ (#) (Method "user.getRecentStations")
-  , (#) user
-  , (#) page
-  , (#) limit
-  , (#) apiKey
-  , (#) sessionKey
-  ]
-
-getRecentTracks ∷ Format → User → Maybe Page → Maybe Limit → Maybe From → Maybe To → APIKey → Lastfm Response
-getRecentTracks t user page limit from to apiKey = callAPI t
-  [ (#) (Method "user.getRecentTracks")
-  , (#) user
-  , (#) page
-  , (#) limit
-  , (#) from
-  , (#) to
-  , (#) apiKey
-  ]
-
-getRecommendedArtists ∷ Format → Maybe Page → Maybe Limit → APIKey → SessionKey → Secret → Lastfm Response
-getRecommendedArtists t page limit apiKey sessionKey secret = callAPIsigned t secret
-  [ (#) (Method "user.getRecommendedArtists")
-  , (#) page
-  , (#) limit
-  , (#) apiKey
-  , (#) sessionKey
-  ]
-
-getRecommendedEvents ∷ Format → Maybe Page → Maybe Limit → APIKey → SessionKey → Secret → Lastfm Response
-getRecommendedEvents t page limit apiKey sessionKey secret = callAPIsigned t secret
-  [ (#) (Method "user.getRecommendedEvents")
-  , (#) page
-  , (#) limit
-  , (#) apiKey
-  , (#) sessionKey
-  ]
-
-getShouts ∷ Format → User → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-getShouts t user page limit apiKey = callAPI t
-  [ (#) (Method "user.getShouts")
-  , (#) user
-  , (#) page
-  , (#) limit
-  , (#) apiKey
-  ]
-
-getTopAlbums ∷ Format → User → Maybe Period → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-getTopAlbums t user period page limit apiKey = callAPI t
-  [ (#) (Method "user.getTopAlbums")
-  , (#) user
-  , (#) period
-  , (#) page
-  , (#) limit
-  , (#) apiKey
-  ]
-
-getTopArtists ∷ Format → User → Maybe Period → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-getTopArtists t user period page limit apiKey = callAPI t
-  [ (#) (Method "user.getTopArtists")
-  , (#) user
-  , (#) period
-  , (#) page
-  , (#) limit
-  , (#) apiKey
-  ]
-
-getTopTags ∷ Format → User → Maybe Limit → APIKey → Lastfm Response
-getTopTags t user limit apiKey = callAPI t
-  [ (#) (Method "user.getTopTags")
-  , (#) user
-  , (#) limit
-  , (#) apiKey
-  ]
-
-getTopTracks ∷ Format → User → Maybe Period → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-getTopTracks t user period page limit apiKey = callAPI t
-  [ (#) (Method "user.getTopTracks")
-  , (#) user
-  , (#) period
-  , (#) page
-  , (#) limit
-  , (#) apiKey
-  ]
-
-getWeeklyAlbumChart ∷ Format → User → Maybe From → Maybe To → APIKey → Lastfm Response
-getWeeklyAlbumChart t user from to apiKey = callAPI t
-  [ (#) (Method "user.getWeeklyAlbumChart")
-  , (#) user
-  , (#) from
-  , (#) to
-  , (#) apiKey
-  ]
-
-getWeeklyArtistChart ∷ Format → User → Maybe From → Maybe To → APIKey → Lastfm Response
-getWeeklyArtistChart t user from to apiKey = callAPI t
-  [ (#) (Method "user.getWeeklyArtistChart")
-  , (#) user
-  , (#) from
-  , (#) to
-  , (#) apiKey
-  ]
-
-getWeeklyChartList ∷ Format → User → APIKey → Lastfm Response
-getWeeklyChartList t user apiKey = callAPI t
-  [ (#) (Method "user.getWeeklyChartList")
-  , (#) user
-  , (#) apiKey
-  ]
-
-getWeeklyTrackChart ∷ Format → User → Maybe From → Maybe To → APIKey → Lastfm Response
-getWeeklyTrackChart t user from to apiKey = callAPI t
-  [ (#) (Method "user.getWeeklyTrackChart")
-  , (#) user
-  , (#) from
-  , (#) to
-  , (#) apiKey
-  ]
-
-shout ∷ Format → User → Message → APIKey → SessionKey → Secret → Lastfm Response
-shout t user message apiKey sessionKey secret = callAPIsigned t secret
-  [ (#) (Method "user.shout")
-  , (#) user
-  , (#) message
-  , (#) apiKey
-  , (#) sessionKey
-  ]
diff --git a/src/Network/Lastfm/API/Venue.hs b/src/Network/Lastfm/API/Venue.hs
deleted file mode 100644
--- a/src/Network/Lastfm/API/Venue.hs
+++ /dev/null
@@ -1,33 +0,0 @@
-module Network.Lastfm.API.Venue
-  ( getEvents, getPastEvents, search
-  ) where
-
-import Network.Lastfm.Internal
-
-getEvents ∷ Format → Venue → Maybe FestivalsOnly → APIKey → Lastfm Response
-getEvents t venue festivalsOnly apiKey = callAPI t
-  [ (#) (Method "venue.getEvents")
-  , (#) venue
-  , (#) festivalsOnly
-  , (#) apiKey
-  ]
-
-getPastEvents ∷ Format → Venue → Maybe FestivalsOnly → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-getPastEvents t venue festivalsOnly page limit apiKey = callAPI t
-  [ (#) (Method "venue.getPastEvents")
-  , (#) venue
-  , (#) festivalsOnly
-  , (#) page
-  , (#) limit
-  , (#) apiKey
-  ]
-
-search ∷ Format → Venuename → Maybe Page → Maybe Limit → Maybe Country → APIKey → Lastfm Response
-search t venue page limit country apiKey = callAPI t
-  [ (#) (Method "venue.search")
-  , (#) venue
-  , (#) page
-  , (#) limit
-  , (#) country
-  , (#) apiKey
-  ]
diff --git a/src/Network/Lastfm/Album.hs b/src/Network/Lastfm/Album.hs
new file mode 100644
--- /dev/null
+++ b/src/Network/Lastfm/Album.hs
@@ -0,0 +1,115 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE UnicodeSyntax #-}
+-- | Lastfm album API
+--
+-- This module is intended to be imported qualified:
+--
+-- @
+-- import qualified Network.Lastfm.Album as Album
+-- @
+module Network.Lastfm.Album
+  ( ArtistAlbumOrMBID
+  , addTags, getBuyLinks, getInfo, getShouts
+  , getTags, getTopTags, removeTag, search, share
+  ) where
+
+import Control.Applicative
+
+import Network.Lastfm.Request
+
+
+-- | Unify ('Artist' → 'Album' → …) and ('MBID' → …)
+class ArtistAlbumOrMBID a
+
+instance ArtistAlbumOrMBID (MBID → APIKey → Ready)
+instance ArtistAlbumOrMBID (Artist → Album → APIKey → Ready)
+
+
+-- | Tag an album using a list of user supplied tags.
+--
+-- <http://www.last.fm/api/show/album.addTags>
+addTags ∷ Request f Sign (Artist → Album → [Tag] → APIKey → SessionKey → Ready)
+addTags = api "album.addTags" <* post
+{-# INLINE addTags #-}
+
+
+-- | Get a list of Buy Links for a particular Album. It is
+-- required that you supply either the artist and track params or the mbid parameter.
+--
+-- Optional: 'autocorrect'
+--
+-- <http://www.last.fm/api/show/album.getBuylinks>
+getBuyLinks ∷ ArtistAlbumOrMBID t ⇒ Request f Send (Country → t)
+getBuyLinks = api "album.getBuyLinks"
+{-# INLINE getBuyLinks #-}
+
+
+-- | Get the metadata for an album on Last.fm using the album name or
+-- a musicbrainz id. See playlist.fetch on how to get the album playlist.
+--
+-- Optional: 'autocorrect', 'username', 'language'
+--
+-- <http://www.last.fm/api/show/album.getInfo>
+getInfo ∷ ArtistAlbumOrMBID t ⇒ Request f Send t
+getInfo = api "album.getInfo"
+{-# INLINE getInfo #-}
+
+
+-- | Get shouts for this album.
+--
+-- Optional: 'autocorrect', 'limit', 'page'
+--
+-- <http://www.last.fm/api/show/album.getShouts>
+getShouts ∷ ArtistAlbumOrMBID t ⇒ Request f Send t
+getShouts = api "album.getShouts"
+{-# INLINE getShouts #-}
+
+
+-- | Get the tags applied by an individual user to an album on Last.fm.
+--
+-- Optional: 'autocorrect', 'user'
+--
+-- <http://www.last.fm/api/show/album.getTags>
+getTags ∷ ArtistAlbumOrMBID t ⇒ Request f a t
+getTags = api "album.getTags"
+{-# INLINE getTags #-}
+
+
+-- | Get the top tags for an album on Last.fm, ordered by popularity.
+--
+-- Optional: 'autocorrect'
+--
+-- <http://www.last.fm/api/show/album.getTopTags>
+getTopTags ∷ ArtistAlbumOrMBID t ⇒ Request f Send t
+getTopTags = api "album.getTopTags"
+{-# INLINE getTopTags #-}
+
+
+-- | Remove a user's tag from an album.
+--
+-- <http://www.last.fm/api/show/album.removeTag>
+removeTag ∷ Request f Sign (Artist → Album → Tag → APIKey → SessionKey → Ready)
+removeTag = api "album.removeTag" <* post
+{-# INLINE removeTag #-}
+
+
+-- | Search for an album by name. Returns album matches sorted by relevance.
+--
+-- Optional: 'limit', 'page'
+--
+-- <http://www.last.fm/api/show/album.search>
+search ∷ Request f Send (Album → APIKey → Ready)
+search = api "album.search"
+{-# INLINE search #-}
+
+
+-- | Share an album with one or more Last.fm users or other friends.
+--
+-- Optional: 'public', 'message', 'recipient'
+--
+-- <http://www.last.fm/api/show/album.share>
+share ∷ Request f Sign (Album → Artist → Recipient → APIKey → SessionKey → Ready)
+share = api "album.share" <* post
+{-# INLINE share #-}
diff --git a/src/Network/Lastfm/Artist.hs b/src/Network/Lastfm/Artist.hs
new file mode 100644
--- /dev/null
+++ b/src/Network/Lastfm/Artist.hs
@@ -0,0 +1,196 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE UnicodeSyntax #-}
+-- | Lastfm artist API
+--
+-- This module is intended to be imported qualified:
+--
+-- @
+-- import qualified Network.Lastfm.Artist as Artist
+-- @
+module Network.Lastfm.Artist
+  ( ArtistOrMBID
+  , addTags, getCorrection, getEvents, getInfo
+  , getPastEvents, getPodcast, getShouts
+  , getSimilar, getTags, getTopAlbums
+  , getTopFans, getTopTags, getTopTracks
+  , removeTag, search, share, shout
+  ) where
+
+import Control.Applicative
+
+import Network.Lastfm.Request
+
+-- | Unify ('Artist' → …) and ('MBID' → …)
+class ArtistOrMBID a
+
+instance ArtistOrMBID MBID
+instance ArtistOrMBID Artist
+
+
+-- | Tag an artist with one or more user supplied tags.
+--
+-- <http://www.last.fm/api/show/artist.addTags>
+addTags ∷ Request f Sign (Artist → [Tag] → APIKey → SessionKey → Ready)
+addTags = api "artist.addTags" <* post
+{-# INLINE addTags #-}
+
+
+-- | Use the last.fm corrections data to check whether the
+-- supplied artist has a correction to a canonical artist
+--
+-- <http://www.last.fm/api/show/artist.getCorrection>
+getCorrection ∷ Request f Send (Artist → APIKey → Ready)
+getCorrection = api "artist.getCorrection"
+{-# INLINE getCorrection #-}
+
+
+-- | Get a list of upcoming events for this artist. Easily
+-- integratable into calendars, using the ical standard (see feeds section below).
+--
+-- Optional: 'autocorrect', 'limit', 'pages', 'festivalsonly'
+--
+-- <http://www.last.fm/api/show/artist.getEvents>
+getEvents ∷ ArtistOrMBID t ⇒ Request f Send (t → APIKey → Ready)
+getEvents = api "artist.getEvents"
+{-# INLINE getEvents #-}
+
+
+-- | Get the metadata for an artist. Includes biography.
+--
+-- Optional: 'language', 'autocorrect', 'username'
+--
+-- <http://www.last.fm/api/show/artist.getInfo>
+getInfo ∷ ArtistOrMBID t ⇒ Request f Send (t → APIKey → Ready)
+getInfo = api "artist.getInfo"
+{-# INLINE getInfo #-}
+
+
+-- | Get a paginated list of all the events this artist has played at in the past.
+--
+-- Optional: 'page', 'autocorrect', 'limit'
+--
+-- <http://www.last.fm/api/show/artist.getPastEvents>
+getPastEvents ∷ ArtistOrMBID t ⇒ Request f Send (t → APIKey → Ready)
+getPastEvents = api "artist.getPastEvents"
+{-# INLINE getPastEvents #-}
+
+
+-- | Get a podcast of free mp3s based on an artist
+--
+-- Optional: 'autocorrect'
+--
+-- <http://www.last.fm/api/show/artist.getPodcast>
+getPodcast ∷ ArtistOrMBID t ⇒ Request f Send (t → APIKey → Ready)
+getPodcast = api "artist.getPodcast"
+{-# INLINE getPodcast #-}
+
+
+-- | Get shouts for this artist. Also available as an rss feed.
+--
+-- Optional:'autocorrect', 'limit', 'page'
+--
+-- <http://www.last.fm/api/show/artist.getShouts>
+getShouts ∷ ArtistOrMBID t ⇒ Request f Send (t → APIKey → Ready)
+getShouts = api "artist.getShouts"
+{-# INLINE getShouts #-}
+
+
+-- | Get all the artists similar to this artist
+--
+-- Optional: 'limit', 'autocorrect'
+--
+-- <http://www.last.fm/api/show/artist.getSimilar>
+getSimilar ∷ ArtistOrMBID t ⇒ Request f Send (t → APIKey → Ready)
+getSimilar = api "artist.getSimilar"
+{-# INLINE getSimilar #-}
+
+
+-- | Get the tags applied by an individual user to an artist on Last.fm.
+-- If accessed as an authenticated service /and/ you don't supply a
+-- user parameter then this service will return tags for
+-- the authenticated user. To retrieve the list of top tags applied
+-- to an artist by all users use 'Network.Lastfm.Artist.getTopTags'.
+--
+-- Optional: 'user', 'autocorrect'
+--
+-- <http://www.last.fm/api/show/artist.getTags>
+getTags ∷ ArtistOrMBID t ⇒ Request f a (t → APIKey → Ready)
+getTags = api "artist.getTags"
+{-# INLINE getTags #-}
+
+
+-- | Get the top albums for an artist on Last.fm, ordered by popularity.
+--
+-- Optional: 'autocorrect', 'page', 'limit'
+--
+-- <http://www.last.fm/api/show/artist.getTopAlbums>
+getTopAlbums ∷ ArtistOrMBID t ⇒ Request f Send (t → APIKey → Ready)
+getTopAlbums = api "artist.getTopAlbums"
+{-# INLINE getTopAlbums #-}
+
+
+-- | Get the top fans for an artist on Last.fm, based on listening data.
+--
+-- Optional: 'autocorrect'
+--
+-- <http://www.last.fm/api/show/artist.getTopFans>
+getTopFans ∷ ArtistOrMBID t ⇒ Request f Send (t → APIKey → Ready)
+getTopFans = api "artist.getTopFans"
+{-# INLINE getTopFans #-}
+
+
+-- | Get the top tags for an artist on Last.fm, ordered by popularity.
+--
+-- Optional: 'autocorrect'
+--
+-- <http://www.last.fm/api/show/artist.getTopTags>
+getTopTags ∷ ArtistOrMBID t ⇒ Request f Send (t → APIKey → Ready)
+getTopTags = api "artist.getTopTags"
+{-# INLINE getTopTags #-}
+
+
+-- | Get the top tracks by an artist on Last.fm, ordered by popularity
+--
+-- Optional: 'autocorrect', 'page', 'limit'
+--
+-- <http://www.last.fm/api/show/artist.getTopTracks>
+getTopTracks ∷ ArtistOrMBID t ⇒ Request f Send (t → APIKey → Ready)
+getTopTracks = api "artist.getTopTracks"
+{-# INLINE getTopTracks #-}
+
+
+-- | Remove a user's tag from an artist.
+--
+-- <http://www.last.fm/api/show/artist.removeTag>
+removeTag ∷ Request f Sign (Artist → Tag → APIKey → SessionKey → Ready)
+removeTag = api "artist.removeTag" <* post
+{-# INLINE removeTag #-}
+
+
+-- | Search for an artist by name. Returns artist matches sorted by relevance.
+--
+-- Optional: 'limit', 'page'
+--
+-- <http://www.last.fm/api/show/artist.search>
+search ∷ Request f Send (Artist → APIKey → Ready)
+search = api "artist.search"
+{-# INLINE search #-}
+
+
+-- | Share an artist with Last.fm users or other friends.
+--
+-- Optional: 'message', 'public'
+--
+-- <http://www.last.fm/api/show/artist.share>
+share ∷ Request f Sign (Artist → Recipient → APIKey → SessionKey → Ready)
+share = api "artist.share" <* post
+{-# INLINE share #-}
+
+
+-- | Shout in this artist's shoutbox
+--
+-- <http://www.last.fm/api/show/artist.shout>
+shout ∷ Request f Sign (Artist → Message → APIKey → SessionKey → Ready)
+shout = api "artist.shout" <* post
+{-# INLINE shout #-}
diff --git a/src/Network/Lastfm/Authentication.hs b/src/Network/Lastfm/Authentication.hs
new file mode 100644
--- /dev/null
+++ b/src/Network/Lastfm/Authentication.hs
@@ -0,0 +1,53 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE UnicodeSyntax #-}
+-- | Lastfm authentication procedure helpers
+--
+-- Basically, lastfm provides 3 ways to authenticate user:
+--
+--  - web application - <http://www.lastfm.ru/api/webauth>
+--
+--  - desktop application - <http://www.lastfm.ru/api/desktopauth>
+--
+--  - modile application - <http://www.lastfm.ru/api/mobileauth>
+--
+-- Note that you can use any of them in your
+-- application despite their names
+module Network.Lastfm.Authentication
+  ( -- * Helpers
+    getToken, getSession, getMobileSession
+  , link
+  ) where
+
+import Data.Monoid
+
+import Network.Lastfm.Internal
+import Network.Lastfm.Request
+
+
+-- | Get authorization token
+getToken ∷ Request f Send (APIKey → Ready)
+getToken = api "auth.getToken"
+{-# INLINE getToken #-}
+
+
+-- | Get session key
+getMobileSession ∷ Request f Sign (Username → Password → APIKey → Ready)
+getMobileSession = api "auth.getMobileSession"
+{-# INLINE getMobileSession #-}
+
+
+-- | Get session key
+getSession ∷ Request f Sign (Token → APIKey → Ready)
+getSession = api "auth.getSession"
+{-# INLINE getSession #-}
+
+
+-- | Construct link user should follow to approve application
+link ∷ Request f a t → String
+link q = render . unwrap q $ R
+  { _host = "http://www.last.fm/api/auth/"
+  , _method = mempty
+  , _query = mempty
+  }
+{-# INLINE link #-}
diff --git a/src/Network/Lastfm/Chart.hs b/src/Network/Lastfm/Chart.hs
new file mode 100644
--- /dev/null
+++ b/src/Network/Lastfm/Chart.hs
@@ -0,0 +1,76 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE UnicodeSyntax #-}
+-- | Lastfm chart API
+--
+-- This module is intended to be imported qualified:
+--
+-- @
+-- import qualified Network.Lastfm.Chart as Chart
+-- @
+module Network.Lastfm.Chart
+  ( getHypedArtists, getHypedTracks, getLovedTracks
+  , getTopArtists, getTopTags, getTopTracks
+  ) where
+
+import Network.Lastfm.Request
+
+
+-- | Get the hyped artists chart
+--
+-- Optional: 'page', 'limit'
+--
+-- <http://www.last.fm/api/show/chart.getHypedArtists>
+getHypedArtists ∷ Request f Send (APIKey → Ready)
+getHypedArtists = api "chart.getHypedArtists"
+{-# INLINE getHypedArtists #-}
+
+
+-- | Get the top artists chart
+--
+-- Optional: 'page', 'limit'
+--
+-- <http://www.last.fm/api/show/chart.getHypedTracks>
+getHypedTracks ∷ Request f Send (APIKey → Ready)
+getHypedTracks = api "chart.getHypedTracks"
+{-# INLINE getHypedTracks #-}
+
+
+-- | Get the most loved tracks chart
+--
+-- Optional: 'page', 'limit'
+--
+-- <http://www.last.fm/api/show/chart.getLovedTracks>
+getLovedTracks ∷ Request f Send (APIKey → Ready)
+getLovedTracks = api "chart.getLovedTracks"
+{-# INLINE getLovedTracks #-}
+
+
+-- | Get the top artists chart
+--
+-- Optional: 'page', 'limit'
+--
+-- <http://www.last.fm/api/show/chart.getTopArtists>
+getTopArtists ∷ Request f Send (APIKey → Ready)
+getTopArtists = api "chart.getTopArtists"
+{-# INLINE getTopArtists #-}
+
+
+-- | Get the top artists chart
+--
+-- Optional: 'page', 'limit'
+--
+-- <http://www.last.fm/api/show/chart.getTopTags>
+getTopTags ∷ Request f Send (APIKey → Ready)
+getTopTags = api "chart.getTopTags"
+{-# INLINE getTopTags #-}
+
+
+-- | Get the top tracks chart
+--
+-- Optional: 'page', 'limit'
+--
+-- <http://www.last.fm/api/show/chart.getTopTracks>
+getTopTracks ∷ Request f Send (APIKey → Ready)
+getTopTracks = api "chart.getTopTracks"
+{-# INLINE getTopTracks #-}
diff --git a/src/Network/Lastfm/Error.hs b/src/Network/Lastfm/Error.hs
deleted file mode 100644
--- a/src/Network/Lastfm/Error.hs
+++ /dev/null
@@ -1,110 +0,0 @@
-{-# LANGUAGE OverloadedStrings #-}
-module Network.Lastfm.Error
-  ( LastfmError(..)
-  , disambiguate
-  ) where
-
-import Network.Curl (CurlCode)
-
-
-data LastfmError
-  = DoesntExist
-  | InvalidService
-  | InvalidMethod
-  | AuthenticationFailed
-  | InvalidFormat
-  | InvalidParameters
-  | InvalidResource
-  | OperationFailed
-  | InvalidSessionKey
-  | InvalidAPIKey
-  | ServiceOffline
-  | SubscribersOnly
-  | InvalidMethodSignature
-  | TokenHasNotAuthorized
-  | NotForStreaming
-  | TemporaryUnavailable
-  | LoginRequired
-  | TrialExpired
-  | DoesntExistAgain
-  | NotEnoughContent
-  | NotEnoughMembers
-  | NotEnoughFans
-  | NotEnoughNeighbours
-  | NoPeakRadio
-  | RadioNotFound
-  | SuspendedAPIKey
-  | Deprecated
-  | RateLimitExceeded
-  | TestAPIKeyDeprecated
-  | UnknownError Int
-  | CurlError CurlCode
-
-
-instance Show LastfmError where
-  show e = unwords $ case e of
-    DoesntExist            → ["DoesntExist:",            "This error does not exist"]
-    InvalidService         → ["InvalidService:",         "This service does not exist"]
-    InvalidMethod          → ["InvalidMethod:",          "No method with that name in this package"]
-    AuthenticationFailed   → ["AuthenticationFailed:",   "You do not have permissions to access the service"]
-    InvalidFormat          → ["InvalidFormat:",          "This service doesn't exist in that format"]
-    InvalidParameters      → ["InvalidParameters:",      "Your request is missing a required parameter"]
-    InvalidResource        → ["InvalidResource:",        "Invalid resource specified"]
-    OperationFailed        → ["OperationFailed:",        "Something else went wrong"]
-    InvalidSessionKey      → ["InvalidSessionKey:",      "Please re-authenticate"]
-    InvalidAPIKey          → ["InvalidAPIKey:",          "You must be granted a valid key by last.fm"]
-    ServiceOffline         → ["ServiceOffline:",         "This service is temporarily offline. Try again later."]
-    SubscribersOnly        → ["SubscribersOnly:",        "This station is only available to paid last.fm subscribers"]
-    InvalidMethodSignature → ["InvalidMethodSignature:", "Invalid method signature supplied"]
-    TokenHasNotAuthorized  → ["TokenHasNotAuthorized:",  "This token has not been authorized"]
-    NotForStreaming        → ["NotForStreaming:",        "This item is not available for streaming."]
-    TemporaryUnavailable   → ["TemporaryUnavailable:",   "The service is temporarily unavailable, please try again."]
-    LoginRequired          → ["LoginRequired:",          "Login: User requires to be logged in"]
-    TrialExpired           → ["TrialExpired:",           "This user has no free radio plays left. Subscription required."]
-    DoesntExistAgain       → ["DoesntExistAgain:",       "This error does not exist"]
-    NotEnoughContent       → ["NotEnoughContent:",       "There is not enough content to play this station"]
-    NotEnoughMembers       → ["NotEnoughMembers:",       "This group does not have enough members for radio"]
-    NotEnoughFans          → ["NotEnoughFans:",          "This artist does not have enough fans for for radio"]
-    NotEnoughNeighbours    → ["NotEnoughNeighbours:",    "There are not enough neighbours for radio"]
-    NoPeakRadio            → ["NoPeakRadio:",            "This user is not allowed to listen to radio during peak usage"]
-    RadioNotFound          → ["RadioNotFound:",          "Radio station not found"]
-    SuspendedAPIKey        → ["SuspendedAPIKey:",        "Access for your account has been suspended, please contact Last.fm"]
-    Deprecated             → ["Deprecated:",             "This type of request is no longer supported"]
-    RateLimitExceeded      → ["RateLimitExceeded:",      "Your IP has made too many requests in a short period"]
-    TestAPIKeyDeprecated   → ["TestAPIKeyDeprecated:",   "The test API key has been deprecated."]
-    UnknownError n         → ["UnknownError:",           "Lastfm API specs say nothing about this particular error:", show n]
-    CurlError s            → ["CurlError:", show s]
-
-
-disambiguate ∷ Int → LastfmError
-disambiguate n = case n of
-  1 → DoesntExist
-  2 → InvalidService
-  3 → InvalidMethod
-  4 → AuthenticationFailed
-  5 → InvalidFormat
-  6 → InvalidParameters
-  7 → InvalidResource
-  8 → OperationFailed
-  9 → InvalidSessionKey
-  10 → InvalidAPIKey
-  11 → ServiceOffline
-  12 → SubscribersOnly
-  13 → InvalidMethodSignature
-  14 → TokenHasNotAuthorized
-  15 → NotForStreaming
-  16 → TemporaryUnavailable
-  17 → LoginRequired
-  18 → TrialExpired
-  19 → DoesntExistAgain
-  20 → NotEnoughContent
-  21 → NotEnoughMembers
-  22 → NotEnoughFans
-  23 → NotEnoughNeighbours
-  24 → NoPeakRadio
-  25 → RadioNotFound
-  26 → SuspendedAPIKey
-  27 → Deprecated
-  29 → RateLimitExceeded
-  30 → TestAPIKeyDeprecated
-  _ → UnknownError n
diff --git a/src/Network/Lastfm/Event.hs b/src/Network/Lastfm/Event.hs
new file mode 100644
--- /dev/null
+++ b/src/Network/Lastfm/Event.hs
@@ -0,0 +1,71 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE UnicodeSyntax #-}
+-- | Lastfm event API
+--
+-- This module is intended to be imported qualified:
+--
+-- @
+-- import qualified Network.Lastfm.Event as Event
+-- @
+module Network.Lastfm.Event
+  ( attend, getAttendees, getInfo, getShouts, share, shout
+  ) where
+
+import Control.Applicative
+
+import Network.Lastfm.Request
+
+
+-- | Set a user's attendance status for an event.
+--
+-- <http://www.last.fm/api/show/event.attend>
+attend ∷ Request f Sign (Event → Status → APIKey → SessionKey → Ready)
+attend = api "event.attend" <* post
+{-# INLINE attend #-}
+
+
+-- | Get a list of attendees for an event.
+--
+-- Optional: 'page', 'limit'
+--
+-- <http://www.last.fm/api/show/event.getAttendees>
+getAttendees ∷ Request f Send (Event → APIKey → Ready)
+getAttendees = api "event.getAttendees"
+{-# INLINE getAttendees #-}
+
+
+-- | Get the metadata for an event on Last.fm. Includes attendance and lineup information.
+--
+-- <http://www.last.fm/api/show/event.getInfo>
+getInfo ∷ Request f Send (Event → APIKey → Ready)
+getInfo = api "event.getInfo"
+{-# INLINE getInfo #-}
+
+
+-- | Get shouts for this event. Also available as an rss feed.
+--
+-- Optional: 'page', 'limit'
+--
+-- <http://www.last.fm/api/show/event.getShouts>
+getShouts ∷ Request f Send (Event → APIKey → Ready)
+getShouts = api "event.getShouts"
+{-# INLINE getShouts #-}
+
+
+-- | Share an event with one or more Last.fm users or other friends.
+--
+-- Optional: 'public', 'message'
+--
+-- <http://www.last.fm/api/show/event.share>
+share ∷ Request f Sign (Event → Recipient → APIKey → SessionKey → Ready)
+share = api "event.share" <* post
+{-# INLINE share #-}
+
+
+-- | Shout in this event's shoutbox
+--
+-- <http://www.last.fm/api/show/event.shout>
+shout ∷ Request f Sign (Event → Message → APIKey → SessionKey → Ready)
+shout = api "event.shout" <* post
+{-# INLINE shout #-}
diff --git a/src/Network/Lastfm/Geo.hs b/src/Network/Lastfm/Geo.hs
new file mode 100644
--- /dev/null
+++ b/src/Network/Lastfm/Geo.hs
@@ -0,0 +1,127 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE UnicodeSyntax #-}
+-- | Lastfm geo API
+--
+-- This module is intended to be imported qualified:
+--
+-- @
+-- import qualified Network.Lastfm.Geo as Geo
+-- @
+module Network.Lastfm.Geo
+  ( getEvents, getMetroArtistChart, getMetroHypeArtistChart
+  , getMetroHypeTrackChart, getMetroTrackChart, getMetroUniqueArtistChart
+  , getMetroUniqueTrackChart, getMetroWeeklyChartlist, getMetros
+  , getTopArtists, getTopTracks
+  ) where
+
+import Network.Lastfm.Request
+
+
+-- | Get all events in a specific location by country or city name.
+--
+-- Optional: 'longitude', 'latitude', 'location', 'distance', 'page', 'tag', 'festivalsonly', 'limit'
+--
+-- <http://www.last.fm/api/show/geo.getEvents>
+getEvents ∷ Request f Send (APIKey → Ready)
+getEvents = api "geo.getEvents"
+{-# INLINE getEvents #-}
+
+
+-- | Get a chart of artists for a metro
+--
+-- Optional: 'start', 'end', 'page', 'limit'
+--
+-- <http://www.last.fm/api/show/geo.getMetroArtistChart>
+getMetroArtistChart ∷ Request f Send (Metro → Country → APIKey → Ready)
+getMetroArtistChart = api "geo.getMetroArtistChart"
+{-# INLINE getMetroArtistChart #-}
+
+
+-- | Get a chart of hyped (up and coming) artists for a metro
+--
+-- Optional: 'start', 'end', 'page', 'limit'
+--
+-- <http://www.last.fm/api/show/geo.getMetroHypeArtistChart>
+getMetroHypeArtistChart ∷ Request f Send (Metro → Country → APIKey → Ready)
+getMetroHypeArtistChart = api "geo.getMetroHypeArtistChart"
+{-# INLINE getMetroHypeArtistChart #-}
+
+
+-- | Get a chart of tracks for a metro
+--
+-- Optional: 'start', 'end', 'page', 'limit'
+--
+-- <http://www.last.fm/api/show/geo.getMetroHypeTrackChart>
+getMetroHypeTrackChart ∷ Request f Send (Metro → Country → APIKey → Ready)
+getMetroHypeTrackChart = api "geo.getMetroHypeTrackChart"
+{-# INLINE getMetroHypeTrackChart #-}
+
+
+-- | Get a chart of tracks for a metro
+--
+-- Optional: 'start', 'end', 'page', 'limit'
+--
+-- <http://www.last.fm/api/show/geo.getMetroTrackChart>
+getMetroTrackChart ∷ Request f Send (Metro → Country → APIKey → Ready)
+getMetroTrackChart = api "geo.getMetroTrackChart"
+{-# INLINE getMetroTrackChart #-}
+
+
+-- | Get a chart of the artists which make that metro unique
+--
+-- Optional: 'start', 'end', 'page', 'limit'
+--
+-- <http://www.last.fm/api/show/geo.getMetroUniqueArtistChart>
+getMetroUniqueArtistChart ∷ Request f Send (Metro → Country → APIKey → Ready)
+getMetroUniqueArtistChart = api "geo.getMetroUniqueArtistChart"
+{-# INLINE getMetroUniqueArtistChart #-}
+
+
+-- | Get a chart of tracks for a metro
+--
+-- Optional: 'start', 'end', 'page', 'limit'
+--
+-- <http://www.last.fm/api/show/geo.getMetroUniqueTrackChart>
+getMetroUniqueTrackChart ∷ Request f Send (Metro → Country → APIKey → Ready)
+getMetroUniqueTrackChart = api "geo.getMetroUniqueTrackChart"
+{-# INLINE getMetroUniqueTrackChart #-}
+
+
+-- | Get a list of available chart periods for this metro,
+-- expressed as date ranges which can be sent to the chart services.
+--
+-- <http://www.last.fm/api/show/geo.getMetroWeeklyChartlist>
+getMetroWeeklyChartlist ∷ Request f Send (Metro → APIKey → Ready)
+getMetroWeeklyChartlist = api "geo.getMetroWeeklyChartlist"
+{-# INLINE getMetroWeeklyChartlist #-}
+
+
+-- | Get a list of valid countries and metros for use in the other webservices
+--
+-- Optional: 'country'
+--
+-- <http://www.last.fm/api/show/geo.getMetros>
+getMetros ∷ Request f Send (APIKey → Ready)
+getMetros = api "geo.getMetros"
+{-# INLINE getMetros #-}
+
+
+-- | Get the most popular artists on Last.fm by country
+--
+-- Optional: 'limit', 'page'
+--
+-- <http://www.last.fm/api/show/geo.getTopArtists>
+getTopArtists ∷ Request f Send (Country → APIKey → Ready)
+getTopArtists = api "geo.getTopArtists"
+{-# INLINE getTopArtists #-}
+
+
+-- | Get the most popular tracks on Last.fm last week by country
+--
+-- Optional: 'limit', 'page'
+--
+-- <http://www.last.fm/api/show/geo.getTopTracks>
+getTopTracks ∷ Request f Send (Country → APIKey → Ready)
+getTopTracks = api "geo.getTopTracks"
+{-# INLINE getTopTracks #-}
diff --git a/src/Network/Lastfm/Group.hs b/src/Network/Lastfm/Group.hs
new file mode 100644
--- /dev/null
+++ b/src/Network/Lastfm/Group.hs
@@ -0,0 +1,75 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE UnicodeSyntax #-}
+-- | Lastfm group API
+--
+-- This module is intended to be imported qualified:
+--
+-- @
+-- import qualified Network.Lastfm.Group as Group
+-- @
+module Network.Lastfm.Group
+  ( getHype, getMembers, getWeeklyAlbumChart, getWeeklyArtistChart, getWeeklyChartList, getWeeklyTrackChart
+  ) where
+
+import Network.Lastfm.Request
+
+
+-- | Get the hype list for a group
+--
+-- <http://www.last.fm/api/show/group.getHype>
+getHype ∷ Request f Send (Group → APIKey → Ready)
+getHype = api "group.getHype"
+{-# INLINE getHype #-}
+
+
+-- | Get a list of members for this group.
+--
+-- Optional: 'page', 'limit'
+--
+-- <http://www.last.fm/api/show/group.getMembers>
+getMembers ∷ Request f Send (Group → APIKey → Ready)
+getMembers = api "group.getMembers"
+{-# INLINE getMembers #-}
+
+
+-- | Get an album chart for a group, for a given date range.
+-- If no date range is supplied, it will return the most recent album chart for this group.
+--
+-- Optional: 'from', 'to'
+--
+-- <http://www.last.fm/api/show/group.getWeeklyAlbumChart>
+getWeeklyAlbumChart ∷ Request f Send (Group → APIKey → Ready)
+getWeeklyAlbumChart = api "group.getWeeklyAlbumChart"
+{-# INLINE getWeeklyAlbumChart #-}
+
+
+-- | Get an artist chart for a group, for a given date range.
+-- If no date range is supplied, it will return the most recent album chart for this group.
+--
+-- Optional: 'from', 'to'
+--
+-- <http://www.last.fm/api/show/group.getWeeklyArtistChart>
+getWeeklyArtistChart ∷ Request f Send (Group → APIKey → Ready)
+getWeeklyArtistChart = api "group.getWeeklyArtistChart"
+{-# INLINE getWeeklyArtistChart #-}
+
+
+-- | Get a list of available charts for this group, expressed as
+-- date ranges which can be sent to the chart services.
+--
+-- <http://www.last.fm/api/show/group.getWeeklyChartList>
+getWeeklyChartList ∷ Request f Send (Group → APIKey → Ready)
+getWeeklyChartList = api "group.getWeeklyChartList"
+{-# INLINE getWeeklyChartList #-}
+
+
+-- | Get a track chart for a group, for a given date range.
+-- If no date range is supplied, it will return the most recent album chart for this group.
+--
+-- Optional: 'from', 'to'
+--
+-- <http://www.last.fm/api/show/group.getWeeklyTrackChart>
+getWeeklyTrackChart ∷ Request f Send (Group → APIKey → Ready)
+getWeeklyTrackChart = api "group.getWeeklyTrackChart"
+{-# INLINE getWeeklyTrackChart #-}
diff --git a/src/Network/Lastfm/Internal.hs b/src/Network/Lastfm/Internal.hs
--- a/src/Network/Lastfm/Internal.hs
+++ b/src/Network/Lastfm/Internal.hs
@@ -1,65 +1,135 @@
-{-# LANGUAGE ScopedTypeVariables #-}
--- | Response module
-{-# OPTIONS_HADDOCK hide #-}
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE EmptyDataDecls #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE KindSignatures #-}
+{-# LANGUAGE UnicodeSyntax #-}
+-- | liblastfm internals
+--
+-- You shouldn't need to import this module unless you are doing something interesting.
 module Network.Lastfm.Internal
-  ( Format(..)
-  , callAPI, callAPIsigned, (#)
-  , module Network.Lastfm
+  ( Request(..), Format(..), Auth(..), Ready
+  , R(..), wrap, unwrap, Coercing(..), render
+    -- * Lenses
+  , host, method, query
   ) where
 
-import Control.Applicative ((<$>))
-import Control.Arrow ((&&&), second)
-import Data.Function (on)
-import Data.List (sortBy)
+import Control.Applicative
+import Data.Monoid
 
-import Codec.Binary.UTF8.String (encodeString)
-import Data.ByteString.Lazy.Char8 (ByteString)
-import qualified Data.ByteString.Lazy.Char8 as BS
-import Data.Digest.Pure.MD5 (md5)
-import Data.URLEncoded (urlEncode, export)
-import Network.Curl
+import           Data.Serialize (Serialize(..))
+import           Data.ByteString (ByteString)
+import           Data.Map.Strict (Map)
+import qualified Data.Map.Strict as M
+import           Data.Text (Text)
+import qualified Data.Text as T
+import qualified Data.Text.Encoding as T
+import           Network.URI (escapeURIChar, isUnreserved)
 
-import Network.Lastfm
 
+-- | Coerce requests changing their phantom parameters.
+-- Used to ensure right flow of working with liblastfm. If you use it on your worn, then
+-- you will break abstraction
+class Coercing t where
+  coerce ∷ t (a ∷ Auth) b → t c d
 
--- Response format
-data Format = Format { errorParser ∷ Response → Maybe LastfmError, uriArgument ∷ Maybe (String, String) }
 
+-- | Lastfm API request data type
+--
+-- low-level representation
+data R (f ∷ Format) (a ∷ Auth) t = R
+  { _host   ∷ {-# UNPACK #-} !Text
+  , _method ∷ {-# UNPACK #-} !ByteString
+  , _query  ∷ !(Map Text Text)
+  }
 
--- Send POST query to Lastfm API
-callAPI ∷ Format → [(String, String)] → Lastfm Response
-callAPI Format {errorParser = e, uriArgument = arg} = query e . (maybe id (:) arg) . map (second encodeString)
+-- | Response format: either JSON or XML
+data Format = JSON | XML
 
--- Send signed POST query to Lastfm API
-callAPIsigned ∷ Format → Secret → [(String, String)] → Lastfm Response
-callAPIsigned Format {errorParser = e, uriArgument = arg} (Secret s) xs = query e zs
+-- | Authentication method
+data Auth =
+    Send -- ^ Public API. Doesn't require anything special besides API key
+  | Sign -- ^ Private API. Requires Session key and Secret as well as API key
+
+-- | Indicates that request is ready for sending
+data Ready
+
+instance Coercing (R f) where
+  coerce R { _host = h, _method = m, _query = q } = R { _host = h, _method = m, _query = q }
+  {-# INLINE coerce #-}
+
+
+-- | Lastfm API request data type
+--
+-- @a@ is authentication state. Might be 'Send' which indicates
+-- that you may send this request already or 'Sign', when request signature
+-- isn't computed yet
+--
+-- @f@ is response format. liblastfm currently supports JSON or XML
+newtype Request f a t = Request { unRequest ∷ Dual (Endo (R f a t)) }
+
+instance Coercing (Request f) where
+  coerce q = wrap $ coerce . unwrap q . coerce
+  {-# INLINE coerce #-}
+
+instance Functor (Request f a) where
+  fmap _ = coerce
+  {-# INLINE fmap #-}
+
+instance Applicative (Request f a) where
+  pure _ = wrap id
+  f <*> x = let Request g = coerce f
+                Request y = coerce x
+            in Request $ g <> y
+  {-# INLINE (<*>) #-}
+
+
+-- | Construct String from request for networking
+render ∷ R f a t → String
+render R { _host = h, _query = q } =
+  T.unpack $ mconcat [h, "?", argie q]
  where
-  ys = map (second encodeString) . filter (not . null . snd) $ xs
-  zs = maybe id (:) arg $ ("api_sig", sign ys) : ys
+  argie = T.intercalate "&" . M.foldrWithKey (\k v m → T.concat [escape k, "=", escape v] : m) []
 
-  sign ∷ [(String, String)] → String
-  sign = show . md5 . BS.pack . (++ s) . concatMap (uncurry (++)) . sortBy (compare `on` fst)
+  escape = T.concatMap (T.pack . escapeURIChar isUnreserved)
 
 
-query ∷ (ByteString → Maybe LastfmError) → [(String, String)] → IO (Either LastfmError Response)
-query γ xs = do
-  (status, body) ← (respCurlCode &&& respBody) <$> curlResponse xs
-  return $ case status of
-    CurlOK → case γ body of
-               Nothing → Right body
-               Just n → Left n
-    s → Left $ CurlError s
+-- | Wrapping to interesting 'Monoid' ('R' -> 'R') instance
+wrap ∷ (R f a t → R f a t) → Request f a t
+wrap = Request . Dual . Endo
+{-# INLINE wrap #-}
 
 
-curlResponse ∷ [(String, String)] → IO (CurlResponse_ [(String, String)] ByteString)
-curlResponse xs = withCurlDo $ curlGetResponse_ "http://ws.audioscrobbler.com/2.0/?"
-  [ CurlPostFields . map (export . urlEncode) $ xs
-  , CurlFailOnError False
-  , CurlUserAgent "Mozilla/5.0 (X11; Linux x86_64; rv:10.0) Gecko/20100101 Firefox/10.0 Iceweasel/10.0"
-  , CurlConnectTimeout 10
-  ]
+-- | Unwrapping from interesting 'Monoid' ('R' -> 'R') instance
+unwrap ∷ Request f a t → R f a t → R f a t
+unwrap = appEndo . getDual . unRequest
+{-# INLINE unwrap #-}
 
 
--- Construct query parameter
-(#) ∷ Argument a ⇒ a → (String, String)
-(#) x = (key x, value x)
+-- Miscellaneous instances
+
+instance Serialize (R f a t) where
+  put r = do
+    put $ T.encodeUtf8 (_host r)
+    put $ _method r
+    put $ mapmap T.encodeUtf8 T.encodeUtf8 (_query r)
+  get = do
+    h ← T.decodeUtf8 <$> get
+    m ← get
+    q ← mapmap T.decodeUtf8 T.decodeUtf8 <$> get
+    return R { _host = h, _method = m, _query = q }
+
+mapmap ∷ (Ord s, Ord t) ⇒ (s → t) → (a → b) → Map s a → Map t b
+mapmap f g = M.mapKeys f . M.map g
+
+
+-- | Request _host lens
+host :: Functor f => (Text -> f Text) -> R h a t -> f (R h a t)
+host f r@R { _host = h } = (\h' -> r { _host = h' }) <$> f h
+
+-- | Request http _method lens
+method :: Functor f => (ByteString -> f ByteString) -> R h a t -> f (R h a t)
+method f r@R { _method = m } = (\m' -> r { _method = m' }) <$> f m
+
+-- | Request _query string lens
+query :: Functor f => (Map Text Text -> f (Map Text Text)) -> R h a t -> f (R h a t)
+query f r@R { _query = q } = (\q' -> r { _query = q' }) <$> f q
diff --git a/src/Network/Lastfm/JSON.hs b/src/Network/Lastfm/JSON.hs
deleted file mode 100644
--- a/src/Network/Lastfm/JSON.hs
+++ /dev/null
@@ -1,41 +0,0 @@
-{-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE TemplateHaskell #-}
-module Network.Lastfm.JSON
-  ( json
-  , jsonWrapper
-  ) where
-
-import Control.Applicative ((<$>), empty)
-
-import qualified Data.Aeson as A
-import qualified Data.Aeson.Types as A
-import qualified Data.Attoparsec.Lazy as AP
-import Data.ByteString.Lazy (ByteString)
-import Language.Haskell.TH
-
-import Network.Lastfm.Internal (Format(..))
-import Network.Lastfm.Error (LastfmError, disambiguate)
-
-
-json ∷ Format
-json = Format
-  { errorParser = parser
-  , uriArgument = Just ("format","json")
-  }
-
-
-parser ∷ ByteString → Maybe LastfmError
-parser xs = case AP.parse A.json xs of
-  AP.Done _ j → case A.parse p j of
-    A.Success v → Just v
-    _ → Nothing
-  _ → Nothing
- where
-  p (A.Object v) = disambiguate <$> v A..: "error"
-  p _ = empty
-
-
-jsonWrapper ∷ [String] → Q [Dec]
-jsonWrapper = mapM func
-  where
-   func xs = funD (mkName xs) [clause [] (normalB $ appE (varE (mkName ("API." ++ xs))) [e| json |]) []]
diff --git a/src/Network/Lastfm/JSON/Album.hs b/src/Network/Lastfm/JSON/Album.hs
deleted file mode 100644
--- a/src/Network/Lastfm/JSON/Album.hs
+++ /dev/null
@@ -1,43 +0,0 @@
-{-# LANGUAGE CPP #-}
-{-# LANGUAGE TemplateHaskell #-}
--- | Album API module
-{-# OPTIONS_HADDOCK prune #-}
-module Network.Lastfm.JSON.Album
-  ( addTags, getBuyLinks, getInfo, getShouts, getTags
-  , getTopTags, removeTag, search, share
-  ) where
-
-#include "album.docs"
-
-import Network.Lastfm.Internal
-import Network.Lastfm.JSON (jsonWrapper)
-import qualified Network.Lastfm.API.Album as API
-
-$(jsonWrapper ["addTags", "getBuyLinks", "getInfo", "getShouts", "getTags", "getTopTags", "removeTag", "search", "share"])
-
-__addTags__
-addTags ∷ (Artist, Album) → [Tag] → APIKey → SessionKey → Secret → Lastfm Response
-
-__getBuyLinks__
-getBuyLinks ∷ Either (Artist, Album) Mbid → Maybe Autocorrect → Country → APIKey → Lastfm Response
-
-__getInfo__
-getInfo ∷ Either (Artist, Album) Mbid → Maybe Autocorrect → Maybe Language → Maybe Username → APIKey → Lastfm Response
-
-__getShouts__
-getShouts ∷ Either (Artist, Album) Mbid → Maybe Autocorrect → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__getTags__
-getTags ∷ Either (Artist, Album) Mbid → Maybe Autocorrect → Either User (SessionKey, Secret) → APIKey → Lastfm Response
-
-__getTopTags__
-getTopTags ∷ Either (Artist, Album) Mbid → Maybe Autocorrect → APIKey → Lastfm Response
-
-__removeTag__
-removeTag ∷ Artist → Album → Tag → APIKey → SessionKey → Secret → Lastfm Response
-
-__search__
-search ∷ Album → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__share__
-share ∷ Artist → Album → Recipient → Maybe Message → Maybe Public → APIKey → SessionKey → Secret → Lastfm Response
diff --git a/src/Network/Lastfm/JSON/Artist.hs b/src/Network/Lastfm/JSON/Artist.hs
deleted file mode 100644
--- a/src/Network/Lastfm/JSON/Artist.hs
+++ /dev/null
@@ -1,71 +0,0 @@
-{-# LANGUAGE CPP #-}
-{-# LANGUAGE TemplateHaskell #-}
--- | Artist API module
-{-# OPTIONS_HADDOCK prune #-}
-module Network.Lastfm.JSON.Artist
-  ( addTags, getCorrection, getEvents, getImages, getInfo
-  , getPastEvents, getPodcast, getShouts, getSimilar, getTags, getTopAlbums
-  , getTopFans, getTopTags, getTopTracks, removeTag, search, share, shout
-  ) where
-
-#include "artist.docs"
-
-import Network.Lastfm.Internal
-import Network.Lastfm.JSON (jsonWrapper)
-import qualified Network.Lastfm.API.Artist as API
-
-$(jsonWrapper ["addTags", "getCorrection", "getEvents", "getImages", "getInfo", "getPastEvents", "getPodcast", "getShouts", "getSimilar", "getTags", "getTopAlbums", "getTopFans", "getTopTags", "getTopTracks", "removeTag", "search", "share", "shout"])
-
-__addTags__
-addTags ∷ Artist → [Tag] → APIKey → SessionKey → Secret → Lastfm Response
-
-__getCorrection__
-getCorrection ∷ Artist → APIKey → Lastfm Response
-
-__getEvents__
-getEvents ∷ Either Artist Mbid → Maybe Autocorrect → Maybe Page → Maybe Limit → Maybe FestivalsOnly → APIKey → Lastfm Response
-
-__getImages__
-getImages ∷ Either Artist Mbid → Maybe Autocorrect → Maybe Page → Maybe Limit → Maybe Order → APIKey → Lastfm Response
-
-__getInfo__
-getInfo ∷ Either Artist Mbid → Maybe Autocorrect → Maybe Language → Maybe Username → APIKey → Lastfm Response
-
-__getPastEvents__
-getPastEvents ∷ Either Artist Mbid → Maybe Autocorrect → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__getPodcast__
-getPodcast ∷ Either Artist Mbid → Maybe Autocorrect → APIKey → Lastfm Response
-
-__getShouts__
-getShouts ∷ Either Artist Mbid → Maybe Autocorrect → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__getSimilar__
-getSimilar ∷ Either Artist Mbid → Maybe Autocorrect → Maybe Limit → APIKey → Lastfm Response
-
-__getTags__
-getTags ∷ Either Artist Mbid → Maybe Autocorrect → Either User (SessionKey, Secret) → APIKey → Lastfm Response
-
-__getTopAlbums__
-getTopAlbums ∷ Either Artist Mbid → Maybe Autocorrect → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__getTopFans__
-getTopFans ∷ Either Artist Mbid → Maybe Autocorrect → APIKey → Lastfm Response
-
-__getTopTags__
-getTopTags ∷ Either Artist Mbid → Maybe Autocorrect → APIKey → Lastfm Response
-
-__getTopTracks__
-getTopTracks ∷ Either Artist Mbid → Maybe Autocorrect → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__removeTag__
-removeTag ∷ Artist → Tag → APIKey → SessionKey → Secret → Lastfm Response
-
-__search__
-search ∷ Artist → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__share__
-share ∷ Artist → Recipient → Maybe Message → Maybe Public → APIKey → SessionKey → Secret → Lastfm Response
-
-__shout__
-shout ∷ Artist → Message → APIKey → SessionKey → Secret → Lastfm Response
diff --git a/src/Network/Lastfm/JSON/Auth.hs b/src/Network/Lastfm/JSON/Auth.hs
deleted file mode 100644
--- a/src/Network/Lastfm/JSON/Auth.hs
+++ /dev/null
@@ -1,7 +0,0 @@
--- | Auth API module
-{-# OPTIONS_HADDOCK prune #-}
-module Network.Lastfm.JSON.Auth
-  ( module Network.Lastfm.API.Auth
-  ) where
-
-import Network.Lastfm.API.Auth
diff --git a/src/Network/Lastfm/JSON/Chart.hs b/src/Network/Lastfm/JSON/Chart.hs
deleted file mode 100644
--- a/src/Network/Lastfm/JSON/Chart.hs
+++ /dev/null
@@ -1,34 +0,0 @@
-{-# LANGUAGE CPP #-}
-{-# LANGUAGE TemplateHaskell #-}
--- | Chart API module
-{-# OPTIONS_HADDOCK prune #-}
-module Network.Lastfm.JSON.Chart
-  ( getHypedArtists, getHypedTracks, getLovedTracks
-  , getTopArtists, getTopTags, getTopTracks
-  ) where
-
-#include "chart.docs"
-
-import Network.Lastfm.Internal
-import Network.Lastfm.JSON (jsonWrapper)
-import qualified Network.Lastfm.API.Chart as API
-
-$(jsonWrapper ["getHypedArtists", "getHypedTracks", "getLovedTracks", "getTopArtists", "getTopTags", "getTopTracks"])
-
-__getHypedArtists__
-getHypedArtists ∷ Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__getHypedTracks__
-getHypedTracks ∷ Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__getLovedTracks__
-getLovedTracks ∷ Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__getTopArtists__
-getTopArtists ∷ Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__getTopTags__
-getTopTags ∷ Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__getTopTracks__
-getTopTracks ∷ Maybe Page → Maybe Limit → APIKey → Lastfm Response
diff --git a/src/Network/Lastfm/JSON/Event.hs b/src/Network/Lastfm/JSON/Event.hs
deleted file mode 100644
--- a/src/Network/Lastfm/JSON/Event.hs
+++ /dev/null
@@ -1,33 +0,0 @@
-{-# LANGUAGE CPP #-}
-{-# LANGUAGE TemplateHaskell #-}
--- | Event API module
-{-# OPTIONS_HADDOCK prune #-}
-module Network.Lastfm.JSON.Event
-  ( attend, getAttendees, getInfo, getShouts, share, shout
-  ) where
-
-#include "event.docs"
-
-import Network.Lastfm.Internal
-import Network.Lastfm.JSON (jsonWrapper)
-import qualified Network.Lastfm.API.Event as API
-
-$(jsonWrapper ["attend", "getAttendees", "getInfo", "getShouts", "share", "shout"])
-
-__attend__
-attend ∷ Event → Status → APIKey → SessionKey → Secret → Lastfm Response
-
-__getAttendees__
-getAttendees ∷ Event → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__getInfo__
-getInfo ∷ Event → APIKey → Lastfm Response
-
-__getShouts__
-getShouts ∷ Event → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__share__
-share ∷ Event → Recipient → Maybe Message → Maybe Public → APIKey → SessionKey → Secret → Lastfm Response
-
-__shout__
-shout ∷ Event → Message → APIKey → SessionKey → Secret → Lastfm Response
diff --git a/src/Network/Lastfm/JSON/Geo.hs b/src/Network/Lastfm/JSON/Geo.hs
deleted file mode 100644
--- a/src/Network/Lastfm/JSON/Geo.hs
+++ /dev/null
@@ -1,57 +0,0 @@
-{-# LANGUAGE CPP #-}
-{-# LANGUAGE TemplateHaskell #-}
--- | Geo API module
-{-# OPTIONS_HADDOCK prune #-}
-module Network.Lastfm.JSON.Geo
-  ( getEvents, getMetroArtistChart, getMetroHypeArtistChart, getMetroHypeTrackChart
-  , getMetroTrackChart, getMetroUniqueArtistChart, getMetroUniqueTrackChart
-  , getMetroWeeklyChartlist, getMetros, getTopArtists, getTopTracks
-  ) where
-
-#include "geo.docs"
-
-import Network.Lastfm.Internal
-import Network.Lastfm.JSON (jsonWrapper)
-import qualified Network.Lastfm.API.Geo as API
-
-$(jsonWrapper ["getEvents", "getMetroArtistChart", "getMetroHypeArtistChart", "getMetroHypeTrackChart", "getMetroTrackChart", "getMetroUniqueArtistChart", "getMetroUniqueTrackChart", "getMetroWeeklyChartlist", "getMetros", "getTopArtists", "getTopTracks"])
-
-__getEvents__
-getEvents ∷ Maybe Latitude
-          → Maybe Longitude
-          → Maybe Location
-          → Maybe Distance
-          → Maybe Page
-          → Maybe Limit
-          → APIKey
-          → Lastfm Response
-
-__getMetroArtistChart__
-getMetroArtistChart ∷ Country → Metro → Maybe Start → Maybe End → APIKey → Lastfm Response
-
-__getMetroHypeArtistChart__
-getMetroHypeArtistChart ∷ Country → Metro → Maybe Start → Maybe End → APIKey → Lastfm Response
-
-__getMetroHypeTrackChart__
-getMetroHypeTrackChart ∷ Country → Metro → Maybe Start → Maybe End → APIKey → Lastfm Response
-
-__getMetroTrackChart__
-getMetroTrackChart ∷ Country → Metro → Maybe Start → Maybe End → APIKey → Lastfm Response
-
-__getMetroUniqueArtistChart__
-getMetroUniqueArtistChart ∷ Country → Metro → Maybe Start → Maybe End → APIKey → Lastfm Response
-
-__getMetroUniqueTrackChart__
-getMetroUniqueTrackChart ∷ Country → Metro → Maybe Start → Maybe End → APIKey → Lastfm Response
-
-__getMetroWeeklyChartlist__
-getMetroWeeklyChartlist ∷ Metro → APIKey → Lastfm Response
-
-__getMetros__
-getMetros ∷ Maybe Country → APIKey → Lastfm Response
-
-__getTopArtists__
-getTopArtists ∷ Country → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__getTopTracks__
-getTopTracks ∷ Country → Maybe Location → Maybe Page → Maybe Limit → APIKey → Lastfm Response
diff --git a/src/Network/Lastfm/JSON/Group.hs b/src/Network/Lastfm/JSON/Group.hs
deleted file mode 100644
--- a/src/Network/Lastfm/JSON/Group.hs
+++ /dev/null
@@ -1,33 +0,0 @@
-{-# LANGUAGE CPP #-}
-{-# LANGUAGE TemplateHaskell #-}
--- | Group API module
-{-# OPTIONS_HADDOCK prune #-}
-module Network.Lastfm.JSON.Group
-  ( getHype, getMembers, getWeeklyChartList, getWeeklyAlbumChart, getWeeklyArtistChart, getWeeklyTrackChart
-  ) where
-
-#include "group.docs"
-
-import Network.Lastfm.Internal
-import Network.Lastfm.JSON (jsonWrapper)
-import qualified Network.Lastfm.API.Group as API
-
-$(jsonWrapper ["getHype", "getMembers", "getWeeklyChartList", "getWeeklyAlbumChart", "getWeeklyArtistChart", "getWeeklyTrackChart"])
-
-__getHype__
-getHype ∷ Group → APIKey → Lastfm Response
-
-__getMembers__
-getMembers ∷ Group → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__getWeeklyChartList__
-getWeeklyChartList ∷ Group → APIKey → Lastfm Response
-
-__getWeeklyAlbumChart__
-getWeeklyAlbumChart ∷ Group → Maybe From → Maybe To → APIKey → Lastfm Response
-
-__getWeeklyArtistChart__
-getWeeklyArtistChart ∷ Group → Maybe From → Maybe To → APIKey → Lastfm Response
-
-__getWeeklyTrackChart__
-getWeeklyTrackChart ∷ Group → Maybe From → Maybe To → APIKey → Lastfm Response
diff --git a/src/Network/Lastfm/JSON/Library.hs b/src/Network/Lastfm/JSON/Library.hs
deleted file mode 100644
--- a/src/Network/Lastfm/JSON/Library.hs
+++ /dev/null
@@ -1,46 +0,0 @@
-{-# LANGUAGE CPP #-}
-{-# LANGUAGE TemplateHaskell #-}
--- | Library API module
-{-# OPTIONS_HADDOCK prune #-}
-module Network.Lastfm.JSON.Library
-  ( addAlbum, addArtist, addTrack, getAlbums, getArtists, getTracks
-  , removeAlbum, removeArtist, removeScrobble, removeTrack
-  ) where
-
-#include "library.docs"
-
-import Network.Lastfm.Internal
-import Network.Lastfm.JSON (jsonWrapper)
-import qualified Network.Lastfm.API.Library as API
-
-$(jsonWrapper ["addAlbum", "addArtist", "addTrack", "getAlbums", "getArtists", "getTracks", "removeAlbum", "removeArtist", "removeScrobble", "removeTrack"])
-
-__addAlbum__
-addAlbum ∷ Artist → Album → APIKey → SessionKey → Secret → Lastfm Response
-
-__addArtist__
-addArtist ∷ Artist → APIKey → SessionKey → Secret → Lastfm Response
-
-__addTrack__
-addTrack ∷ Artist → Track → APIKey → SessionKey → Secret → Lastfm Response
-
-__getAlbums__
-getAlbums ∷ User → Maybe Artist → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__getArtists__
-getArtists ∷ User → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__getTracks__
-getTracks ∷ User → Maybe Artist → Maybe Album → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__removeAlbum__
-removeAlbum ∷ Artist → Album → APIKey → SessionKey → Secret → Lastfm Response
-
-__removeArtist__
-removeArtist ∷ Artist → APIKey → SessionKey → Secret → Lastfm Response
-
-__removeScrobble__
-removeScrobble ∷ Artist → Track → Timestamp → APIKey → SessionKey → Secret → Lastfm Response
-
-__removeTrack__
-removeTrack ∷ Artist → Track → APIKey → SessionKey → Secret → Lastfm Response
diff --git a/src/Network/Lastfm/JSON/Playlist.hs b/src/Network/Lastfm/JSON/Playlist.hs
deleted file mode 100644
--- a/src/Network/Lastfm/JSON/Playlist.hs
+++ /dev/null
@@ -1,21 +0,0 @@
-{-# LANGUAGE CPP #-}
-{-# LANGUAGE TemplateHaskell #-}
--- | Playlist API module
-{-# OPTIONS_HADDOCK prune #-}
-module Network.Lastfm.JSON.Playlist
-  ( addTrack, create
-  ) where
-
-#include "playlist.docs"
-
-import Network.Lastfm.Internal
-import Network.Lastfm.JSON (jsonWrapper)
-import qualified Network.Lastfm.API.Playlist as API
-
-$(jsonWrapper ["addTrack", "create"])
-
-__addTrack__
-addTrack ∷ Playlist → Artist → Track → APIKey → SessionKey → Secret → Lastfm Response
-
-__create__
-create ∷ Maybe Title → Maybe Description → APIKey → SessionKey → Secret → Lastfm Response
diff --git a/src/Network/Lastfm/JSON/Radio.hs b/src/Network/Lastfm/JSON/Radio.hs
deleted file mode 100644
--- a/src/Network/Lastfm/JSON/Radio.hs
+++ /dev/null
@@ -1,32 +0,0 @@
-{-# LANGUAGE CPP #-}
-{-# LANGUAGE TemplateHaskell #-}
--- | Radio API module
-{-# OPTIONS_HADDOCK prune #-}
-module Network.Lastfm.JSON.Radio
-  ( getPlaylist, search, tune
-  ) where
-
-#include "radio.docs"
-
-import Network.Lastfm.Internal
-import Network.Lastfm.JSON (jsonWrapper)
-import qualified Network.Lastfm.API.Radio as API
-
-$(jsonWrapper ["getPlaylist", "search", "tune"])
-
-__getPlaylist__
-getPlaylist ∷ Maybe Discovery
-            → Maybe RTP
-            → Maybe BuyLinks
-            → Multiplier
-            → Bitrate
-            → APIKey
-            → SessionKey
-            → Secret
-            → Lastfm Response
-
-__search__
-search ∷ Name → APIKey → Lastfm Response
-
-__tune__
-tune ∷ Maybe Language → Station → APIKey → SessionKey → Secret → Lastfm Response
diff --git a/src/Network/Lastfm/JSON/Tag.hs b/src/Network/Lastfm/JSON/Tag.hs
deleted file mode 100644
--- a/src/Network/Lastfm/JSON/Tag.hs
+++ /dev/null
@@ -1,43 +0,0 @@
-{-# LANGUAGE CPP #-}
-{-# LANGUAGE TemplateHaskell #-}
--- | Tag API module
-{-# OPTIONS_HADDOCK prune #-}
-module Network.Lastfm.JSON.Tag
-  ( getInfo, getSimilar, getTopAlbums, getTopArtists, getTopTags, getTopTracks
-  , getWeeklyArtistChart, getWeeklyChartList, search
-  ) where
-
-#include "tag.docs"
-
-import Network.Lastfm.Internal
-import Network.Lastfm.JSON (jsonWrapper)
-import qualified Network.Lastfm.API.Tag as API
-
-$(jsonWrapper ["getInfo", "getSimilar", "getTopAlbums", "getTopArtists", "getTopTags", "getTopTracks", "getWeeklyArtistChart", "getWeeklyChartList", "search"])
-
-__getInfo__
-getInfo ∷ Tag → Maybe Language → APIKey → Lastfm Response
-
-__getSimilar__
-getSimilar ∷ Tag → APIKey → Lastfm Response
-
-__getTopAlbums__
-getTopAlbums ∷ Tag → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__getTopArtists__
-getTopArtists ∷ Tag → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__getTopTags__
-getTopTags ∷ APIKey → Lastfm Response
-
-__getTopTracks__
-getTopTracks ∷ Tag → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__getWeeklyArtistChart__
-getWeeklyArtistChart ∷ Tag → Maybe From → Maybe To → Maybe Limit → APIKey → Lastfm Response
-
-__getWeeklyChartList__
-getWeeklyChartList ∷ Tag → APIKey → Lastfm Response
-
-__search__
-search ∷ Tag → Maybe Page → Maybe Limit → APIKey → Lastfm Response
diff --git a/src/Network/Lastfm/JSON/Tasteometer.hs b/src/Network/Lastfm/JSON/Tasteometer.hs
deleted file mode 100644
--- a/src/Network/Lastfm/JSON/Tasteometer.hs
+++ /dev/null
@@ -1,20 +0,0 @@
-{-# LANGUAGE CPP #-}
-{-# LANGUAGE TemplateHaskell #-}
--- | Tasteometer API module
-{-# OPTIONS_HADDOCK prune #-}
-module Network.Lastfm.JSON.Tasteometer
-  ( compare
-  ) where
-
-#include "tasteometer.docs"
-
-import Prelude hiding (compare)
-
-import Network.Lastfm.Internal
-import Network.Lastfm.JSON (jsonWrapper)
-import qualified Network.Lastfm.API.Tasteometer as API
-
-$(jsonWrapper ["compare"])
-
-__compare__
-compare ∷ Value → Value → Maybe Limit → APIKey → Lastfm Response
diff --git a/src/Network/Lastfm/JSON/Track.hs b/src/Network/Lastfm/JSON/Track.hs
deleted file mode 100644
--- a/src/Network/Lastfm/JSON/Track.hs
+++ /dev/null
@@ -1,91 +0,0 @@
-{-# LANGUAGE CPP #-}
-{-# LANGUAGE TemplateHaskell #-}
--- | Track API module
-{-# OPTIONS_HADDOCK prune #-}
-module Network.Lastfm.JSON.Track
-  ( addTags, ban, getBuyLinks, getCorrection, getFingerprintMetadata
-  , getInfo, getShouts, getSimilar, getTags, getTopFans, getTopTags
-  , love, removeTag, scrobble, search, share, unban, unlove, updateNowPlaying
-  ) where
-
-#include "track.docs"
-
-import Network.Lastfm.Internal
-import Network.Lastfm.JSON (jsonWrapper)
-import qualified Network.Lastfm.API.Track as API
-
-$(jsonWrapper ["addTags", "ban", "getBuyLinks", "getCorrection", "getFingerprintMetadata", "getInfo", "getShouts", "getSimilar", "getTags", "getTopFans", "getTopTags", "love", "removeTag", "scrobble", "search", "share", "unban", "unlove", "updateNowPlaying"])
-
-__addTags__
-addTags ∷ Artist → Track → [Tag] → APIKey → SessionKey → Secret → Lastfm Response
-
-__ban__
-ban ∷ Artist → Track → APIKey → SessionKey → Secret → Lastfm Response
-
-__getBuyLinks__
-getBuyLinks ∷ Either (Artist, Track) Mbid → Maybe Autocorrect → Country → APIKey → Lastfm Response
-
-__getCorrection__
-getCorrection ∷ Artist → Track → APIKey → Lastfm Response
-
-__getFingerprintMetadata__
-getFingerprintMetadata ∷ Fingerprint → APIKey → Lastfm Response
-
-__getInfo__
-getInfo ∷ Either (Artist, Track) Mbid → Maybe Autocorrect → Maybe Username → APIKey → Lastfm Response
-
-__getShouts__
-getShouts ∷ Either (Artist, Track) Mbid → Maybe Autocorrect → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__getSimilar__
-getSimilar ∷ Either (Artist, Track) Mbid → Maybe Autocorrect → Maybe Limit → APIKey → Lastfm Response
-
-__getTags__
-getTags ∷ Either (Artist, Track) Mbid → Maybe Autocorrect → Either User (SessionKey, Secret) → APIKey → Lastfm Response
-
-__getTopFans__
-getTopFans ∷ Either (Artist, Track) Mbid → Maybe Autocorrect → APIKey → Lastfm Response
-
-__getTopTags__
-getTopTags ∷ Either (Artist, Track) Mbid → Maybe Autocorrect → APIKey → Lastfm Response
-
-__love__
-love ∷ Artist → Track → APIKey → SessionKey → Secret → Lastfm Response
-
-__removeTag__
-removeTag ∷ Artist → Track → Tag → APIKey → SessionKey → Secret → Lastfm Response
-
-__scrobble__
-scrobble ∷ ( Timestamp, Maybe Album, Artist, Track, Maybe AlbumArtist
-           , Maybe Duration, Maybe StreamId, Maybe ChosenByUser
-           , Maybe Context, Maybe TrackNumber, Maybe Mbid )
-         → APIKey
-         → SessionKey
-         → Secret
-         → Lastfm Response
-
-__search__
-search ∷ Track → Maybe Page → Maybe Limit → Maybe Artist → APIKey → Lastfm Response
-
-__share__
-share ∷ Artist → Track → Recipient → Maybe Message → Maybe Public → APIKey → SessionKey → Secret → Lastfm Response
-
-__unban__
-unban ∷ Artist → Track → APIKey → SessionKey → Secret → Lastfm Response
-
-__unlove__
-unlove ∷ Artist → Track → APIKey → SessionKey → Secret → Lastfm Response
-
-__updateNowPlaying__
-updateNowPlaying ∷ Artist
-                 → Track
-                 → Maybe Album
-                 → Maybe AlbumArtist
-                 → Maybe Context
-                 → Maybe TrackNumber
-                 → Maybe Mbid
-                 → Maybe Duration
-                 → APIKey
-                 → SessionKey
-                 → Secret
-                 → Lastfm Response
diff --git a/src/Network/Lastfm/JSON/User.hs b/src/Network/Lastfm/JSON/User.hs
deleted file mode 100644
--- a/src/Network/Lastfm/JSON/User.hs
+++ /dev/null
@@ -1,94 +0,0 @@
-{-# LANGUAGE CPP #-}
-{-# LANGUAGE TemplateHaskell #-}
--- | User API module
-{-# OPTIONS_HADDOCK prune #-}
-module Network.Lastfm.JSON.User
-  ( getArtistTracks, getBannedTracks, getEvents, getFriends, getInfo, getLovedTracks
-  , getNeighbours, getNewReleases, getPastEvents, getPersonalTags, getPlaylists, getRecentStations
-  , getRecentTracks, getRecommendedArtists, getRecommendedEvents, getShouts, getTopAlbums
-  , getTopArtists, getTopTags, getTopTracks, getWeeklyAlbumChart, getWeeklyArtistChart
-  , getWeeklyChartList, getWeeklyTrackChart, shout
-  ) where
-
-#include "user.docs"
-
-import Network.Lastfm.Internal
-import Network.Lastfm.JSON (jsonWrapper)
-import qualified Network.Lastfm.API.User as API
-
-$(jsonWrapper ["getArtistTracks", "getBannedTracks", "getEvents", "getFriends", "getInfo", "getLovedTracks", "getNeighbours", "getNewReleases", "getPastEvents", "getPersonalTags", "getPlaylists", "getRecentStations", "getRecentTracks", "getRecommendedArtists", "getRecommendedEvents", "getShouts", "getTopAlbums", "getTopArtists", "getTopTags", "getTopTracks", "getWeeklyAlbumChart", "getWeeklyArtistChart", "getWeeklyChartList", "getWeeklyTrackChart", "shout"])
-
-__getArtistTracks__
-getArtistTracks ∷ User → Artist → Maybe StartTimestamp → Maybe EndTimestamp → Maybe Page → APIKey → Lastfm Response
-
-__getBannedTracks__
-getBannedTracks ∷ User → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__getEvents__
-getEvents ∷ User → Maybe Page → Maybe Limit → Maybe FestivalsOnly → APIKey → Lastfm Response
-
-__getFriends__
-getFriends ∷ User → Maybe RecentTracks → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__getInfo__
-getInfo ∷ Maybe User → APIKey → Lastfm Response
-
-__getLovedTracks__
-getLovedTracks ∷ User → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__getNeighbours__
-getNeighbours ∷ User → Maybe Limit → APIKey → Lastfm Response
-
-__getNewReleases__
-getNewReleases ∷ User → Maybe UseRecs → APIKey → Lastfm Response
-
-__getPastEvents__
-getPastEvents ∷ User → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__getPersonalTags__
-getPersonalTags ∷ User → Tag → TaggingType → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__getPlaylists__
-getPlaylists ∷ User → APIKey → Lastfm Response
-
-__getRecentStations__
-getRecentStations ∷ User → Maybe Page → Maybe Limit → APIKey → SessionKey → Secret → Lastfm Response
-
-__getRecentTracks__
-getRecentTracks ∷ User → Maybe Page → Maybe Limit → Maybe From → Maybe To → APIKey → Lastfm Response
-
-__getRecommendedArtists__
-getRecommendedArtists ∷ Maybe Page → Maybe Limit → APIKey → SessionKey → Secret → Lastfm Response
-
-__getRecommendedEvents__
-getRecommendedEvents ∷ Maybe Page → Maybe Limit → APIKey → SessionKey → Secret → Lastfm Response
-
-__getShouts__
-getShouts ∷ User → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__getTopAlbums__
-getTopAlbums ∷ User → Maybe Period → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__getTopArtists__
-getTopArtists ∷ User → Maybe Period → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__getTopTags__
-getTopTags ∷ User → Maybe Limit → APIKey → Lastfm Response
-
-__getTopTracks__
-getTopTracks ∷ User → Maybe Period → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__getWeeklyAlbumChart__
-getWeeklyAlbumChart ∷ User → Maybe From → Maybe To → APIKey → Lastfm Response
-
-__getWeeklyArtistChart__
-getWeeklyArtistChart ∷ User → Maybe From → Maybe To → APIKey → Lastfm Response
-
-__getWeeklyChartList__
-getWeeklyChartList ∷ User → APIKey → Lastfm Response
-
-__getWeeklyTrackChart__
-getWeeklyTrackChart ∷ User → Maybe From → Maybe To → APIKey → Lastfm Response
-
-__shout__
-shout ∷ User → Message → APIKey → SessionKey → Secret → Lastfm Response
diff --git a/src/Network/Lastfm/JSON/Venue.hs b/src/Network/Lastfm/JSON/Venue.hs
deleted file mode 100644
--- a/src/Network/Lastfm/JSON/Venue.hs
+++ /dev/null
@@ -1,24 +0,0 @@
-{-# LANGUAGE CPP #-}
-{-# LANGUAGE TemplateHaskell #-}
--- | Venue API module
-{-# OPTIONS_HADDOCK prune #-}
-module Network.Lastfm.JSON.Venue
-  ( getEvents, getPastEvents, search
-  ) where
-
-#include "venue.docs"
-
-import Network.Lastfm.Internal
-import Network.Lastfm.JSON (jsonWrapper)
-import qualified Network.Lastfm.API.Venue as API
-
-$(jsonWrapper ["getEvents", "getPastEvents", "search"])
-
-__getEvents__
-getEvents ∷ Venue → Maybe FestivalsOnly → APIKey → Lastfm Response
-
-__getPastEvents__
-getPastEvents ∷ Venue → Maybe FestivalsOnly → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__search__
-search ∷ Venuename → Maybe Page → Maybe Limit → Maybe Country → APIKey → Lastfm Response
diff --git a/src/Network/Lastfm/Library.hs b/src/Network/Lastfm/Library.hs
new file mode 100644
--- /dev/null
+++ b/src/Network/Lastfm/Library.hs
@@ -0,0 +1,105 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE UnicodeSyntax #-}
+-- | Lastfm library API
+--
+-- This module is intended to be imported qualified:
+--
+-- @
+-- import qualified Network.Lastfm.Library as Library
+-- @
+module Network.Lastfm.Library
+  ( addAlbum, addArtist, addTrack
+  , getAlbums, getArtists, getTracks
+  , removeAlbum, removeArtist, removeScrobble, removeTrack
+  ) where
+
+import Control.Applicative
+
+import Network.Lastfm.Request
+
+
+-- | Add an album or collection of albums to a user's Last.fm library
+--
+-- <http://www.last.fm/api/show/library.addAlbum>
+addAlbum ∷ Request f Sign (Artist → Album → APIKey → SessionKey → Ready)
+addAlbum = api "library.addAlbum" <* post
+{-# INLINE addAlbum #-}
+
+
+-- | Add an artist to a user's Last.fm library
+--
+-- <http://www.last.fm/api/show/library.addArtist>
+addArtist ∷ Request f Sign (Artist → APIKey → SessionKey → Ready)
+addArtist = api "library.addArtist" <* post
+{-# INLINE addArtist #-}
+
+
+-- | Add a track to a user's Last.fm library
+--
+-- <http://www.last.fm/api/show/library.addTrack>
+addTrack ∷ Request f Sign (Artist → Track → APIKey → SessionKey → Ready)
+addTrack = api "library.addTrack" <* post
+{-# INLINE addTrack #-}
+
+
+-- | A paginated list of all the albums in a user's library, with play counts and tag counts.
+--
+-- Optional: 'artist', 'limit', 'page'
+--
+-- <http://www.last.fm/api/show/library.getAlbums>
+getAlbums ∷ Request f Send (User → APIKey → Ready)
+getAlbums = api "library.getAlbums"
+{-# INLINE getAlbums #-}
+
+
+-- | A paginated list of all the artists in a user's library, with play counts and tag counts.
+--
+-- Optional: 'limit', 'page'
+--
+-- <http://www.last.fm/api/show/library.getArtists>
+getArtists ∷ Request f Send (User → APIKey → Ready)
+getArtists = api "library.getArtists"
+{-# INLINE getArtists #-}
+
+
+-- | A paginated list of all the tracks in a user's library, with play counts and tag counts.
+--
+-- Optional: 'artist', 'album', 'page', 'limit'
+--
+-- <http://www.last.fm/api/show/library.getTracks>
+getTracks ∷ Request f Send (User → APIKey → Ready)
+getTracks = api "library.getTracks"
+{-# INLINE getTracks #-}
+
+
+-- | Remove an album from a user's Last.fm library
+--
+-- <http://www.last.fm/api/show/library.removeAlbum>
+removeAlbum ∷ Request f Sign (Artist → Album → APIKey → SessionKey → Ready)
+removeAlbum = api "library.removeAlbum" <* post
+{-# INLINE removeAlbum #-}
+
+
+-- | Remove an artist from a user's Last.fm library
+--
+-- <http://www.last.fm/api/show/library.removeArtist>
+removeArtist ∷ Request f Sign (Artist → APIKey → SessionKey → Ready)
+removeArtist = api "library.removeArtist" <* post
+{-# INLINE removeArtist #-}
+
+
+-- | Remove a scrobble from a user's Last.fm library
+--
+-- <http://www.last.fm/api/show/library.removeScrobble>
+removeScrobble ∷ Request f Sign (Artist → Track → Timestamp → APIKey → SessionKey → Ready)
+removeScrobble = api "library.removeScrobble" <* post
+{-# INLINE removeScrobble #-}
+
+
+-- | Remove a track from a user's Last.fm library
+--
+-- <http://www.last.fm/api/show/library.removeTrack>
+removeTrack ∷ Request f Sign (Artist → Track → APIKey → SessionKey → Ready)
+removeTrack = api "library.removeTrack" <* post
+{-# INLINE removeTrack #-}
diff --git a/src/Network/Lastfm/Playlist.hs b/src/Network/Lastfm/Playlist.hs
new file mode 100644
--- /dev/null
+++ b/src/Network/Lastfm/Playlist.hs
@@ -0,0 +1,35 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE UnicodeSyntax #-}
+-- | Lastfm playlist API
+--
+-- This module is intended to be imported qualified:
+--
+-- @
+-- import qualified Network.Lastfm.Playlist as Playlist
+-- @
+module Network.Lastfm.Playlist
+  ( addTrack, create
+  ) where
+
+import Control.Applicative
+
+import Network.Lastfm.Request
+
+
+-- | Add a track to a Last.fm user's playlist
+--
+-- <http://www.last.fm/api/show/playlist.addTrack>
+addTrack ∷ Request f Sign (Playlist → Artist → Track → APIKey → SessionKey → Ready)
+addTrack = api "playlist.addTrack" <* post
+{-# INLINE addTrack #-}
+
+
+-- | Create a Last.fm playlist on behalf of a user
+--
+-- Optional: 'title', 'description'
+--
+-- <http://www.last.fm/api/show/playlist.create>
+create ∷ Request f Sign (APIKey → SessionKey → Ready)
+create = api "playlist.create" <* post
+{-# INLINE create #-}
diff --git a/src/Network/Lastfm/Radio.hs b/src/Network/Lastfm/Radio.hs
new file mode 100644
--- /dev/null
+++ b/src/Network/Lastfm/Radio.hs
@@ -0,0 +1,46 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE UnicodeSyntax #-}
+-- | Lastfm radio API
+--
+-- This module is intended to be imported qualified:
+--
+-- @
+-- import qualified Network.Lastfm.Radio as Radio
+-- @
+module Network.Lastfm.Radio
+  ( getPlaylist, search, tune
+  ) where
+
+import Control.Applicative
+
+import Network.Lastfm.Request
+
+
+-- | Fetch new radio content periodically in an XSPF format.
+--
+-- Optional: 'discovery', 'rtp', 'buyLinks'
+--
+-- <http://www.last.fm/api/show/radio.getPlaylist>
+getPlaylist ∷ Request f Sign (Multiplier → Bitrate → APIKey → SessionKey → Ready)
+getPlaylist = api "radio.getPlaylist"
+{-# INLINE getPlaylist #-}
+
+
+-- | Resolve the name of a resource into a station depending on which resource
+-- it is most likely to represent.
+--
+-- <http://www.last.fm/api/show/radio.search>
+search ∷ Request f Send (Name → APIKey → Ready)
+search = api "radio.search"
+{-# INLINE search #-}
+
+
+-- | Tune in to a Last.fm radio station.
+--
+-- Optional: 'language'
+--
+-- <http://www.last.fm/api/show/radio.tune>
+tune ∷ Request f Sign (Station → APIKey → SessionKey → Ready)
+tune = api "radio.tune" <* post
+{-# INLINE tune #-}
diff --git a/src/Network/Lastfm/Request.hs b/src/Network/Lastfm/Request.hs
new file mode 100644
--- /dev/null
+++ b/src/Network/Lastfm/Request.hs
@@ -0,0 +1,493 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE EmptyDataDecls #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE UnicodeSyntax #-}
+-- | Request construction
+module Network.Lastfm.Request
+  ( -- * Request
+    Request, R, Auth(..), Format(..)
+    -- * Request major parameters
+  , api, post, get, json, xml, Ready, APIKey, apiKey, SessionKey, sessionKey
+    -- * Request minor parameters
+  , Token, token, Callback, callback
+  , Artist, artist, artists, Album, album, MBID, mbid
+  , Country, country, Autocorrect, autocorrect
+  , Event, event, Status(..), status
+  , From, from, To, to, Group, group
+  , Language, language, Distance, distance
+  , Longitude, longitude, Latitude, latitude, Location, location
+  , Start, start, End, end, Festivals, festivalsonly
+  , StartTimestamp, startTimestamp, EndTimestamp, endTimestamp
+  , Metro, metro, Tag, tags, tag, Track, track, Timestamp , timestamp, Fingerprint, fingerprint
+  , AlbumArtist, albumArtist, Duration, duration, TrackNumber, trackNumber
+  , Playlist, playlist, Title, title, Description, description
+  , ChosenByUser, chosenByUser, Context, context, StreamId, streamId
+  , RecentTracks, recentTracks
+  , Recipient, recipient, Username, username, User, user, Password, password
+  , Public, public, Message, message, Page, page, Limit, limit
+  , TaggingType, taggingType, UseRecs, useRecs, Venue, venue, VenueName, venueName
+  , Discovery, discovery, RTP, rtp, BuyLinks, buyLinks, Multiplier(..), multiplier
+  , Bitrate(..), bitrate, Name, name, Station, station
+  , Targeted, comparison
+  ) where
+
+import Control.Applicative
+import Data.Int (Int64)
+import Data.Monoid ((<>), mempty)
+
+import qualified Data.Map.Strict as M
+import           Data.Text (Text)
+import qualified Data.Text as T
+
+import Network.Lastfm.Internal
+
+
+class Argument a where
+  add ∷ Text → a → Request f b t
+  add k v = wrap $ \r@R { _query = q } → r { _query = M.insert k (toText v) q }
+  {-# INLINE add #-}
+
+  toText ∷ a → Text
+
+instance Argument Text where
+  toText = id
+  {-# INLINE toText #-}
+
+instance Argument Bool where
+  toText b = if b then "1" else "0"
+  {-# INLINE toText #-}
+
+instance Argument Int64 where
+  toText = T.pack . show
+  {-# INLINE toText #-}
+
+instance Argument a ⇒ Argument [a] where
+  toText = T.intercalate "," . map toText
+  {-# INLINE toText #-}
+
+
+data APIKey
+data SessionKey
+data Token
+data Callback
+
+
+-- | Change request API method
+--
+-- Primarily used in API call wrappers, not intended for usage by library user
+api ∷ Text → Request f a t
+api = add "method"
+{-# INLINE api #-}
+
+-- | Change html _method to GET
+--
+-- Primarily used in API call wrappers, not intended for usage by library user
+get ∷ Request f a t
+get = wrap $ \r → r { _method = "GET" }
+{-# INLINE get #-}
+
+-- | Change html _method to POST
+--
+-- Primarily used in API call wrappers, not intended for usage by library user
+post ∷ Request f a t
+post = wrap $ \r → r { _method = "POST" }
+{-# INLINE post #-}
+
+-- | Change API response format to JSON
+--
+-- This is a little helper. It's actually enough
+-- to specialize Format manually
+json ∷ Request JSON a t
+json = wrap id
+{-# INLINE json #-}
+
+-- | Change API response format to XML
+--
+-- This is a little helper. It's actually enough
+-- to specialize Format manually
+xml ∷ Request XML a t
+xml = wrap id
+{-# INLINE xml #-}
+
+-- | Change request API key
+apiKey ∷ Text → Request f a APIKey
+apiKey = add "api_key"
+{-# INLINE apiKey #-}
+
+-- | Change request session key
+sessionKey ∷ Text → Request f Sign SessionKey
+sessionKey = add "sk"
+{-# INLINE sessionKey #-}
+
+-- | Add token parameter
+token ∷ Text → Request f a Token
+token = add "token"
+{-# INLINE token #-}
+
+-- | Add callback link parameter
+callback ∷ Text → Request f a Callback
+callback = add "cb"
+{-# INLINE callback #-}
+
+
+data Artist
+data Album
+data MBID
+data Country
+data Language
+data Tag
+data Autocorrect
+data Page
+data Limit
+data Message
+data Public
+data Recipient
+data Username
+data User
+data Password
+data Status = Attending | Maybe | NotAttending
+data Event
+data Festivals
+data Longitude
+data Latitude
+data Location
+data Distance
+data Metro
+data Start
+data End
+data StartTimestamp
+data EndTimestamp
+data From
+data To
+data Playlist
+data Title
+data Description
+data Track
+data Timestamp
+data Fingerprint
+data AlbumArtist
+data Context
+data StreamId
+data Duration
+data TrackNumber
+data ChosenByUser
+data TaggingType
+data RecentTracks
+data UseRecs
+data Group
+data Venue
+data VenueName
+data Multiplier = M1 | M2
+data Bitrate = B64 | B128
+data Name
+data Station
+data Discovery
+data RTP
+data BuyLinks
+
+
+-- | Add artist parameter
+artist ∷ Text → Request f a Artist
+artist = add "artist"
+{-# INLINE artist #-}
+
+-- | Add artist parameter
+artists ∷ [Text] → Request f a [Artist]
+artists = add "artists"
+{-# INLINE artists #-}
+
+-- | Add artist parameter
+album ∷ Text → Request f a Album
+album = add "album"
+{-# INLINE album #-}
+
+-- | Add MBID parameter
+mbid ∷ Text → Request f a MBID
+mbid = add "mbid"
+{-# INLINE mbid #-}
+
+-- | Add country parameter
+country ∷ Text → Request f a Country
+country = add "country"
+{-# INLINE country #-}
+
+-- | Add language parameter
+language ∷ Text → Request f a Language
+language = add "lang"
+{-# INLINE language #-}
+
+-- | Add tags parameter
+tags ∷ [Text] → Request f a [Tag]
+tags = add "tags"
+{-# INLINE tags #-}
+
+-- | Add tag parameter
+tag ∷ Text → Request f a Tag
+tag = add "tag"
+{-# INLINE tag #-}
+
+-- | Add autocorrect parameter
+autocorrect ∷ Bool → Request f a Autocorrect
+autocorrect = add "tags"
+{-# INLINE autocorrect #-}
+
+-- | Add page parameter
+page ∷ Int64 → Request f a Page
+page = add "page"
+{-# INLINE page #-}
+
+-- | Add limit parameter
+limit ∷ Int64 → Request f a Limit
+limit = add "limit"
+{-# INLINE limit #-}
+
+-- | Add message parameter
+message ∷ Text → Request f a Message
+message = add "message"
+{-# INLINE message #-}
+
+-- | Add public parameter
+public ∷ Bool → Request f a Public
+public = add "public"
+{-# INLINE public #-}
+
+-- | Add recipient parameter
+recipient ∷ Text → Request f a Recipient
+recipient = add "recipient"
+{-# INLINE recipient #-}
+
+-- | Add username parameter
+username ∷ Text → Request f a Username
+username = add "username"
+{-# INLINE username #-}
+
+-- | Add user parameter
+user ∷ Text → Request f a User
+user = add "user"
+{-# INLINE user #-}
+
+-- | Add password parameter
+password ∷ Text → Request f a Password
+password = add "password"
+{-# INLINE password #-}
+
+-- | Add status parameter
+status ∷ Status → Request f a Status
+status = add "status" . T.pack . \s -> case s of
+  Attending → "0"
+  Maybe     → "1"
+  _         → "2"
+{-# INLINE status #-}
+
+-- | Add event parameter
+event ∷ Int64 → Request f a Event
+event = add "event"
+{-# INLINE event #-}
+
+-- | Add festivalsonly parameter
+festivalsonly ∷ Bool → Request f a Festivals
+festivalsonly = add "festivalsonly"
+{-# INLINE festivalsonly #-}
+
+-- | Add longitude parameter
+longitude ∷ Text → Request f a Longitude
+longitude = add "longitude"
+{-# INLINE longitude #-}
+
+-- | Add latitude parameter
+latitude ∷ Text → Request f a Latitude
+latitude = add "latitude"
+{-# INLINE latitude #-}
+
+-- | Add location parameter
+location ∷ Text → Request f a Location
+location = add "location"
+{-# INLINE location #-}
+
+-- | Add distance parameter
+distance ∷ Int64 → Request f a Distance
+distance = add "distance"
+{-# INLINE distance #-}
+
+-- | Add venue parameter
+venue ∷ Int64 → Request f a Venue
+venue = add "venue"
+{-# INLINE venue #-}
+
+-- | Add venue parameter
+venueName ∷ Text → Request f a VenueName
+venueName = add "venue"
+{-# INLINE venueName #-}
+
+-- | Add metro parameter
+metro ∷ Text → Request f a Metro
+metro = add "metro"
+{-# INLINE metro #-}
+
+-- | Add start parameter
+start ∷ Int64 → Request f a Start
+start = add "start"
+{-# INLINE start #-}
+
+-- | Add end parameter
+end ∷ Int64 → Request f a End
+end = add "end"
+{-# INLINE end #-}
+
+-- | Add startTimestamp parameter
+startTimestamp ∷ Int64 → Request f a StartTimestamp
+startTimestamp = add "startTimestamp"
+{-# INLINE startTimestamp #-}
+
+-- | Add endTimestamp parameter
+endTimestamp ∷ Int64 → Request f a EndTimestamp
+endTimestamp = add "endTimestamp"
+{-# INLINE endTimestamp #-}
+
+-- | Add from parameter
+from ∷ Int64 → Request f a From
+from = add "from"
+{-# INLINE from #-}
+
+-- | Add to parameter
+to ∷ Int64 → Request f a To
+to = add "to"
+{-# INLINE to #-}
+
+-- | Add track parameter
+track ∷ Text → Request f a Track
+track = add "track"
+{-# INLINE track #-}
+
+-- | Add timestamp parameter
+timestamp ∷ Int64 → Request f a Timestamp
+timestamp = add "timestamp"
+{-# INLINE timestamp #-}
+
+-- | Add playlistID parameter
+playlist ∷ Int64 → Request f a Playlist
+playlist = add "playlistID"
+{-# INLINE playlist #-}
+
+-- | Add title parameter
+title ∷ Text → Request f a Title
+title = add "title"
+{-# INLINE title #-}
+
+-- | Add description parameter
+description ∷ Text → Request f a Description
+description = add "description"
+{-# INLINE description #-}
+
+-- | Add fingerprint parameter
+fingerprint ∷ Int64 → Request f a Fingerprint
+fingerprint = add "fingerprintid"
+{-# INLINE fingerprint #-}
+
+-- | Add albumArtist parameter
+albumArtist ∷ Text → Request f a AlbumArtist
+albumArtist = add "albumArtist"
+{-# INLINE albumArtist #-}
+
+-- | Add context parameter
+context ∷ Text → Request f a Context
+context = add "context"
+{-# INLINE context #-}
+
+-- | Add streamId parameter
+streamId ∷ Int64 → Request f a StreamId
+streamId = add "streamId"
+{-# INLINE streamId #-}
+
+-- | Add duration parameter
+duration ∷ Int64 → Request f a Duration
+duration = add "duration"
+{-# INLINE duration #-}
+
+-- | Add trackNumber parameter
+trackNumber ∷ Int64 → Request f a TrackNumber
+trackNumber = add "trackNumber"
+{-# INLINE trackNumber #-}
+
+-- | Add chosenByUser parameter
+chosenByUser ∷ Bool → Request f a ChosenByUser
+chosenByUser = add "chosenByUser"
+{-# INLINE chosenByUser #-}
+
+-- | Add taggingType parameter
+taggingType ∷ Text → Request f a TaggingType
+taggingType = add "taggingtype"
+{-# INLINE taggingType #-}
+
+-- | Add recentTracks parameter
+recentTracks ∷ Bool → Request f a RecentTracks
+recentTracks = add "recentTracks"
+{-# INLINE recentTracks #-}
+
+-- | Add useRecs parameter
+useRecs ∷ Bool → Request f a UseRecs
+useRecs = add "useRecs"
+{-# INLINE useRecs #-}
+
+-- | Add group parameter
+group ∷ Text → Request f a Group
+group = add "group"
+{-# INLINE group #-}
+
+-- | Add multiplier parameter
+multiplier ∷ Multiplier → Request f a Multiplier
+multiplier m = case m of
+  M1 → add "speed_multiplier" (T.pack "1.0")
+  M2 → add "speed_multiplier" (T.pack "2.0")
+{-# INLINE multiplier #-}
+
+-- | Add bitrate parameter
+bitrate ∷ Bitrate → Request f a Bitrate
+bitrate b = case b of
+  B64  → add "bitrate" (64 ∷ Int64)
+  B128 → add "bitrate" (128 ∷ Int64)
+{-# INLINE bitrate #-}
+
+-- | Add name parameter
+name ∷ Text → Request f a Name
+name = add "name"
+{-# INLINE name #-}
+
+-- | Add station parameter
+station ∷ Text → Request f a Station
+station = add "station"
+{-# INLINE station #-}
+
+-- | Add group parameter
+discovery ∷ Bool → Request f a Discovery
+discovery = add "discovery"
+{-# INLINE discovery #-}
+
+-- | Add rtp parameter
+rtp ∷ Bool → Request f a RTP
+rtp = add "rtp"
+{-# INLINE rtp #-}
+
+-- | Add buyLinks parameter
+buyLinks ∷ Bool → Request f a BuyLinks
+buyLinks = add "buyLinks"
+{-# INLINE buyLinks #-}
+
+
+class Targeted t where
+  target ∷ Request f a t → Text
+
+instance Targeted [Artist] where
+  target _ = "artists"
+  {-# INLINE target #-}
+
+instance Targeted User where
+  target _ = "user"
+  {-# INLINE target #-}
+
+-- | Add comparison parameter
+comparison ∷ Targeted t ⇒ Int64 → Request f a t → Request f a t
+comparison n t = let z = target t in
+  add ("type" <> toText n) z <*> add ("value" <> toText n) (_query (unwrap t (R mempty mempty mempty)) M.! z)
+{-# INLINE comparison #-}
diff --git a/src/Network/Lastfm/Response.hs b/src/Network/Lastfm/Response.hs
new file mode 100644
--- /dev/null
+++ b/src/Network/Lastfm/Response.hs
@@ -0,0 +1,114 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE UnicodeSyntax #-}
+-- | Request sending and Response parsing
+module Network.Lastfm.Response
+  ( -- * Sign Request
+    -- $sign
+    Secret(..), sign
+    -- * Get Response
+  , Response, lastfm, lastfm', finalize
+  ) where
+
+import Control.Applicative
+import Control.Exception (throw)
+import Control.Monad
+import Data.Monoid
+import Data.String (IsString)
+
+import           Crypto.Classes (hash')
+import           Data.Aeson ((.:), Value, decode, parseJSON)
+import           Data.Aeson.Types (parseMaybe)
+import qualified Data.ByteString.Lazy as Lazy
+import qualified Data.ByteString as Strict
+import           Data.Digest.Pure.MD5 (MD5Digest)
+import qualified Data.Map as M
+import           Data.Text (Text)
+import qualified Data.Text as T
+import qualified Data.Text.Encoding as T
+import qualified Network.HTTP.Conduit as C
+import qualified Network.HTTP.Types as C
+
+import Network.Lastfm.Internal
+
+
+-- $sign
+--
+-- Signing is important part of every
+-- authentication requiring API request.
+-- Basically, every such request is appended
+-- with md5 footprint of its arguments as
+-- described at <http://www.last.fm/api/authspec#8>
+
+
+class Supported (f ∷ Format) where
+  type Response f
+  parse ∷ R f a t → Lazy.ByteString → C.ResponseHeaders → Response f
+  base ∷ R f a t
+
+
+instance Supported JSON where
+  type Response JSON = Maybe Value
+  parse _ b hs = do
+    v ← decode b
+    case parseMaybe ((.: "error") <=< parseJSON) v of
+      Just (_ ∷ Int) →
+        throw (C.StatusCodeException C.status400 (("Response", Strict.concat $ Lazy.toChunks b) : hs))
+      _ → return v
+  base = R
+    { _host = "https://ws.audioscrobbler.com/2.0/"
+    , _method = "GET"
+    , _query = M.fromList [("format", "json")]
+    }
+  {-# INLINE base #-}
+
+instance Supported XML where
+  type Response XML = Lazy.ByteString
+  parse _ b _ = b
+  base = R
+    { _host = "https://ws.audioscrobbler.com/2.0/"
+    , _method = "GET"
+    , _query = mempty
+    }
+  {-# INLINE base #-}
+
+
+-- | Application secret
+newtype Secret = Secret Text deriving (Show, IsString)
+
+
+-- | Sign 'Request' with 'Secret'
+sign ∷ Secret → Request f Sign Ready → Request f Send Ready
+sign (Secret s) = coerce . (<* signature)
+ where
+  signature = wrap $ \r@R { _query = q } →
+    r { _query = M.insert "api_sig" (signer (foldr M.delete q ["format", "callback"])) q }
+
+  signer = T.pack . show . (hash' :: Strict.ByteString -> MD5Digest) .
+    T.encodeUtf8 . M.foldrWithKey(\k v xs → k <> v <> xs) s
+
+
+-- | Send Request and parse Response
+lastfm ∷ Supported f ⇒ Request f Send Ready → IO (Response f)
+lastfm = lastfm' . finalize
+
+
+-- | Get R from Request
+--
+-- That's rarely needed unless you want low-level requests manipulation
+finalize ∷ Supported f ⇒ Request f Send Ready → R f Send Ready
+finalize = ($ base) . unwrap
+
+
+-- | Send R and parse Response
+--
+-- That's rarely needed unless you want low-level requests manipulation
+lastfm' ∷ Supported f ⇒ R f Send Ready → IO (Response f)
+lastfm' request =
+  C.withManager $ \m → C.parseUrl (render request) >>= \url → do
+    t ← C.httpLbs (url { C.method = _method request, C.responseTimeout = Just 10000000 }) m
+    return $ parse request (C.responseBody t) (C.responseHeaders t)
diff --git a/src/Network/Lastfm/TH.hs b/src/Network/Lastfm/TH.hs
deleted file mode 100644
--- a/src/Network/Lastfm/TH.hs
+++ /dev/null
@@ -1,24 +0,0 @@
-{-# LANGUAGE TemplateHaskell #-}
-{-# LANGUAGE ViewPatterns #-}
-module Network.Lastfm.TH
-  ( instances, newtypes
-  ) where
-
-import Language.Haskell.TH
-
-
-instances ∷ String → [(String, String)] → Q [Dec]
-instances f = mapM (instanceDeclaration "Argument")
-  where
-   instanceDeclaration (mkName → tc) (mkName → n, m) = instanceD (cxt []) (appT (conT tc) (conT n)) [first, second]
-    where
-     first = funD (mkName "key") [clause [] (normalB [e| const m |]) []]
-     second = let var = mkName "a"
-                  func = mkName f
-              in funD (mkName "value") [clause [conP n [varP var]] (normalB $ appE (varE func) (varE var)) []]
-
-
-newtypes ∷ String → [String] → Q [Dec]
-newtypes (mkName → t) (map mkName → ns) = mapM newtypeDeclaration ns
-  where
-   newtypeDeclaration n = newtypeD (cxt []) n [] (normalC n [strictType notStrict (conT t)]) [mkName "Show"]
diff --git a/src/Network/Lastfm/Tag.hs b/src/Network/Lastfm/Tag.hs
new file mode 100644
--- /dev/null
+++ b/src/Network/Lastfm/Tag.hs
@@ -0,0 +1,102 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE UnicodeSyntax #-}
+-- | Lastfm tag API
+--
+-- This module is intended to be imported qualified:
+--
+-- @
+-- import qualified Network.Lastfm.Tag as Tag
+-- @
+module Network.Lastfm.Tag
+  ( getInfo, getSimilar, getTopAlbums, getTopArtists, getTopTags, getTopTracks
+  , getWeeklyArtistChart, getWeeklyChartList, search
+  ) where
+
+import Network.Lastfm.Request
+
+
+-- | Get the metadata for a tag
+--
+-- Optional: language
+--
+-- <http://www.last.fm/api/show/tag.getInfo>
+getInfo ∷ Request f Send (Tag → APIKey → Ready)
+getInfo = api "tag.getInfo"
+{-# INLINE getInfo #-}
+
+
+-- | Search for tags similar to this one. Returns tags ranked by similarity, based on listening data.
+--
+-- <http://www.last.fm/api/show/tag.getSimilar>
+getSimilar ∷ Request f Send (Tag → APIKey → Ready)
+getSimilar = api "tag.getSimilar"
+{-# INLINE getSimilar #-}
+
+
+-- | Get the top albums tagged by this tag, ordered by tag count.
+--
+-- Optional: 'limit', 'page'
+--
+-- <http://www.last.fm/api/show/tag.getTopAlbums>
+getTopAlbums ∷ Request f Send (Tag → APIKey → Ready)
+getTopAlbums = api "tag.getTopAlbums"
+{-# INLINE getTopAlbums #-}
+
+
+-- | Get the top artists tagged by this tag, ordered by tag count.
+--
+-- Optional: 'limit', 'page'
+--
+-- <http://www.last.fm/api/show/tag.getTopArtists>
+getTopArtists ∷ Request f Send (Tag → APIKey → Ready)
+getTopArtists = api "tag.getTopArtists"
+{-# INLINE getTopArtists #-}
+
+
+-- | Fetches the top global tags on Last.fm, sorted by popularity (number of times used)
+--
+-- <http://www.last.fm/api/show/tag.getTopTags>
+getTopTags ∷ Request f Send (APIKey → Ready)
+getTopTags = api "tag.getTopTags"
+{-# INLINE getTopTags #-}
+
+
+-- | Get the top tracks tagged by this tag, ordered by tag count.
+--
+-- Optional: 'limit', 'page'
+--
+-- <http://www.last.fm/api/show/tag.getTopTracks>
+getTopTracks ∷ Request f Send (Tag → APIKey → Ready)
+getTopTracks = api "tag.getTopTracks"
+{-# INLINE getTopTracks #-}
+
+
+-- | Get an artist chart for a tag, for a given date range.
+-- If no date range is supplied, it will return the most recent artist chart for this tag.
+--
+-- Optional: 'from', 'to', 'limit'
+--
+-- <http://www.last.fm/api/show/tag.getWeeklyArtistChart>
+getWeeklyArtistChart ∷ Request f Send (Tag → APIKey → Ready)
+getWeeklyArtistChart = api "tag.getWeeklyArtistChart"
+{-# INLINE getWeeklyArtistChart #-}
+
+
+-- | Get a list of available charts for this tag, expressed as
+-- date ranges which can be sent to the chart services.
+--
+-- <http://www.last.fm/api/show/tag.getWeeklyChartList>
+getWeeklyChartList ∷ Request f Send (Tag → APIKey → Ready)
+getWeeklyChartList = api "tag.getWeeklyChartList"
+{-# INLINE getWeeklyChartList #-}
+
+
+-- | Search for a tag by name. Returns matches sorted by relevance.
+--
+-- Optional: 'limit', 'page'
+--
+-- <http://www.last.fm/api/show/tag.search>
+search ∷ Request f Send (Tag → APIKey → Ready)
+search = api "tag.search"
+{-# INLINE search #-}
diff --git a/src/Network/Lastfm/Tasteometer.hs b/src/Network/Lastfm/Tasteometer.hs
new file mode 100644
--- /dev/null
+++ b/src/Network/Lastfm/Tasteometer.hs
@@ -0,0 +1,26 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE UnicodeSyntax #-}
+-- | Lastfm tasteometer API
+--
+-- This module is intended to be imported qualified:
+--
+-- @
+-- import qualified Network.Lastfm.Tasteometer as Tasteometer
+-- @
+module Network.Lastfm.Tasteometer where
+
+import Control.Applicative
+
+import Network.Lastfm.Request
+
+
+-- | Get a Tasteometer score from two inputs, along with a list of shared artists.
+-- If the input is a user some additional information is returned.
+--
+-- Optional: 'limit'
+--
+-- <http://www.lastfm.ru/api/show/tasteometer.compare>
+compare ∷ (Targeted u, Targeted v) ⇒ Request f Send u → Request f Send v → Request f Send (APIKey → Ready)
+compare u v = api "tasteometer.compare" <* comparison 1 u <* comparison 2 v
+{-# INLINE compare #-}
diff --git a/src/Network/Lastfm/Track.hs b/src/Network/Lastfm/Track.hs
new file mode 100644
--- /dev/null
+++ b/src/Network/Lastfm/Track.hs
@@ -0,0 +1,209 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE UnicodeSyntax #-}
+-- | Lastfm track API
+--
+-- This module is intended to be imported qualified:
+--
+-- @
+-- import qualified Network.Lastfm.Track as Track
+-- @
+module Network.Lastfm.Track
+  ( ArtistTrackOrMBID
+  , addTags, ban, getBuyLinks, getCorrection, getFingerprintMetadata
+  , getInfo, getShouts, getSimilar, getTags, getTopFans
+  , getTopTags, love, removeTag, scrobble
+  , search, share, unban, unlove, updateNowPlaying
+  ) where
+
+import Control.Applicative
+
+import Network.Lastfm.Request
+
+
+-- | Unify ('Artist' → 'Track' → …) and ('MBID' → …)
+class ArtistTrackOrMBID a
+
+instance ArtistTrackOrMBID (MBID → APIKey → Ready)
+instance ArtistTrackOrMBID (Artist → Track → APIKey → Ready)
+
+
+-- | Tag a track using a list of user supplied tags.
+--
+-- <http://www.last.fm/api/show/track.addTags>
+addTags ∷ Request f Sign (Artist → Track → [Tag] → APIKey → SessionKey → Ready)
+addTags = api "track.addTags" <* post
+{-# INLINE addTags #-}
+
+
+-- | Ban a track for a given user profile.
+--
+-- <http://www.last.fm/api/show/track.ban>
+ban ∷ Request f Sign (Artist → Track → APIKey → SessionKey → Ready)
+ban = api "track.ban" <* post
+{-# INLINE ban #-}
+
+
+-- | Get a list of Buy Links for a particular track.
+--
+-- Optional: 'autocorrect'
+--
+-- <http://www.last.fm/api/show/track.getBuylinks>
+getBuyLinks ∷ ArtistTrackOrMBID t ⇒ Request f Send (Country → t)
+getBuyLinks = api "track.getBuyLinks"
+{-# INLINE getBuyLinks #-}
+
+
+-- | Use the last.fm corrections data to check whether
+-- the supplied track has a correction to a canonical track.
+--
+-- <http://www.last.fm/api/show/track.getCorrection>
+getCorrection ∷ Request f Send (Artist → Track → APIKey → Ready)
+getCorrection = api "track.getCorrection"
+{-# INLINE getCorrection #-}
+
+
+-- | Retrieve track metadata associated with a fingerprint id
+-- generated by the Last.fm Fingerprinter. Returns track
+-- elements, along with a 'rank' value between 0 and 1 reflecting the confidence for each match.
+--
+-- <http://www.last.fm/api/show/track.getFingerprintMetadata>
+getFingerprintMetadata ∷ Request f Send (Fingerprint → APIKey → Ready)
+getFingerprintMetadata = api "track.getFingerprintMetadata"
+{-# INLINE getFingerprintMetadata #-}
+
+
+-- | Get the metadata for a track on Last.fm.
+--
+-- Optional: 'autocorrect', 'username'
+--
+-- <http://www.last.fm/api/show/track.getInfo>
+getInfo ∷ ArtistTrackOrMBID t ⇒ Request f Send t
+getInfo = api "track.getInfo"
+{-# INLINE getInfo #-}
+
+
+-- | Get shouts for this track. Also available as an rss feed.
+--
+-- Optional: 'autocorrect', 'limit', 'page'
+--
+-- <http://www.last.fm/api/show/track.getShouts>
+getShouts ∷ ArtistTrackOrMBID t ⇒ Request f Send t
+getShouts = api "track.getShouts"
+{-# INLINE getShouts #-}
+
+
+-- | Get the similar tracks for this track on Last.fm, based on listening data.
+--
+-- Optional: 'autocorrect', 'limit'
+--
+-- <http://www.last.fm/api/show/track.getSimilar>
+getSimilar ∷ ArtistTrackOrMBID t ⇒ Request f Send t
+getSimilar = api "track.getSimilar"
+{-# INLINE getSimilar #-}
+
+
+-- | Get the tags applied by an individual user to a track on Last.fm.
+--
+-- Optional: 'autocorrect', 'user'
+--
+-- <http://www.last.fm/api/show/track.getTags>
+getTags ∷ ArtistTrackOrMBID t ⇒ Request f Send t
+getTags = api "track.getTags"
+{-# INLINE getTags #-}
+
+
+-- | Get the top fans for this track on Last.fm, based on listening data.
+--
+-- Optional: 'autocorrect'
+--
+-- <http://www.last.fm/api/show/track.getTopFans>
+getTopFans ∷ ArtistTrackOrMBID t ⇒ Request f Send t
+getTopFans = api "track.getTopFans"
+{-# INLINE getTopFans #-}
+
+
+-- | Get the top tags for this track on Last.fm, ordered by tag count.
+--
+-- Optional: 'autocorrect'
+--
+-- <http://www.last.fm/api/show/track.getTopTags>
+getTopTags ∷ ArtistTrackOrMBID t ⇒ Request f Send t
+getTopTags = api "track.getTopTags"
+{-# INLINE getTopTags #-}
+
+
+-- | Love a track for a user profile.
+--
+-- <http://www.last.fm/api/show/track.love>
+love ∷ Request f Sign (Artist → Track → APIKey → SessionKey → Ready)
+love = api "track.love" <* post
+{-# INLINE love #-}
+
+
+-- | Remove a user's tag from a track.
+--
+-- <http://www.last.fm/api/show/track.removeTag>
+removeTag ∷ Request f Sign (Artist → Track → Tag → APIKey → SessionKey → Ready)
+removeTag = api "track.removeTag" <* post
+{-# INLINE removeTag #-}
+
+
+-- | Used to add a track-play to a user's profile.
+--
+-- Optional: 'album', 'albumArtist', 'chosenByUser', 'context',
+-- 'duration', 'mbid', 'streamId', 'trackNumber'
+--
+-- <http://www.last.fm/api/show/track.scrobble>
+scrobble ∷ Request f Sign (Artist → Track → Timestamp → APIKey → SessionKey → Ready)
+scrobble = api "track.scrobble" <* post
+{-# INLINE scrobble #-}
+
+
+-- | Search for a track by track name. Returns track matches sorted by relevance.
+--
+-- Optional: 'artist', 'limit', 'page'
+--
+-- <http://www.last.fm/api/show/track.search>
+search ∷ Request f Send (Track → APIKey → Ready)
+search = api "track.search"
+{-# INLINE search #-}
+
+
+-- | Share a track twith one or more Last.fm users or other friends.
+--
+-- Optional: 'public', 'message', 'recipient'
+--
+-- <http://www.last.fm/api/show/track.share>
+share ∷ Request f Sign (Artist → Track → Recipient → APIKey → SessionKey → Ready)
+share = api "track.share" <* post
+{-# INLINE share #-}
+
+
+-- | Unban a track for a user profile.
+--
+-- <http://www.last.fm/api/show/track.unban>
+unban ∷ Request f Sign (Artist → Track → APIKey → SessionKey → Ready)
+unban = api "track.unban" <* post
+{-# INLINE unban #-}
+
+
+-- | Unlove a track for a user profile.
+--
+-- <http://www.last.fm/api/show/track.unlove>
+unlove ∷ Request f Sign (Artist → Track → APIKey → SessionKey → Ready)
+unlove = api "track.unlove" <* post
+{-# INLINE unlove #-}
+
+
+-- | Used to notify Last.fm that a user has started listening
+-- to a track. Parameter names are case sensitive.
+--
+-- Optional: 'album', 'albumArtist', 'context',
+-- 'duration', 'mbid', 'trackNumber'
+--
+-- <http://www.last.fm/api/show/track.updateNowPlaying>
+updateNowPlaying ∷ Request f Sign (Artist → Track → APIKey → SessionKey → Ready)
+updateNowPlaying = api "track.updateNowPlaying" <* post
+{-# INLINE updateNowPlaying #-}
diff --git a/src/Network/Lastfm/User.hs b/src/Network/Lastfm/User.hs
new file mode 100644
--- /dev/null
+++ b/src/Network/Lastfm/User.hs
@@ -0,0 +1,276 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE UnicodeSyntax #-}
+-- | Lastfm user API
+--
+-- This module is intended to be imported qualified:
+--
+-- @
+-- import qualified Network.Lastfm.User as User
+-- @
+module Network.Lastfm.User
+  ( getArtistTracks, getBannedTracks, getEvents, getFriends
+  , getInfo, getLovedTracks, getNeighbours, getNewReleases
+  , getPastEvents, getPersonalTags, getPlaylists, getRecentStations
+  , getRecentTracks, getRecommendedArtists, getRecommendedEvents
+  , getShouts, getTopAlbums, getTopArtists, getTopTags
+  , getTopTracks, getWeeklyAlbumChart, getWeeklyArtistChart
+  , getWeeklyChartList, getWeeklyTrackChart, shout
+  ) where
+
+import Control.Applicative
+
+import Network.Lastfm.Request
+
+
+-- | Get a list of tracks by a given artist scrobbled by this user
+-- , including scrobble time. Can be limited to specific timeranges, defaults to all time.
+--
+-- Optional: 'startTimestamp', 'page', 'endTimestamp'
+--
+-- <http://www.last.fm/api/show/user.getArtistTracks>
+getArtistTracks ∷ Request f Send (User → Artist → APIKey → Ready)
+getArtistTracks = api "user.getArtistTracks"
+{-# INLINE getArtistTracks #-}
+
+
+-- | Returns the tracks banned by the user
+--
+-- Optional: 'limit', 'page'
+--
+-- <http://www.last.fm/api/show/user.getBannedTracks>
+getBannedTracks ∷ Request f Send (User → APIKey → Ready)
+getBannedTracks = api "user.getBannedTracks"
+{-# INLINE getBannedTracks #-}
+
+
+-- | Get a list of upcoming events that this user is attending.
+-- Easily integratable into calendars, using the ical standard (see 'more formats' section below).
+--
+-- Optional: 'page', 'festivalsonly', 'limit'
+--
+-- <http://www.last.fm/api/show/user.getEvents>
+getEvents ∷ Request f Send (User → APIKey → Ready)
+getEvents = api "user.getEvents"
+{-# INLINE getEvents #-}
+
+
+-- | Get a list of the user's friends on Last.fm.
+--
+-- Optional: 'recenttracks', 'limit', 'page'
+--
+-- <http://www.last.fm/api/show/user.getFriends>
+getFriends ∷ Request f Send (User → APIKey → Ready)
+getFriends = api "user.getFriends"
+{-# INLINE getFriends #-}
+
+
+-- | Get information about a user profile.
+--
+-- <http://www.last.fm/api/show/user.getInfo>
+getInfo ∷ Request f Send (User → APIKey → Ready)
+getInfo = api "user.getInfo"
+{-# INLINE getInfo #-}
+
+
+-- | Get the last 50 tracks loved by a user.
+--
+-- Optional: 'limit', 'page'
+--
+-- <http://www.last.fm/api/show/user.getLovedTracks>
+getLovedTracks ∷ Request f Send (User → APIKey → Ready)
+getLovedTracks = api "user.getLovedTracks"
+{-# INLINE getLovedTracks #-}
+
+
+-- | Get a list of a user's neighbours on Last.fm.
+--
+-- Optional: 'limit'
+--
+-- <http://www.last.fm/api/show/user.getNeighbours>
+getNeighbours ∷ Request f Send (User → APIKey → Ready)
+getNeighbours = api "user.getNeighbours"
+{-# INLINE getNeighbours #-}
+
+
+-- | Gets a list of forthcoming releases based on a user's musical taste.
+--
+-- Optional: 'userecs'
+--
+-- <http://www.last.fm/api/show/user.getNewReleases>
+getNewReleases ∷ Request f Send (User → APIKey → Ready)
+getNewReleases = api "user.getNewReleases"
+{-# INLINE getNewReleases #-}
+
+
+-- | Get a paginated list of all events a user has attended in the past.
+--
+-- Optional: 'page', 'limit'
+--
+-- <http://www.last.fm/api/show/user.getPastEvents>
+getPastEvents ∷ Request f Send (User → APIKey → Ready)
+getPastEvents = api "user.getPastEvents"
+{-# INLINE getPastEvents #-}
+
+
+-- | Get the user's personal tags
+--
+-- Optional: 'taggingtype', 'limit', 'page'
+--
+-- <http://www.last.fm/api/show/user.getPersonalTags>
+getPersonalTags ∷ Request f Send (User → Tag → TaggingType → APIKey → Ready)
+getPersonalTags = api "user.getPersonalTags"
+{-# INLINE getPersonalTags #-}
+
+
+-- | Get a list of a user's playlists on Last.fm.
+--
+-- <http://www.last.fm/api/show/user.getPlaylists>
+getPlaylists ∷ Request f Send (User → APIKey → Ready)
+getPlaylists = api "user.getPlaylists"
+{-# INLINE getPlaylists #-}
+
+
+-- | Get a list of the recent Stations listened to by this user.
+--
+-- Optional: 'limit', 'page'
+--
+-- <http://www.last.fm/api/show/user.getRecentStations>
+getRecentStations ∷ Request f Sign (User → APIKey → SessionKey → Ready)
+getRecentStations = api "user.getRecentStations"
+{-# INLINE getRecentStations #-}
+
+
+-- | Get a list of the recent tracks listened to by this user.
+-- Also includes the currently playing track with the nowplaying="true"
+-- attribute if the user is currently listening.
+--
+-- Optional: 'limit', 'page', 'from', 'extended', 'to'
+--
+-- <http://www.last.fm/api/show/user.getRecentTracks>
+getRecentTracks ∷ Request f Send (User → APIKey → Ready)
+getRecentTracks = api "user.getRecentTracks"
+{-# INLINE getRecentTracks #-}
+
+
+-- | Get Last.fm artist recommendations for a user
+--
+-- Optional: 'page', 'limit'
+--
+-- <http://www.last.fm/api/show/user.getRecommendedArtists>
+getRecommendedArtists ∷ Request f Sign (APIKey → SessionKey → Ready)
+getRecommendedArtists = api "user.getRecommendedArtists"
+{-# INLINE getRecommendedArtists #-}
+
+
+-- | Get a paginated list of all events recommended to a user by Last.fm, based on their listening profile.
+--
+-- Optional: 'limit', 'page', 'latitude', 'longitude', 'festivalsonly', 'country'
+--
+-- <http://www.last.fm/api/show/user.getRecommendedEvents>
+getRecommendedEvents ∷ Request f Sign (APIKey → SessionKey → Ready)
+getRecommendedEvents = api "user.getRecommendedEvents"
+{-# INLINE getRecommendedEvents #-}
+
+
+-- | Get shouts for this user. Also available as an rss feed.
+--
+-- Optional: 'page', 'limit'
+--
+-- <http://www.last.fm/api/show/user.getShouts>
+getShouts ∷ Request f Send (User → APIKey → Ready)
+getShouts = api "user.getShouts"
+{-# INLINE getShouts #-}
+
+
+-- | Get the top albums listened to by a user.
+-- You can stipulate a time period. Sends the overall chart by default.
+--
+-- Optional: 'period', 'limit', 'page'
+--
+-- <http://www.last.fm/api/show/user.getTopAlbums>
+getTopAlbums ∷ Request f Send (User → APIKey → Ready)
+getTopAlbums = api "user.getTopAlbums"
+{-# INLINE getTopAlbums #-}
+
+
+-- | Get the top artists listened to by a user.
+-- You can stipulate a time period. Sends the overall chart by default.
+--
+-- Optional: 'period', 'limit', 'page'
+--
+-- <http://www.last.fm/api/show/user.getTopArtists>
+getTopArtists ∷ Request f Send (User → APIKey → Ready)
+getTopArtists = api "user.getTopArtists"
+{-# INLINE getTopArtists #-}
+
+
+-- | Get the top tags used by this user.
+--
+-- Optional: 'limit'
+--
+-- <http://www.last.fm/api/show/user.getTopTags>
+getTopTags ∷ Request f Send (User → APIKey → Ready)
+getTopTags = api "user.getTopTags"
+{-# INLINE getTopTags #-}
+
+
+-- | Get the top tracks listened to by a user.
+-- You can stipulate a time period. Sends the overall chart by default.
+--
+-- Optional: 'period', 'limit', 'page'
+--
+-- <http://www.last.fm/api/show/user.getTopTracks>
+getTopTracks ∷ Request f Send (User → APIKey → Ready)
+getTopTracks = api "user.getTopTracks"
+{-# INLINE getTopTracks #-}
+
+
+-- | Get an album chart for a user profile, for a given date range.
+-- If no date range is supplied, it will return the most recent album chart for this user.
+--
+-- Optional: 'from', 'to'
+--
+-- <http://www.last.fm/api/show/user.getWeeklyAlbumChart>
+getWeeklyAlbumChart ∷ Request f Send (User → APIKey → Ready)
+getWeeklyAlbumChart = api "user.getWeeklyAlbumChart"
+{-# INLINE getWeeklyAlbumChart #-}
+
+
+-- | Get an artist chart for a user profile, for a given date range.
+-- If no date range is supplied, it will return the most recent artist chart for this user.
+--
+-- Optional: 'from', 'to'
+--
+-- <http://www.last.fm/api/show/user.getWeeklyArtistChart>
+getWeeklyArtistChart ∷ Request f Send (User → APIKey → Ready)
+getWeeklyArtistChart = api "user.getWeeklyArtistChart"
+{-# INLINE getWeeklyArtistChart #-}
+
+
+-- | Get a list of available charts for this user, expressed as
+-- date ranges which can be sent to the chart services.
+--
+-- <http://www.last.fm/api/show/user.getWeeklyChartList>
+getWeeklyChartList ∷ Request f Send (User → APIKey → Ready)
+getWeeklyChartList = api "user.getWeeklyChartList"
+{-# INLINE getWeeklyChartList #-}
+
+
+-- | Get a track chart for a user profile, for a given date range.
+-- If no date range is supplied, it will return the most recent track chart for this user.
+--
+-- Optional: 'from', 'to'
+--
+-- <http://www.last.fm/api/show/user.getWeeklyTrackChart>
+getWeeklyTrackChart ∷ Request f Send (User → APIKey → Ready)
+getWeeklyTrackChart = api "user.getWeeklyTrackChart"
+{-# INLINE getWeeklyTrackChart #-}
+
+
+-- | Shout on this user's shoutbox
+--
+-- <http://www.last.fm/api/show/user.shout>
+shout ∷ Request f Sign (User → Message → APIKey → SessionKey → Ready)
+shout = api "user.shout" <* post
+{-# INLINE shout #-}
diff --git a/src/Network/Lastfm/Venue.hs b/src/Network/Lastfm/Venue.hs
new file mode 100644
--- /dev/null
+++ b/src/Network/Lastfm/Venue.hs
@@ -0,0 +1,42 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE UnicodeSyntax #-}
+-- | Lastfm venue API
+--
+-- This module is intended to be imported qualified:
+--
+-- @
+-- import qualified Network.Lastfm.Venue as Venue
+-- @
+module Network.Lastfm.Venue
+  ( getEvents, getPastEvents, search
+  ) where
+
+import Network.Lastfm.Request
+
+
+-- | Get a list of upcoming events at this venue.
+--
+-- Optional: 'festivalsonly'
+--
+-- <http://www.last.fm/api/show/venue.getEvents>
+getEvents ∷ Request f Send (Venue → APIKey → Ready)
+getEvents = api "venue.getEvents"
+
+
+-- | Get a paginated list of all the events held at this venue in the past.
+--
+-- Optional: 'festivalsonly', 'page', 'limit'
+--
+-- <http://www.last.fm/api/show/venue.getPastEvents>
+getPastEvents ∷ Request f Send (Venue → APIKey → Ready)
+getPastEvents = api "venue.getPastEvents"
+
+
+-- | Search for a venue by venue name
+--
+-- Optional: 'page', 'limit', 'country'
+--
+-- <http://www.last.fm/api/show/venue.search>
+search ∷ Request f Send (VenueName → APIKey → Ready)
+search = api "venue.search"
diff --git a/src/Network/Lastfm/XML.hs b/src/Network/Lastfm/XML.hs
deleted file mode 100644
--- a/src/Network/Lastfm/XML.hs
+++ /dev/null
@@ -1,32 +0,0 @@
-{-# LANGUAGE TemplateHaskell #-}
-module Network.Lastfm.XML
-  ( xml
-  , xmlWrapper
-  ) where
-
-import Control.Applicative ((<$>))
-import Control.Monad ((<=<))
-
-import Data.ByteString.Lazy.Char8 (ByteString)
-import Language.Haskell.TH
-import Text.XML.Light
-
-import Network.Lastfm.Error (LastfmError, disambiguate)
-import Network.Lastfm.Internal (Format(..))
-
-
-xml ∷ Format
-xml = Format
-  { errorParser = xmlErrorParser
-  , uriArgument = Nothing
-  }
-
-
-xmlWrapper ∷ [String] → Q [Dec]
-xmlWrapper = mapM func
-  where
-   func xs = funD (mkName xs) [clause [] (normalB $ appE (varE (mkName ("API." ++ xs))) [e| xml |]) []]
-
-
-xmlErrorParser ∷ ByteString → Maybe LastfmError
-xmlErrorParser r = disambiguate . read <$> (findAttr (unqual "code") <=< findChild (unqual "error") <=< parseXMLDoc) r
diff --git a/src/Network/Lastfm/XML/Album.hs b/src/Network/Lastfm/XML/Album.hs
deleted file mode 100644
--- a/src/Network/Lastfm/XML/Album.hs
+++ /dev/null
@@ -1,43 +0,0 @@
-{-# LANGUAGE CPP #-}
-{-# LANGUAGE TemplateHaskell #-}
--- | Album API module
-{-# OPTIONS_HADDOCK prune #-}
-module Network.Lastfm.XML.Album
-  ( addTags, getBuyLinks, getInfo, getShouts, getTags
-  , getTopTags, removeTag, search, share
-  ) where
-
-#include "album.docs"
-
-import Network.Lastfm.Internal
-import Network.Lastfm.XML (xmlWrapper)
-import qualified Network.Lastfm.API.Album as API
-
-$(xmlWrapper ["addTags", "getBuyLinks", "getInfo", "getShouts", "getTags", "getTopTags", "removeTag", "search", "share"])
-
-__addTags__
-addTags ∷ (Artist, Album) → [Tag] → APIKey → SessionKey → Secret → Lastfm Response
-
-__getBuyLinks__
-getBuyLinks ∷ Either (Artist, Album) Mbid → Maybe Autocorrect → Country → APIKey → Lastfm Response
-
-__getInfo__
-getInfo ∷ Either (Artist, Album) Mbid → Maybe Autocorrect → Maybe Language → Maybe Username → APIKey → Lastfm Response
-
-__getShouts__
-getShouts ∷ Either (Artist, Album) Mbid → Maybe Autocorrect → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__getTags__
-getTags ∷ Either (Artist, Album) Mbid → Maybe Autocorrect → Either User (SessionKey, Secret) → APIKey → Lastfm Response
-
-__getTopTags__
-getTopTags ∷ Either (Artist, Album) Mbid → Maybe Autocorrect → APIKey → Lastfm Response
-
-__removeTag__
-removeTag ∷ Artist → Album → Tag → APIKey → SessionKey → Secret → Lastfm Response
-
-__search__
-search ∷ Album → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__share__
-share ∷ Artist → Album → Recipient → Maybe Message → Maybe Public → APIKey → SessionKey → Secret → Lastfm Response
diff --git a/src/Network/Lastfm/XML/Artist.hs b/src/Network/Lastfm/XML/Artist.hs
deleted file mode 100644
--- a/src/Network/Lastfm/XML/Artist.hs
+++ /dev/null
@@ -1,71 +0,0 @@
-{-# LANGUAGE CPP #-}
-{-# LANGUAGE TemplateHaskell #-}
--- | Artist API module
-{-# OPTIONS_HADDOCK prune #-}
-module Network.Lastfm.XML.Artist
-  ( addTags, getCorrection, getEvents, getImages, getInfo
-  , getPastEvents, getPodcast, getShouts, getSimilar, getTags, getTopAlbums
-  , getTopFans, getTopTags, getTopTracks, removeTag, search, share, shout
-  ) where
-
-#include "artist.docs"
-
-import Network.Lastfm.Internal
-import Network.Lastfm.XML (xmlWrapper)
-import qualified Network.Lastfm.API.Artist as API
-
-$(xmlWrapper ["addTags", "getCorrection", "getEvents", "getImages", "getInfo", "getPastEvents", "getPodcast", "getShouts", "getSimilar", "getTags", "getTopAlbums", "getTopFans", "getTopTags", "getTopTracks", "removeTag", "search", "share", "shout"])
-
-__addTags__
-addTags ∷ Artist → [Tag] → APIKey → SessionKey → Secret → Lastfm Response
-
-__getCorrection__
-getCorrection ∷ Artist → APIKey → Lastfm Response
-
-__getEvents__
-getEvents ∷ Either Artist Mbid → Maybe Autocorrect → Maybe Page → Maybe Limit → Maybe FestivalsOnly → APIKey → Lastfm Response
-
-__getImages__
-getImages ∷ Either Artist Mbid → Maybe Autocorrect → Maybe Page → Maybe Limit → Maybe Order → APIKey → Lastfm Response
-
-__getInfo__
-getInfo ∷ Either Artist Mbid → Maybe Autocorrect → Maybe Language → Maybe Username → APIKey → Lastfm Response
-
-__getPastEvents__
-getPastEvents ∷ Either Artist Mbid → Maybe Autocorrect → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__getPodcast__
-getPodcast ∷ Either Artist Mbid → Maybe Autocorrect → APIKey → Lastfm Response
-
-__getShouts__
-getShouts ∷ Either Artist Mbid → Maybe Autocorrect → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__getSimilar__
-getSimilar ∷ Either Artist Mbid → Maybe Autocorrect → Maybe Limit → APIKey → Lastfm Response
-
-__getTags__
-getTags ∷ Either Artist Mbid → Maybe Autocorrect → Either User (SessionKey, Secret) → APIKey → Lastfm Response
-
-__getTopAlbums__
-getTopAlbums ∷ Either Artist Mbid → Maybe Autocorrect → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__getTopFans__
-getTopFans ∷ Either Artist Mbid → Maybe Autocorrect → APIKey → Lastfm Response
-
-__getTopTags__
-getTopTags ∷ Either Artist Mbid → Maybe Autocorrect → APIKey → Lastfm Response
-
-__getTopTracks__
-getTopTracks ∷ Either Artist Mbid → Maybe Autocorrect → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__removeTag__
-removeTag ∷ Artist → Tag → APIKey → SessionKey → Secret → Lastfm Response
-
-__search__
-search ∷ Artist → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__share__
-share ∷ Artist → Recipient → Maybe Message → Maybe Public → APIKey → SessionKey → Secret → Lastfm Response
-
-__shout__
-shout ∷ Artist → Message → APIKey → SessionKey → Secret → Lastfm Response
diff --git a/src/Network/Lastfm/XML/Auth.hs b/src/Network/Lastfm/XML/Auth.hs
deleted file mode 100644
--- a/src/Network/Lastfm/XML/Auth.hs
+++ /dev/null
@@ -1,7 +0,0 @@
--- | Auth API module
-{-# OPTIONS_HADDOCK prune #-}
-module Network.Lastfm.XML.Auth
-  ( module Network.Lastfm.API.Auth
-  ) where
-
-import Network.Lastfm.API.Auth
diff --git a/src/Network/Lastfm/XML/Chart.hs b/src/Network/Lastfm/XML/Chart.hs
deleted file mode 100644
--- a/src/Network/Lastfm/XML/Chart.hs
+++ /dev/null
@@ -1,34 +0,0 @@
-{-# LANGUAGE CPP #-}
-{-# LANGUAGE TemplateHaskell #-}
--- | Chart API module
-{-# OPTIONS_HADDOCK prune #-}
-module Network.Lastfm.XML.Chart
-  ( getHypedArtists, getHypedTracks, getLovedTracks
-  , getTopArtists, getTopTags, getTopTracks
-  ) where
-
-#include "chart.docs"
-
-import Network.Lastfm.Internal
-import Network.Lastfm.XML (xmlWrapper)
-import qualified Network.Lastfm.API.Chart as API
-
-$(xmlWrapper ["getHypedArtists", "getHypedTracks", "getLovedTracks", "getTopArtists", "getTopTags", "getTopTracks"])
-
-__getHypedArtists__
-getHypedArtists ∷ Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__getHypedTracks__
-getHypedTracks ∷ Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__getLovedTracks__
-getLovedTracks ∷ Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__getTopArtists__
-getTopArtists ∷ Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__getTopTags__
-getTopTags ∷ Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__getTopTracks__
-getTopTracks ∷ Maybe Page → Maybe Limit → APIKey → Lastfm Response
diff --git a/src/Network/Lastfm/XML/Event.hs b/src/Network/Lastfm/XML/Event.hs
deleted file mode 100644
--- a/src/Network/Lastfm/XML/Event.hs
+++ /dev/null
@@ -1,33 +0,0 @@
-{-# LANGUAGE CPP #-}
-{-# LANGUAGE TemplateHaskell #-}
--- | Event API module
-{-# OPTIONS_HADDOCK prune #-}
-module Network.Lastfm.XML.Event
-  ( attend, getAttendees, getInfo, getShouts, share, shout
-  ) where
-
-#include "event.docs"
-
-import Network.Lastfm.Internal
-import Network.Lastfm.XML (xmlWrapper)
-import qualified Network.Lastfm.API.Event as API
-
-$(xmlWrapper ["attend", "getAttendees", "getInfo", "getShouts", "share", "shout"])
-
-__attend__
-attend ∷ Event → Status → APIKey → SessionKey → Secret → Lastfm Response
-
-__getAttendees__
-getAttendees ∷ Event → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__getInfo__
-getInfo ∷ Event → APIKey → Lastfm Response
-
-__getShouts__
-getShouts ∷ Event → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__share__
-share ∷ Event → Recipient → Maybe Message → Maybe Public → APIKey → SessionKey → Secret → Lastfm Response
-
-__shout__
-shout ∷ Event → Message → APIKey → SessionKey → Secret → Lastfm Response
diff --git a/src/Network/Lastfm/XML/Geo.hs b/src/Network/Lastfm/XML/Geo.hs
deleted file mode 100644
--- a/src/Network/Lastfm/XML/Geo.hs
+++ /dev/null
@@ -1,57 +0,0 @@
-{-# LANGUAGE CPP #-}
-{-# LANGUAGE TemplateHaskell #-}
--- | Geo API module
-{-# OPTIONS_HADDOCK prune #-}
-module Network.Lastfm.XML.Geo
-  ( getEvents, getMetroArtistChart, getMetroHypeArtistChart, getMetroHypeTrackChart
-  , getMetroTrackChart, getMetroUniqueArtistChart, getMetroUniqueTrackChart
-  , getMetroWeeklyChartlist, getMetros, getTopArtists, getTopTracks
-  ) where
-
-#include "geo.docs"
-
-import Network.Lastfm.Internal
-import Network.Lastfm.XML (xmlWrapper)
-import qualified Network.Lastfm.API.Geo as API
-
-$(xmlWrapper ["getEvents", "getMetroArtistChart", "getMetroHypeArtistChart", "getMetroHypeTrackChart", "getMetroTrackChart", "getMetroUniqueArtistChart", "getMetroUniqueTrackChart", "getMetroWeeklyChartlist", "getMetros", "getTopArtists", "getTopTracks"])
-
-__getEvents__
-getEvents ∷ Maybe Latitude
-          → Maybe Longitude
-          → Maybe Location
-          → Maybe Distance
-          → Maybe Page
-          → Maybe Limit
-          → APIKey
-          → Lastfm Response
-
-__getMetroArtistChart__
-getMetroArtistChart ∷ Country → Metro → Maybe Start → Maybe End → APIKey → Lastfm Response
-
-__getMetroHypeArtistChart__
-getMetroHypeArtistChart ∷ Country → Metro → Maybe Start → Maybe End → APIKey → Lastfm Response
-
-__getMetroHypeTrackChart__
-getMetroHypeTrackChart ∷ Country → Metro → Maybe Start → Maybe End → APIKey → Lastfm Response
-
-__getMetroTrackChart__
-getMetroTrackChart ∷ Country → Metro → Maybe Start → Maybe End → APIKey → Lastfm Response
-
-__getMetroUniqueArtistChart__
-getMetroUniqueArtistChart ∷ Country → Metro → Maybe Start → Maybe End → APIKey → Lastfm Response
-
-__getMetroUniqueTrackChart__
-getMetroUniqueTrackChart ∷ Country → Metro → Maybe Start → Maybe End → APIKey → Lastfm Response
-
-__getMetroWeeklyChartlist__
-getMetroWeeklyChartlist ∷ Metro → APIKey → Lastfm Response
-
-__getMetros__
-getMetros ∷ Maybe Country → APIKey → Lastfm Response
-
-__getTopArtists__
-getTopArtists ∷ Country → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__getTopTracks__
-getTopTracks ∷ Country → Maybe Location → Maybe Page → Maybe Limit → APIKey → Lastfm Response
diff --git a/src/Network/Lastfm/XML/Group.hs b/src/Network/Lastfm/XML/Group.hs
deleted file mode 100644
--- a/src/Network/Lastfm/XML/Group.hs
+++ /dev/null
@@ -1,33 +0,0 @@
-{-# LANGUAGE CPP #-}
-{-# LANGUAGE TemplateHaskell #-}
--- | Group API module
-{-# OPTIONS_HADDOCK prune #-}
-module Network.Lastfm.XML.Group
-  ( getHype, getMembers, getWeeklyChartList, getWeeklyAlbumChart, getWeeklyArtistChart, getWeeklyTrackChart
-  ) where
-
-#include "group.docs"
-
-import Network.Lastfm.Internal
-import Network.Lastfm.XML (xmlWrapper)
-import qualified Network.Lastfm.API.Group as API
-
-$(xmlWrapper ["getHype", "getMembers", "getWeeklyChartList", "getWeeklyAlbumChart", "getWeeklyArtistChart", "getWeeklyTrackChart"])
-
-__getHype__
-getHype ∷ Group → APIKey → Lastfm Response
-
-__getMembers__
-getMembers ∷ Group → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__getWeeklyChartList__
-getWeeklyChartList ∷ Group → APIKey → Lastfm Response
-
-__getWeeklyAlbumChart__
-getWeeklyAlbumChart ∷ Group → Maybe From → Maybe To → APIKey → Lastfm Response
-
-__getWeeklyArtistChart__
-getWeeklyArtistChart ∷ Group → Maybe From → Maybe To → APIKey → Lastfm Response
-
-__getWeeklyTrackChart__
-getWeeklyTrackChart ∷ Group → Maybe From → Maybe To → APIKey → Lastfm Response
diff --git a/src/Network/Lastfm/XML/Library.hs b/src/Network/Lastfm/XML/Library.hs
deleted file mode 100644
--- a/src/Network/Lastfm/XML/Library.hs
+++ /dev/null
@@ -1,46 +0,0 @@
-{-# LANGUAGE CPP #-}
-{-# LANGUAGE TemplateHaskell #-}
--- | Library API module
-{-# OPTIONS_HADDOCK prune #-}
-module Network.Lastfm.XML.Library
-  ( addAlbum, addArtist, addTrack, getAlbums, getArtists, getTracks
-  , removeAlbum, removeArtist, removeScrobble, removeTrack
-  ) where
-
-#include "library.docs"
-
-import Network.Lastfm.Internal
-import Network.Lastfm.XML (xmlWrapper)
-import qualified Network.Lastfm.API.Library as API
-
-$(xmlWrapper ["addAlbum", "addArtist", "addTrack", "getAlbums", "getArtists", "getTracks", "removeAlbum", "removeArtist", "removeScrobble", "removeTrack"])
-
-__addAlbum__
-addAlbum ∷ Artist → Album → APIKey → SessionKey → Secret → Lastfm Response
-
-__addArtist__
-addArtist ∷ Artist → APIKey → SessionKey → Secret → Lastfm Response
-
-__addTrack__
-addTrack ∷ Artist → Track → APIKey → SessionKey → Secret → Lastfm Response
-
-__getAlbums__
-getAlbums ∷ User → Maybe Artist → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__getArtists__
-getArtists ∷ User → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__getTracks__
-getTracks ∷ User → Maybe Artist → Maybe Album → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__removeAlbum__
-removeAlbum ∷ Artist → Album → APIKey → SessionKey → Secret → Lastfm Response
-
-__removeArtist__
-removeArtist ∷ Artist → APIKey → SessionKey → Secret → Lastfm Response
-
-__removeScrobble__
-removeScrobble ∷ Artist → Track → Timestamp → APIKey → SessionKey → Secret → Lastfm Response
-
-__removeTrack__
-removeTrack ∷ Artist → Track → APIKey → SessionKey → Secret → Lastfm Response
diff --git a/src/Network/Lastfm/XML/Playlist.hs b/src/Network/Lastfm/XML/Playlist.hs
deleted file mode 100644
--- a/src/Network/Lastfm/XML/Playlist.hs
+++ /dev/null
@@ -1,21 +0,0 @@
-{-# LANGUAGE CPP #-}
-{-# LANGUAGE TemplateHaskell #-}
--- | Playlist API module
-{-# OPTIONS_HADDOCK prune #-}
-module Network.Lastfm.XML.Playlist
-  ( addTrack, create
-  ) where
-
-#include "playlist.docs"
-
-import Network.Lastfm.Internal
-import Network.Lastfm.XML (xmlWrapper)
-import qualified Network.Lastfm.API.Playlist as API
-
-$(xmlWrapper ["addTrack", "create"])
-
-__addTrack__
-addTrack ∷ Playlist → Artist → Track → APIKey → SessionKey → Secret → Lastfm Response
-
-__create__
-create ∷ Maybe Title → Maybe Description → APIKey → SessionKey → Secret → Lastfm Response
diff --git a/src/Network/Lastfm/XML/Radio.hs b/src/Network/Lastfm/XML/Radio.hs
deleted file mode 100644
--- a/src/Network/Lastfm/XML/Radio.hs
+++ /dev/null
@@ -1,32 +0,0 @@
-{-# LANGUAGE CPP #-}
-{-# LANGUAGE TemplateHaskell #-}
--- | Radio API module
-{-# OPTIONS_HADDOCK prune #-}
-module Network.Lastfm.XML.Radio
-  ( getPlaylist, search, tune
-  ) where
-
-#include "radio.docs"
-
-import Network.Lastfm.Internal
-import Network.Lastfm.XML (xmlWrapper)
-import qualified Network.Lastfm.API.Radio as API
-
-$(xmlWrapper ["getPlaylist", "search", "tune"])
-
-__getPlaylist__
-getPlaylist ∷ Maybe Discovery
-            → Maybe RTP
-            → Maybe BuyLinks
-            → Multiplier
-            → Bitrate
-            → APIKey
-            → SessionKey
-            → Secret
-            → Lastfm Response
-
-__search__
-search ∷ Name → APIKey → Lastfm Response
-
-__tune__
-tune ∷ Maybe Language → Station → APIKey → SessionKey → Secret → Lastfm Response
diff --git a/src/Network/Lastfm/XML/Tag.hs b/src/Network/Lastfm/XML/Tag.hs
deleted file mode 100644
--- a/src/Network/Lastfm/XML/Tag.hs
+++ /dev/null
@@ -1,43 +0,0 @@
-{-# LANGUAGE CPP #-}
-{-# LANGUAGE TemplateHaskell #-}
--- | Tag API module
-{-# OPTIONS_HADDOCK prune #-}
-module Network.Lastfm.XML.Tag
-  ( getInfo, getSimilar, getTopAlbums, getTopArtists, getTopTags, getTopTracks
-  , getWeeklyArtistChart, getWeeklyChartList, search
-  ) where
-
-#include "tag.docs"
-
-import Network.Lastfm.Internal
-import Network.Lastfm.XML (xmlWrapper)
-import qualified Network.Lastfm.API.Tag as API
-
-$(xmlWrapper ["getInfo", "getSimilar", "getTopAlbums", "getTopArtists", "getTopTags", "getTopTracks", "getWeeklyArtistChart", "getWeeklyChartList", "search"])
-
-__getInfo__
-getInfo ∷ Tag → Maybe Language → APIKey → Lastfm Response
-
-__getSimilar__
-getSimilar ∷ Tag → APIKey → Lastfm Response
-
-__getTopAlbums__
-getTopAlbums ∷ Tag → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__getTopArtists__
-getTopArtists ∷ Tag → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__getTopTags__
-getTopTags ∷ APIKey → Lastfm Response
-
-__getTopTracks__
-getTopTracks ∷ Tag → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__getWeeklyArtistChart__
-getWeeklyArtistChart ∷ Tag → Maybe From → Maybe To → Maybe Limit → APIKey → Lastfm Response
-
-__getWeeklyChartList__
-getWeeklyChartList ∷ Tag → APIKey → Lastfm Response
-
-__search__
-search ∷ Tag → Maybe Page → Maybe Limit → APIKey → Lastfm Response
diff --git a/src/Network/Lastfm/XML/Tasteometer.hs b/src/Network/Lastfm/XML/Tasteometer.hs
deleted file mode 100644
--- a/src/Network/Lastfm/XML/Tasteometer.hs
+++ /dev/null
@@ -1,20 +0,0 @@
-{-# LANGUAGE CPP #-}
-{-# LANGUAGE TemplateHaskell #-}
--- | Tasteometer API module
-{-# OPTIONS_HADDOCK prune #-}
-module Network.Lastfm.XML.Tasteometer
-  ( compare
-  ) where
-
-#include "tasteometer.docs"
-
-import Prelude hiding (compare)
-
-import Network.Lastfm.Internal
-import Network.Lastfm.XML (xmlWrapper)
-import qualified Network.Lastfm.API.Tasteometer as API
-
-$(xmlWrapper ["compare"])
-
-__compare__
-compare ∷ Value → Value → Maybe Limit → APIKey → Lastfm Response
diff --git a/src/Network/Lastfm/XML/Track.hs b/src/Network/Lastfm/XML/Track.hs
deleted file mode 100644
--- a/src/Network/Lastfm/XML/Track.hs
+++ /dev/null
@@ -1,91 +0,0 @@
-{-# LANGUAGE CPP #-}
-{-# LANGUAGE TemplateHaskell #-}
--- | Track API module
-{-# OPTIONS_HADDOCK prune #-}
-module Network.Lastfm.XML.Track
-  ( addTags, ban, getBuyLinks, getCorrection, getFingerprintMetadata
-  , getInfo, getShouts, getSimilar, getTags, getTopFans, getTopTags
-  , love, removeTag, scrobble, search, share, unban, unlove, updateNowPlaying
-  ) where
-
-#include "track.docs"
-
-import Network.Lastfm.Internal
-import Network.Lastfm.XML (xmlWrapper)
-import qualified Network.Lastfm.API.Track as API
-
-$(xmlWrapper ["addTags", "ban", "getBuyLinks", "getCorrection", "getFingerprintMetadata", "getInfo", "getShouts", "getSimilar", "getTags", "getTopFans", "getTopTags", "love", "removeTag", "scrobble", "search", "share", "unban", "unlove", "updateNowPlaying"])
-
-__addTags__
-addTags ∷ Artist → Track → [Tag] → APIKey → SessionKey → Secret → Lastfm Response
-
-__ban__
-ban ∷ Artist → Track → APIKey → SessionKey → Secret → Lastfm Response
-
-__getBuyLinks__
-getBuyLinks ∷ Either (Artist, Track) Mbid → Maybe Autocorrect → Country → APIKey → Lastfm Response
-
-__getCorrection__
-getCorrection ∷ Artist → Track → APIKey → Lastfm Response
-
-__getFingerprintMetadata__
-getFingerprintMetadata ∷ Fingerprint → APIKey → Lastfm Response
-
-__getInfo__
-getInfo ∷ Either (Artist, Track) Mbid → Maybe Autocorrect → Maybe Username → APIKey → Lastfm Response
-
-__getShouts__
-getShouts ∷ Either (Artist, Track) Mbid → Maybe Autocorrect → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__getSimilar__
-getSimilar ∷ Either (Artist, Track) Mbid → Maybe Autocorrect → Maybe Limit → APIKey → Lastfm Response
-
-__getTags__
-getTags ∷ Either (Artist, Track) Mbid → Maybe Autocorrect → Either User (SessionKey, Secret) → APIKey → Lastfm Response
-
-__getTopFans__
-getTopFans ∷ Either (Artist, Track) Mbid → Maybe Autocorrect → APIKey → Lastfm Response
-
-__getTopTags__
-getTopTags ∷ Either (Artist, Track) Mbid → Maybe Autocorrect → APIKey → Lastfm Response
-
-__love__
-love ∷ Artist → Track → APIKey → SessionKey → Secret → Lastfm Response
-
-__removeTag__
-removeTag ∷ Artist → Track → Tag → APIKey → SessionKey → Secret → Lastfm Response
-
-__scrobble__
-scrobble ∷ ( Timestamp, Maybe Album, Artist, Track, Maybe AlbumArtist
-           , Maybe Duration, Maybe StreamId, Maybe ChosenByUser
-           , Maybe Context, Maybe TrackNumber, Maybe Mbid )
-         → APIKey
-         → SessionKey
-         → Secret
-         → Lastfm Response
-
-__search__
-search ∷ Track → Maybe Page → Maybe Limit → Maybe Artist → APIKey → Lastfm Response
-
-__share__
-share ∷ Artist → Track → Recipient → Maybe Message → Maybe Public → APIKey → SessionKey → Secret → Lastfm Response
-
-__unban__
-unban ∷ Artist → Track → APIKey → SessionKey → Secret → Lastfm Response
-
-__unlove__
-unlove ∷ Artist → Track → APIKey → SessionKey → Secret → Lastfm Response
-
-__updateNowPlaying__
-updateNowPlaying ∷ Artist
-                 → Track
-                 → Maybe Album
-                 → Maybe AlbumArtist
-                 → Maybe Context
-                 → Maybe TrackNumber
-                 → Maybe Mbid
-                 → Maybe Duration
-                 → APIKey
-                 → SessionKey
-                 → Secret
-                 → Lastfm Response
diff --git a/src/Network/Lastfm/XML/User.hs b/src/Network/Lastfm/XML/User.hs
deleted file mode 100644
--- a/src/Network/Lastfm/XML/User.hs
+++ /dev/null
@@ -1,94 +0,0 @@
-{-# LANGUAGE CPP #-}
-{-# LANGUAGE TemplateHaskell #-}
--- | User API module
-{-# OPTIONS_HADDOCK prune #-}
-module Network.Lastfm.XML.User
-  ( getArtistTracks, getBannedTracks, getEvents, getFriends, getInfo, getLovedTracks
-  , getNeighbours, getNewReleases, getPastEvents, getPersonalTags, getPlaylists, getRecentStations
-  , getRecentTracks, getRecommendedArtists, getRecommendedEvents, getShouts, getTopAlbums
-  , getTopArtists, getTopTags, getTopTracks, getWeeklyAlbumChart, getWeeklyArtistChart
-  , getWeeklyChartList, getWeeklyTrackChart, shout
-  ) where
-
-#include "user.docs"
-
-import Network.Lastfm.Internal
-import Network.Lastfm.XML (xmlWrapper)
-import qualified Network.Lastfm.API.User as API
-
-$(xmlWrapper ["getArtistTracks", "getBannedTracks", "getEvents", "getFriends", "getInfo", "getLovedTracks", "getNeighbours", "getNewReleases", "getPastEvents", "getPersonalTags", "getPlaylists", "getRecentStations", "getRecentTracks", "getRecommendedArtists", "getRecommendedEvents", "getShouts", "getTopAlbums", "getTopArtists", "getTopTags", "getTopTracks", "getWeeklyAlbumChart", "getWeeklyArtistChart", "getWeeklyChartList", "getWeeklyTrackChart", "shout"])
-
-__getArtistTracks__
-getArtistTracks ∷ User → Artist → Maybe StartTimestamp → Maybe EndTimestamp → Maybe Page → APIKey → Lastfm Response
-
-__getBannedTracks__
-getBannedTracks ∷ User → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__getEvents__
-getEvents ∷ User → Maybe Page → Maybe Limit → Maybe FestivalsOnly → APIKey → Lastfm Response
-
-__getFriends__
-getFriends ∷ User → Maybe RecentTracks → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__getInfo__
-getInfo ∷ Maybe User → APIKey → Lastfm Response
-
-__getLovedTracks__
-getLovedTracks ∷ User → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__getNeighbours__
-getNeighbours ∷ User → Maybe Limit → APIKey → Lastfm Response
-
-__getNewReleases__
-getNewReleases ∷ User → Maybe UseRecs → APIKey → Lastfm Response
-
-__getPastEvents__
-getPastEvents ∷ User → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__getPersonalTags__
-getPersonalTags ∷ User → Tag → TaggingType → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__getPlaylists__
-getPlaylists ∷ User → APIKey → Lastfm Response
-
-__getRecentStations__
-getRecentStations ∷ User → Maybe Page → Maybe Limit → APIKey → SessionKey → Secret → Lastfm Response
-
-__getRecentTracks__
-getRecentTracks ∷ User → Maybe Page → Maybe Limit → Maybe From → Maybe To → APIKey → Lastfm Response
-
-__getRecommendedArtists__
-getRecommendedArtists ∷ Maybe Page → Maybe Limit → APIKey → SessionKey → Secret → Lastfm Response
-
-__getRecommendedEvents__
-getRecommendedEvents ∷ Maybe Page → Maybe Limit → APIKey → SessionKey → Secret → Lastfm Response
-
-__getShouts__
-getShouts ∷ User → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__getTopAlbums__
-getTopAlbums ∷ User → Maybe Period → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__getTopArtists__
-getTopArtists ∷ User → Maybe Period → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__getTopTags__
-getTopTags ∷ User → Maybe Limit → APIKey → Lastfm Response
-
-__getTopTracks__
-getTopTracks ∷ User → Maybe Period → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__getWeeklyAlbumChart__
-getWeeklyAlbumChart ∷ User → Maybe From → Maybe To → APIKey → Lastfm Response
-
-__getWeeklyArtistChart__
-getWeeklyArtistChart ∷ User → Maybe From → Maybe To → APIKey → Lastfm Response
-
-__getWeeklyChartList__
-getWeeklyChartList ∷ User → APIKey → Lastfm Response
-
-__getWeeklyTrackChart__
-getWeeklyTrackChart ∷ User → Maybe From → Maybe To → APIKey → Lastfm Response
-
-__shout__
-shout ∷ User → Message → APIKey → SessionKey → Secret → Lastfm Response
diff --git a/src/Network/Lastfm/XML/Venue.hs b/src/Network/Lastfm/XML/Venue.hs
deleted file mode 100644
--- a/src/Network/Lastfm/XML/Venue.hs
+++ /dev/null
@@ -1,24 +0,0 @@
-{-# LANGUAGE CPP #-}
-{-# LANGUAGE TemplateHaskell #-}
--- | Venue API module
-{-# OPTIONS_HADDOCK prune #-}
-module Network.Lastfm.XML.Venue
-  ( getEvents, getPastEvents, search
-  ) where
-
-#include "venue.docs"
-
-import Network.Lastfm.Internal
-import Network.Lastfm.XML (xmlWrapper)
-import qualified Network.Lastfm.API.Venue as API
-
-$(xmlWrapper ["getEvents", "getPastEvents", "search"])
-
-__getEvents__
-getEvents ∷ Venue → Maybe FestivalsOnly → APIKey → Lastfm Response
-
-__getPastEvents__
-getPastEvents ∷ Venue → Maybe FestivalsOnly → Maybe Page → Maybe Limit → APIKey → Lastfm Response
-
-__search__
-search ∷ Venuename → Maybe Page → Maybe Limit → Maybe Country → APIKey → Lastfm Response
