diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,15 @@
+0.3.2.0
+=======
+
+  * Fix `auth.getMobileSession` method
+  * Provide `lastfm_` function for making requests without parsing response. That is most useful for `POST` requests like `N.L.Track.love`
+
+0.3.0.0
+=======
+
+  * Support batch operations for `N.L.Track.scrobble`, `N.L.Library.{addAlbum,addArtist}`
+
+0.2.0.0
+=======
+
+  * Vastly simplified internal representations leading to simpler library interface
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,101 @@
+# liblastfm
+[![Hackage](https://budueba.com/hackage/liblastfm)](http://hackage.haskell.org/package/liblastfm)
+[![Build Status](https://drone.io/github.com/supki/liblastfm/status.png)](https://drone.io/github.com/supki/liblastfm/latest)
+[![Build Status](https://secure.travis-ci.org/supki/liblastfm.png?branch=develop)](http://travis-ci.org/supki/liblastfm)
+
+Complete API interface to [last.fm][1] service.
+Documentation is available in two flavours:
+  * original [API reference][2]
+  * liblastfm [haddocks][3]
+
+## General introduction
+liblastfm provides Applicative interface for constructing requests. Also, it handles all machinery needed to prepare request for sending:
+  * [signing][4]
+  * url-encoding
+  * miscellaneous stuff like choosing correct HTTP method, etc
+
+Once request is ready, liblastfm may send it and get you back a response.
+Response format might be:
+  * `Maybe Value` from [aeson][5] for json queries (nice interaction with [aeson-lens][6] for free!)
+  * raw `ByteString` for xml queries
+
+## Installation
+To install either use hackage:
+
+    % cabal install liblastfm
+
+Or git:
+
+    % git clone git@github.com:supki/liblastfm
+    % cd liblastfm
+    % cabal install
+
+## Usage
+Suppose, you need to use [`tag.search`](http://www.last.fm/api/show/tag.search) API method.
+First find it in liblastfm: `Tag` would be the name of the module and `search` would be the name of function. [Here it is][7].
+So import a couple of modules:
+
+    ghci> import Network.Lastfm -- a bunch of useful utilities
+    ghci> import qualified Network.Lastfm.Tag as Tag -- for Tag.search
+
+Now you may you applicative `<*>` for required and `<*` or `*>` for optional parameters to construct
+desired request:
+
+    Tag.search <*> tag "russian-folk" <* limit 3 <*> apiKey "29effec263316a1f8a97f753caaa83e0" <* json
+
+To send constructed request use `lastfm`:
+
+    ghci> lastfm $ Tag.search <*> tag "russian-folk" <* limit 10 <*> apiKey "29effec263316a1f8a97f753caaa83e0" <* json
+    Just (Object fromList [("results",Object fromList [("tagmatches", ...
+
+How to parse responses is described [in wiki][8].
+
+## FAQ
+**Q: I'm getting the following error. How do I fix it?**
+```
+> Artist.getInfo <*> artist "Pink Floyd" <*> apiKey "29effec263316a1f8a97f753caaa83e0"
+
+<interactive>:8:27:
+    Couldn't match expected type `Data.Text.Lazy.Internal.Text'
+                with actual type `[Char]
+```
+A: This means you haven't OverloadedStrings extension enabled.
+To enable it (either one works):
+  * type in `:set -XOverloadedStrings` while in ghci session.
+  * add `{-# LANGUAGE OverloadedStrings #-}` to the top of the file
+  * compile with `-XOverloadedStrings` switch
+
+**Q: I'm getting the following error. How do I fix it?**
+```
+> lastfm (Artist.getInfo <*> artist "Pink Floyd" <*> apiKey "29effec263316a1f8a97f753caaa83e0")
+
+<interactive>:13:1:
+    No instance for (Network.Lastfm.Response.Supported f0)
+      arising from a use of `lastfm'
+    The type variable `f0' is ambiguous
+    Possible fix: add a type signature that fixes these type variable(s)
+    Note: there are several potential instances:
+      instance Network.Lastfm.Response.Supported 'XML
+        -- Defined at src/Network/Lastfm/Response.hs:66:10
+      instance Network.Lastfm.Response.Supported 'JSON
+        -- Defined at src/Network/Lastfm/Response.hs:51:10
+```
+A: This error message indicates that GHC cannot infer response format for `Request`.
+To fix it, add use `json` or `xml` helpers, depending on your needs
+
+```
+> lastfm (Artist.getInfo <*> artist "Pink Floyd" <*> apiKey "29effec263316a1f8a97f753caaa83e0" <* json)
+Just (Object fromList [("artist" ...
+> lastfm (Artist.getInfo <*> artist "Pink Floyd" <*> apiKey "29effec263316a1f8a97f753caaa83e0" <* xml)
+"<?xml version=\"1.0\" ...
+```
+-
+
+ [1]: http://www.last.fm/
+ [2]: http://www.last.fm/api/intro
+ [3]: http://supki.github.com/liblastfm/
+ [4]: http://www.last.fm/api/authspec#8
+ [5]: http://hackage.haskell.org/package/aeson
+ [6]: http://hackage.haskell.org/package/aeson-lens
+ [7]: http://supki.github.com/liblastfm/Network-Lastfm-Tag.html#v:search
+ [8]: https://github.com/supki/liblastfm/wiki/How-to-parse-JSON-response
diff --git a/liblastfm.cabal b/liblastfm.cabal
--- a/liblastfm.cabal
+++ b/liblastfm.cabal
@@ -1,33 +1,41 @@
-name: liblastfm
-version: 0.3.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
+name:          liblastfm
+version:       0.3.2.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:
   Provides interface to Lastfm REST API, supports XML and JSON formats.
 cabal-version: >= 1.10
-build-type: Simple
+build-type:    Simple
+extra-source-files:
+  README.md
+  CHANGELOG.md
 
+source-repository head
+  type:     git
+  location: https://github.com/supki/liblastfm
+
 library
   default-language: Haskell2010
   build-depends:
-    aeson,
-    base >= 3 && < 5,
-    bytestring,
-    cereal,
-    containers >= 0.5,
-    contravariant,
-    crypto-api,
-    http-conduit >= 1.9,
-    http-types,
-    network,
-    pureMD5,
-    semigroups,
-    text,
-    void
+      aeson
+    , base >= 3 && < 5
+    , bytestring
+    , cereal
+    , containers >= 0.5
+    , contravariant
+    , crypto-api
+    , http-conduit >= 1.9
+    , http-types
+    , network
+    , pureMD5
+    , semigroups
+    , tagged
+    , text
+    , void
   hs-source-dirs: src
   exposed-modules:
     Network.Lastfm
@@ -53,8 +61,3 @@
     -Wall
     -fno-warn-unused-do-bind
     -funbox-strict-fields
-
-
-source-repository head
-  type:     git
-  location: https://github.com/supki/liblastfm
diff --git a/src/Network/Lastfm/Authentication.hs b/src/Network/Lastfm/Authentication.hs
--- a/src/Network/Lastfm/Authentication.hs
+++ b/src/Network/Lastfm/Authentication.hs
@@ -4,11 +4,11 @@
 --
 -- Basically, lastfm provides 3 ways to authenticate user:
 --
---  - web application - <http://www.lastfm.ru/api/webauth>
+--  - web application - <http://www.last.fm/api/webauth>
 --
---  - desktop application - <http://www.lastfm.ru/api/desktopauth>
+--  - desktop application - <http://www.last.fm/api/desktopauth>
 --
---  - modile application - <http://www.lastfm.ru/api/mobileauth>
+--  - modile application - <http://www.last.fm/api/mobileauth>
 --
 -- Note that you can use any of them in your
 -- application despite their names
@@ -31,6 +31,7 @@
   , link
   ) where
 
+import Control.Applicative ((<*))
 import Data.Monoid
 
 import Network.Lastfm.Internal
@@ -45,7 +46,7 @@
 
 -- | Get session key
 getMobileSession :: Request f (Username -> Password -> APIKey -> Sign)
-getMobileSession = api "auth.getMobileSession"
+getMobileSession = api "auth.getMobileSession" <* post
 {-# INLINE getMobileSession #-}
 
 
diff --git a/src/Network/Lastfm/Response.hs b/src/Network/Lastfm/Response.hs
--- a/src/Network/Lastfm/Response.hs
+++ b/src/Network/Lastfm/Response.hs
@@ -1,23 +1,23 @@
 {-# LANGUAGE DataKinds #-}
-{-# LANGUAGE FlexibleContexts #-}
-{-# LANGUAGE GeneralizedNewtypeDeriving #-}
 {-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE ScopedTypeVariables #-}
 {-# LANGUAGE TypeFamilies #-}
 -- | Request sending and Response parsing
 module Network.Lastfm.Response
   ( -- * Sign Request
     -- $sign
     Secret(..), sign
-    -- * Get Response
-  , Response, lastfm, lastfm', finalize
+    -- * Get 'Response'
+  , Response, Supported, lastfm, lastfm_
+    -- ** Internal
+  , lastfm', finalize
   ) where
 
 import Control.Applicative
 import Control.Exception (throw)
 import Control.Monad
 import Data.Monoid
-import Data.String (IsString)
+import Data.String (IsString(..))
+import Data.Proxy (Proxy(..))
 
 import           Crypto.Classes (hash')
 import           Data.Aeson ((.:), Value, decode, parseJSON)
@@ -25,6 +25,7 @@
 import qualified Data.ByteString.Lazy as Lazy
 import qualified Data.ByteString as Strict
 import           Data.Digest.Pure.MD5 (MD5Digest)
+import           Data.Map (Map)
 import qualified Data.Map as M
 import           Data.Text (Text)
 import qualified Data.Text as T
@@ -44,9 +45,13 @@
 -- described at <http://www.last.fm/api/authspec#8>
 
 
+-- | 'Supported' provides parsing for a chosen 'Format'
+--
+-- 'JSON' is parsed to aeson's 'Value', 'XML' is to lazy 'ByteString'
+-- (in other words, parsing XML is left to the user)
 class Supported (f :: Format) where
   type Response f
-  parse :: R f -> Lazy.ByteString -> C.ResponseHeaders -> Response f
+  parse :: proxy f -> Lazy.ByteString -> C.ResponseHeaders -> Response f
   base :: R f
 
 
@@ -54,8 +59,8 @@
   type Response JSON = Maybe Value
   parse _ b hs = do
     v <- decode b
-    case parseMaybe ((.: "error") <=< parseJSON) v of
-      Just (_ :: Int) ->
+    case parseMaybe ((.: "error") <=< parseJSON) v :: Maybe Int of
+      Just _ ->
         throw (C.StatusCodeException C.status400 (("Response", Strict.concat $ Lazy.toChunks b) : hs) (C.createCookieJar []))
       _ -> return v
   base = R
@@ -68,6 +73,7 @@
 instance Supported XML where
   type Response XML = Lazy.ByteString
   parse _ b _ = b
+  {-# INLINE parse #-}
   base = R
     { _host = "https://ws.audioscrobbler.com/2.0/"
     , _method = "GET"
@@ -77,37 +83,54 @@
 
 
 -- | Application secret
-newtype Secret = Secret Text deriving (Show, IsString)
+newtype Secret = Secret Text deriving (Show)
 
+instance IsString Secret where
+  fromString = Secret . fromString
 
 -- | Sign 'Request' with 'Secret'
 sign :: Secret -> Request f Sign -> Request f Ready
-sign (Secret s) = coerce . (<* signature)
+sign s = coerce . (<* signature)
  where
-  signature = wrap $ \r@R { _query = q } ->
-    r { _query = M.insert "api_sig" (signer (foldr M.delete q ["format", "callback"])) q }
+  signature = wrap $
+    \r@R { _query = q } -> r { _query = api_sig s . authToken $ q }
 
-  signer = T.pack . show . (hash' :: Strict.ByteString -> MD5Digest) .
-    T.encodeUtf8 . M.foldrWithKey(\k v xs -> k <> v <> xs) s
+authToken :: Map Text Text -> Map Text Text
+authToken q = maybe q (M.delete "password") $ do
+  password <- M.lookup "password" q
+  username <- M.lookup "username" q
+  return (M.insert "authToken" (md5 (username <> (md5 password))) q)
 
+api_sig :: Secret -> Map Text Text -> Map Text Text
+api_sig (Secret s) q = M.insert "api_sig" (signer (foldr M.delete q ["format", "callback"])) q
+ where
+  signer = md5 . M.foldrWithKey(\k v xs -> k <> v <> xs) s
 
--- | Send Request and parse Response
+md5 :: Text -> Text
+md5 = T.pack . show . (hash' :: Strict.ByteString -> MD5Digest) . T.encodeUtf8
+
+
+-- | Send 'Request' and parse the 'Response'
 lastfm :: Supported f => Request f Ready -> IO (Response f)
-lastfm = lastfm' . finalize
+lastfm = lastfm' parse . finalize
 
+-- | Send 'Request' without parsing the 'Response'
+lastfm_ :: Supported f => Request f Ready -> IO ()
+lastfm_ = lastfm' (\_ _ _ -> ()) . finalize
 
--- | Get R from Request
---
--- That's rarely needed unless you want low-level requests manipulation
+
+-- | Get 'R' from 'Request'
 finalize :: Supported f => Request f Ready -> R f
 finalize = ($ base) . unwrap
 
 
--- | Send R and parse Response
---
--- That's rarely needed unless you want low-level requests manipulation
-lastfm' :: Supported f => R f -> 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)
+-- | Send 'R' and parse 'Response' with the supplied function
+lastfm' :: Supported f => (Proxy f -> Lazy.ByteString -> C.ResponseHeaders -> a) -> R f -> IO a
+lastfm' f request = C.withManager $ \manager -> do
+  req <- C.parseUrl (render request)
+  let req' = req
+       { C.method          = _method request
+       , C.responseTimeout = Just 10000000
+       }
+  res <- C.httpLbs req' manager
+  return $ f Proxy (C.responseBody res) (C.responseHeaders res)
diff --git a/src/Network/Lastfm/Tasteometer.hs b/src/Network/Lastfm/Tasteometer.hs
--- a/src/Network/Lastfm/Tasteometer.hs
+++ b/src/Network/Lastfm/Tasteometer.hs
@@ -19,7 +19,7 @@
 --
 -- Optional: 'limit'
 --
--- <http://www.lastfm.ru/api/show/tasteometer.compare>
+-- <http://www.last.fm/api/show/tasteometer.compare>
 compare :: (Targeted u, Targeted v) => Request f u -> Request f v -> Request f (APIKey -> Ready)
 compare u v = api "tasteometer.compare" <* comparison 1 u <* comparison 2 v
 {-# INLINE compare #-}
