Since it is an estimate, are there any limitations to it? What cases should its use be relied upon less as it may produce an estimate that is not meaningful or accurate?
estimateGas works by pretending the transaction was actually being included in the blockchain, and then returning the exact gas amount that would have been charged if that pretend operation was real. In other words, it uses the exact same procedure a miner would use to calculate the actual fee.
Given this approach, a casual observer might think that there are no limitations and that the estimate will always be exactly right. Unfortunately, they would be mistaken.
Imagine a simple contract which checks the most recent block hash and then calls another large, gas-guzzling contract only if the 10th bit in the block hash is a 1. If you use estimateGas to measure the consumption of a transaction which calls this simple contract, your result will depend entirely on whether the most recent block contained a 1 in the 10th bit of its block hash. If a new block is published between when you call estimateGas and when the transaction is actually included, your estimate has a 50% chance of being wildly wrong. This may seem like a contrived example, but lots of contracts use block hashes as a source of entropy, and change their behaviour based on what the hash is. So this is something that could definitely happen in the wild.
But it gets worse. What if the contract your transaction is calling on changes its behaviour based on the transactions submitted to it? Now it's possible for the gas consumption to be different based on the order in which transactions were submitted.
For example, suppose a commonly called contract nearly always costs only a few hundred gas to call. But it contains one weird clause in its code so that a message signed by a specific key alters its behaviour, making it consume millions of gas every time it is called. The same key is then able to alter the behaviour back. Now the owner of that key can alter the contract's gas consumption at will. If the owner is a miner, they could use this "trapped contract" to turn this gas-wasting behaviour on at the beginning of every block they compute, and off at the end. As long as they never publicly publish these private transactions, then every call of estimateGas will return a low value, while every genuinely submitted transaction will cost millions of gas (which goes straight into the miner's pocket). With a careful contract design, they wouldn't even have to actually perform the calculations, because they know what the result will be. And they could execute this "fee-stealing attack" at random, only performing it occasionally so no one realises until many users have been taken in.
Again, this example is contrived. But the point is that you should always consider if an attacker has something to gain by you making a bad estimate. If there's a possible vector, put additional code into the part of your app to handle the situation where your estimate is wrong. One simple technique you should always use is to include a sane gas limit. That way there is at least an upper bound on what an attacker can do. Beyond that, remember that it's called the estimateGas call, not the guaranteedMaximumGas call and just be reasonable in your reliance on it.
If there is any way for the consequences of your transaction to change depending on when or to whom it is submitted, there is a potential for estimateGas to be wrong by arbitrarily large amounts. Take extra precautions.
One limitation I have found, or at least a noteworthy aspect of the eth.estimateGas method, is that it only works as intended if the actual call does not throw an exception.
This may well be a bug in the current version of geth, but what I have found is that a call to a method that throws an exception always yields the same gas estimate result, which is a value of 50000000 units of gas. At a gas price of 20000000000, my estimateGas calls were yielding exactly 1 ether, which seemed to be a huge amount for a simple call throwing an exception.
This is a little bit of a bummer, since I have not found a reliable way to estimate gas in transactions that actually throw an exception (which do consume gas) - but at least it is worth knowing ;-) So it looks like a good idea to debug.traceTransaction your transaction prior to estimating its gas consumption to make sure it will not throw an exception and it will not give you widely wrong gas estimate.
Incidentally, this "50000000" value seems to be same that is used to initialize gas related variables in the the source code of the go API (https://github.com/ethereum/go-ethereum/blob/master/eth/api.go), which seems to hint that this is more of a bug than a feature ;-)
One limitation (from my own observation, hopefully someone will correct me if I'm miunderstanding) is that even if
estimateGas estimates correctly, that doesn't give you the gas limit that you need to set when sending your transaction.
The issue is that refunds are credited only at the end of the transaction, so if you have a transaction that does some work, and cleans up some storage as it goes, you need to set a high enough gas limit to do all the work without the benefit of the refund.