Using Wreq and Lens libraries to query Prosper for account info
You're on the right path! You're asking great questions.


  I feel like I really went overboard with the let bindings, but I couldn't figure out how to break up some absurdly long lines in another way.


This is sort of unavoidable with wreq (or any HTTP client library in general probably), as you have to pass around a lot of state to communicate on a stateless protocol. You do a little better if you pass your options as a single Options value. Leads to friendlier type signatures too.


  All my maybeXXX functions. I needed them to operate on the Maybe typeclass in the way I was expecting, but I feel like there is a more elegant use of Monads that could avoid these auxiliary functions.


You can use fmap or &lt;$&gt; instead of constructing these maybeWobble helpers, but as we see below there's an even better option.


  The creation of the MResult type was done just to parse the response body of the web API. There must be a way to use Lens to extract that field and parse it as a list of Note directly.


There is! Your usage of the lens-aeson package is right on the money. To grab all the prosper_rating :: Text of all the notes in the result key, we can simply write this lens traversal:



_ratings = responseBody . key "result"
                        . _Array
                        . traverse
                        . key "prosper_rating"
                        . _String


(Traversal just means that you need to use ^.. instead of ^. and that you'll get back a list of values instead of one when you do so.)

The other way you could be using more lens is to import the text lens module, which provides two "once you learn of them you can't stop using them" helpers:


packed, unpacked: isomorphisms between Text and String. Never import qualified Data.Text again!
utf8: a prism that converts a ByteString to a Text, when possible. Never import qualified Data.Bytestring again!
_Show: a prism (in Control.Lens actually, but we'll toss it onto this list) that lets you convert any Show a =&gt; a value into String. Earn a combo bonus when you compose it with packed to get a Text.


One other note is that you should use param and := from the wreq library to construct parameter strings and POST bodies. They handle the text massaging for you.

OK, putting everything together:

{-# LANGUAGE OverloadedStrings #-}

module Free_D where

import Control.Lens
import Data.Aeson
import Data.Aeson.Lens
import Data.Monoid ((&lt;&gt;))
import Data.Text.Strict.Lens
import Network.Wreq

import Data.Text (Text)

-- String Constants
prosperAddress, clientID, clientSecret, userID, password :: Text
prosperAddress = "https://api.prosper.com/v1/"
clientID = "cicicicici"
clientSecret = "ssssssssssssssss"
userID = "uuuuuuuuuu"
password = "pppppppppp"

prosperURL :: Text -&gt; String
prosperURL target = (prosperAddress &lt;&gt; target) ^. unpacked

-- | Retrives the OAuth2 token from the Prosper server.
oauthToken :: IO Text
oauthToken = do
    let opts =
          defaults
            &amp; header "Accept" .~ ["application/json"]
            &amp; header "Content-Type" .~ ["application/x-www-form-urlencoded"]
    let body =
          [ "grant_type" := ("password" :: Text)
          , "client_id" := clientID
          , "client_secret" := clientSecret
          , "username" := userID
          , "password" := password]
    resp &lt;- postWith opts (prosperURL "security/oauth/token") body
    return (resp ^. responseBody . key "access_token" . _String)

-- | Performs a GET request to the provided target using the provided
-- OAuth token.
getTarget :: Options -&gt; String -&gt; Text -&gt; IO (Response Value)
getTarget opts url token = do
    getWith (opts &amp; header "Authorization" .~ rawr) url &gt;&gt;= asJSON
    where
        rawr = ["bearer " &lt;&gt; utf8 # token]

-- | Retrives the list of currently owned notes from Prosper.
--
-- Note: The query responses are paginated to 25 results per response.
getNotesList :: Text -&gt; Int -&gt; IO [Text]
getNotesList token offset = do
    resp &lt;- getTarget opts (prosperURL "notes/") token
    let ratings = resp ^.. _ratings
    remainder &lt;- if length ratings == 25 then return [] else getNotesList token (offset + 25)
    return (ratings ++ remainder)
    where
        opts = defaults &amp; param "offset" .~ [offset ^. re _Show . packed]
        _ratings = responseBody . key "result" . _Array . traverse . key "prosper_rating" . _String

main :: IO ()
main = do
    token &lt;- oauthToken
    notes &lt;- getNotesList token 0
    print notes


(I got the code compiling but, as I don't have an account, I was unable to run and test it.)