diff --git a/miner/miner.go b/miner/miner.go
index bf6a4880202a260c3bb92f7ad5d2746d03eb464c..5087785794abc9d3e2974a8f837a02cc1f7d5af3 100644
--- a/miner/miner.go
+++ b/miner/miner.go
@@ -139,8 +139,15 @@ func (self *Miner) Mining() bool {
 	return atomic.LoadInt32(&self.mining) > 0
 }
 
-func (self *Miner) HashRate() int64 {
-	return self.pow.GetHashrate()
+func (self *Miner) HashRate() (tot int64) {
+	tot += self.pow.GetHashrate()
+	// do we care this might race? is it worth we're rewriting some
+	// aspects of the worker/locking up agents so we can get an accurate
+	// hashrate?
+	for _, agent := range self.worker.agents {
+		tot += agent.GetHashRate()
+	}
+	return
 }
 
 func (self *Miner) SetExtra(extra []byte) {
diff --git a/miner/remote_agent.go b/miner/remote_agent.go
index 674ca40acd516296551c2bfd23e8d46ac4dd4eae..5c672a6e028893000a9489ee3b3038b64504481a 100644
--- a/miner/remote_agent.go
+++ b/miner/remote_agent.go
@@ -27,6 +27,11 @@ import (
 	"github.com/ethereum/go-ethereum/logger/glog"
 )
 
+type hashrate struct {
+	ping time.Time
+	rate uint64
+}
+
 type RemoteAgent struct {
 	mu sync.Mutex
 
@@ -36,14 +41,24 @@ type RemoteAgent struct {
 
 	currentWork *Work
 	work        map[common.Hash]*Work
+
+	hashrateMu sync.RWMutex
+	hashrate   map[common.Hash]hashrate
 }
 
 func NewRemoteAgent() *RemoteAgent {
-	agent := &RemoteAgent{work: make(map[common.Hash]*Work)}
+	agent := &RemoteAgent{work: make(map[common.Hash]*Work), hashrate: make(map[common.Hash]hashrate)}
 
 	return agent
 }
 
+func (a *RemoteAgent) SubmitHashrate(id common.Hash, rate uint64) {
+	a.hashrateMu.Lock()
+	defer a.hashrateMu.Unlock()
+
+	a.hashrate[id] = hashrate{time.Now(), rate}
+}
+
 func (a *RemoteAgent) Work() chan<- *Work {
 	return a.workCh
 }
@@ -63,7 +78,17 @@ func (a *RemoteAgent) Stop() {
 	close(a.workCh)
 }
 
-func (a *RemoteAgent) GetHashRate() int64 { return 0 }
+// GetHashRate returns the accumulated hashrate of all identifier combined
+func (a *RemoteAgent) GetHashRate() (tot int64) {
+	a.hashrateMu.RLock()
+	defer a.hashrateMu.RUnlock()
+
+	// this could overflow
+	for _, hashrate := range a.hashrate {
+		tot += int64(hashrate.rate)
+	}
+	return
+}
 
 func (a *RemoteAgent) GetWork() [3]string {
 	a.mu.Lock()
@@ -131,6 +156,14 @@ out:
 				}
 			}
 			a.mu.Unlock()
+
+			a.hashrateMu.Lock()
+			for id, hashrate := range a.hashrate {
+				if time.Since(hashrate.ping) > 10*time.Second {
+					delete(a.hashrate, id)
+				}
+			}
+			a.hashrateMu.Unlock()
 		}
 	}
 }
diff --git a/rpc/api/eth.go b/rpc/api/eth.go
index 4041811f06fa5266b01519d5ecd61454e8439c8f..820ea761b3aa30b4dc3872220aabb8c009225baa 100644
--- a/rpc/api/eth.go
+++ b/rpc/api/eth.go
@@ -92,6 +92,7 @@ var (
 		"eth_hashrate":                            (*ethApi).Hashrate,
 		"eth_getWork":                             (*ethApi).GetWork,
 		"eth_submitWork":                          (*ethApi).SubmitWork,
+		"eth_submitHashrate":                      (*ethApi).SubmitHashrate,
 		"eth_resend":                              (*ethApi).Resend,
 		"eth_pendingTransactions":                 (*ethApi).PendingTransactions,
 		"eth_getTransactionReceipt":               (*ethApi).GetTransactionReceipt,
@@ -573,6 +574,15 @@ func (self *ethApi) SubmitWork(req *shared.Request) (interface{}, error) {
 	return self.xeth.RemoteMining().SubmitWork(args.Nonce, common.HexToHash(args.Digest), common.HexToHash(args.Header)), nil
 }
 
+func (self *ethApi) SubmitHashrate(req *shared.Request) (interface{}, error) {
+	args := new(SubmitHashRateArgs)
+	if err := self.codec.Decode(req.Params, &args); err != nil {
+		return nil, shared.NewDecodeParamError(err.Error())
+	}
+	self.xeth.RemoteMining().SubmitHashrate(common.HexToHash(args.Id), args.Rate)
+	return nil, nil
+}
+
 func (self *ethApi) Resend(req *shared.Request) (interface{}, error) {
 	args := new(ResendArgs)
 	if err := self.codec.Decode(req.Params, &args); err != nil {
diff --git a/rpc/api/eth_args.go b/rpc/api/eth_args.go
index 1218bd6254ab99cdf73fd409bc55c3e4c08e929c..5a1841cbedb5690bd60c8e1d973ce436f9a97ed8 100644
--- a/rpc/api/eth_args.go
+++ b/rpc/api/eth_args.go
@@ -169,6 +169,37 @@ func (args *GetTxCountArgs) UnmarshalJSON(b []byte) (err error) {
 	return nil
 }
 
+type SubmitHashRateArgs struct {
+	Id   string
+	Rate uint64
+}
+
+func (args *SubmitHashRateArgs) UnmarshalJSON(b []byte) (err error) {
+	var obj []interface{}
+	if err := json.Unmarshal(b, &obj); err != nil {
+		return shared.NewDecodeParamError(err.Error())
+	}
+
+	if len(obj) < 2 {
+		return shared.NewInsufficientParamsError(len(obj), 2)
+	}
+
+	arg0, ok := obj[0].(string)
+	if !ok {
+		return shared.NewInvalidTypeError("hash", "not a string")
+	}
+	args.Id = arg0
+
+	arg1, ok := obj[1].(string)
+	if !ok {
+		return shared.NewInvalidTypeError("rate", "not a string")
+	}
+
+	args.Rate = common.String2Big(arg1).Uint64()
+
+	return nil
+}
+
 type HashArgs struct {
 	Hash string
 }