diff --git a/bindings/ud.go b/bindings/ud.go
index 15b0e012a7ffaad5b290da54b832bcffdf39fa57..900e102d75621c76d110732fca3c4011dfd23b8b 100644
--- a/bindings/ud.go
+++ b/bindings/ud.go
@@ -8,6 +8,7 @@
 package bindings
 
 import (
+	"fmt"
 	"github.com/pkg/errors"
 	"gitlab.com/elixxir/client/ud"
 	"gitlab.com/elixxir/crypto/contact"
@@ -167,7 +168,7 @@ func (ud UserDiscovery) SearchSingle(f string, callback SingleSearchCallback,
 	return ud.ud.Search([]fact.Fact{fObj}, cb, timeout)
 }
 
-// SingleSearchCallback returns the result of a single search
+// LookupCallback returns the result of a single lookup
 type LookupCallback interface {
 	Callback(contact *Contact, error string)
 }
@@ -200,3 +201,98 @@ func (ud UserDiscovery) Lookup(idBytes []byte, callback LookupCallback,
 	return ud.ud.Lookup(uid, cb, timeout)
 
 }
+
+// MultiLookupCallback returns the result of many paralel lookups
+type MultiLookupCallback interface {
+	Callback(Succeeded *ContactList, failed *IdList, errors string)
+}
+
+
+type lookupResponse struct{
+	C contact.Contact
+	err error
+	index int
+	id *id.ID
+}
+
+// MultiLookup Looks for the contact object associated with all given userIDs.
+// The ids are the byte representation of an id stored in an IDList object.
+// This will reject if that id is malformed or if the indexing on the IDList
+// object is wrong. The MultiLookupCallback will return with all contacts
+// returned within the timeout.
+func (ud UserDiscovery) MultiLookup(ids *IdList, callback MultiLookupCallback,
+	timeoutMS int) error {
+
+	idList := make([]*id.ID,0,ids.Len())
+
+	//extract all IDs from
+	for i:=0;i<ids.Len();i++{
+		idBytes, err := ids.Get(i)
+		if err!=nil{
+			return errors.WithMessagef(err, "Failed to get ID at index %d", i)
+		}
+		uid, err := id.Unmarshal(idBytes)
+		if err != nil {
+			return errors.WithMessagef(err, "Failed to lookup due to "+
+				"malformed id at index %d", i)
+		}
+		idList = append(idList, uid)
+	}
+
+	//make the channels for the requests
+	results := make(chan lookupResponse, len(idList))
+
+	timeout := time.Duration(timeoutMS) * time.Millisecond
+
+	//loop through the IDs and send the lookup
+	for i := range idList{
+		locali := i
+		localID := idList[locali]
+		cb := func(c contact.Contact, err error){
+			results <- lookupResponse{
+				C:     c,
+				err:   err,
+				index: locali,
+				id: localID,
+			}
+		}
+
+		go func(){
+			err := ud.ud.Lookup(localID, cb, timeout)
+			if err!=nil{
+				results <- lookupResponse{
+					C:     contact.Contact{},
+					err:   errors.WithMessagef(err, "Failed to send lookup " +
+						"for user %s[%d]", localID, locali),
+					index: locali,
+					id: localID,
+				}
+			}
+		}()
+	}
+
+	//run the result gathering in its own thread
+	go func(){
+		returnedContactList := make([]contact.Contact,0,len(idList))
+		failedIDList := make([]*id.ID,0,len(idList))
+		var concatonatedErrs string
+
+		//Get the responses and return
+		for numReturned := 0; numReturned<len(idList);numReturned++{
+			response := <- results
+			if response.err==nil{
+				returnedContactList = append(returnedContactList, response.C)
+			}else{
+				failedIDList = append(failedIDList, response.id)
+				concatonatedErrs = concatonatedErrs + fmt.Sprintf("Error returned from " +
+					"send to %d [%d]:%+v\t", response.id, response.index, response.err)
+			}
+		}
+
+		callback.Callback(&ContactList{list:returnedContactList}, &IdList{list:failedIDList}, concatonatedErrs)
+	}()
+
+
+	return nil
+}
+