Calculating "total received" and balance by an address This is something which I think should be simple but it's driving me crazy. See for example this list of transactions associated with an address:

https://blockchain.info/address/1EZK42jGEJVniyBn1wXrUx92wzyUkYB8kJ?format=json

What I want to do is simply arrive at the same total_received, total_sent and balance stats for the given data.

An obvious requirement is to not count the "change" as a new payment to the address. My idea was to simply sum all the outputs in transactions whose inputs are not from the same target address, e.g. if a transaction has an input (or multiple inputs) from A and outputs to B and A, treat this output as change and don't include it in the sum -- only this doesn't produce the same sum as blockchain.info! In this case, it produces 158.11536395 vs 251.24164662. It looks like I'm missing something?

The next step would be to take the list of outputs produced by the first step, and see which are spent and which are not. The sum of the unspent outputs is the current balance. Is this correct?

Anyone?
As the other answer has noted, balances don't technically exist for a specific address. This is because there may be many outputs that are addressed to an address you own, but you can only spend these outputs discretely (I.e. You can say deduct x from my account, you can only say deduct this output amount which was previously awarded to this address). 

We can define a pseudo-notion of a balance, though, let's call it the address' unlock-able value (UV), because the owner of the private key for that address can unlock some # of coins by knowing the private key for that address. 

Now, to calculate the UV balance, total received, and total sent of an address, you follow this pseudo-algorithm:

Address a;
Uv = 0;
Received = 0;
Sent = 0;
For each (output in blockchain):
    If (output is unlock-able by owner of address a):
        Received += output.amount;
        If (output has been spent):
            Sent += output.amount;
        Else:
            Uv += output.amount;


At the end of all the outputs in the blockchain, the Uv amount will have a value that is similar in concept to a balance. But here we can also see why this is a very tricky notion:


How do you know if an output is unlock-able by owner of address? Sometimes only the key holder themselves can know. 
What if an output can be unlocked by, say, 1 of a set of 3 keys? Do you credit the balances of all three keys, or none? 


I suspect the way blockchain.info does this is by basically running the above pseudo algorithm (although much more optimized and using a database), but only considering outputs in the Pay-to-Pubkey-Hash format. 



Alright, through contacting the blockchain.info support team and some testing I've found that the problem was the API link given in the question is both paginated and rate limited. The following PHP code produces the same values that blockchain.info shows on their site, assuming you don't run into rate limiting problems.

&lt;?php

// Method: POST, PUT, GET etc
// Data: array("param" =&gt; "value") ==&gt; index.php?param=value

function CallAPI($method, $url, $data = false)
{
    $curl = curl_init();

    switch ($method)
    {
        case "POST":
            curl_setopt($curl, CURLOPT_POST, 1);

            if ($data)
                curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
            break;
        case "PUT":
            curl_setopt($curl, CURLOPT_PUT, 1);
            break;
        default:
            if ($data)
                $url = sprintf("%s?%s", $url, http_build_query($data));
    }

    // Optional Authentication:
    curl_setopt($curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
    // curl_setopt($curl, CURLOPT_USERPWD, "username:password");

    curl_setopt($curl, CURLOPT_URL, $url);
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);

    $result = curl_exec($curl);

    curl_close($curl);

    return $result;
}

$address = "1EZK42jGEJVniyBn1wXrUx92wzyUkYB8kJ";
$offset=0;
$txs = array();

while (true)
{
    $val = json_decode(CallAPI("GET", "https://blockchain.info/address/$address?format=json&amp;limit=10&amp;offset=$offset"), true);
    if (count($val["txs"]) == 0)
        break;
    else 
        $txs = array_merge($txs, $val["txs"]);

    $offset += 10;
}

$uv       = 0;
$received = 0;
$sent     = 0;
$nrefs    = 0;

foreach ($txs as $tx)
{   
    $ininputs = false;
    $totalin = 0;
    foreach ($tx["inputs"] as $input)
    {
        if ($input["prev_out"]["addr"] == $address)
        {
            $ininputs = true;
            $nrefs += 1;
            $totalin += $input["prev_out"]["value"];
        }
    }

    $inoutputs = false;
    $totalout = 0;
    foreach ($tx["out"] as $output)
    {
        if ($output["addr"] == $address)
        {
            $inoutputs = true;
            $nrefs += 1;
            $totalout += $output["value"];
        }
    }

    $uv       += ($totalout - $totalin);

    // $received += $totalout;
    // $sent     += $totalin;

    if ($ininputs &amp;&amp; $inoutputs &amp;&amp; $totalout &gt;= $totalin)
    {
        $received += $totalout - $totalin;
    }
    else if ($ininputs &amp;&amp; $inoutputs &amp;&amp; $totalout &lt; $totalin)
    {
        $sent += $totalin - $totalout;
    }
    else if ($inoutputs)
    {
        $received += $totalout;
    }
    else if ($ininputs)
    {
        $sent += $totalin;
    }
}
echo "\n";

echo "nrefs    : " . $nrefs . "\n";
echo "uv       : " . $uv . "\n";
echo "received : " . $received . "\n";
echo "sent     : " . $sent . "\n";